blob: 8c03b1fe587a11064350287c10d9b63495410574 [file] [log] [blame]
// Copyright 2019 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.devtools.build.lib.rules.cpp;
import com.google.common.base.Joiner;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Streams;
import com.google.devtools.build.lib.actions.ActionKeyContext;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.bugreport.BugReport;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
import com.google.devtools.build.lib.events.Location;
import com.google.devtools.build.lib.packages.SymbolGenerator;
import com.google.devtools.build.lib.skylarkbuildapi.cpp.CcLinkingContextApi;
import com.google.devtools.build.lib.skylarkbuildapi.cpp.LinkerInputApi;
import com.google.devtools.build.lib.syntax.Depset;
import com.google.devtools.build.lib.syntax.EvalException;
import com.google.devtools.build.lib.syntax.Sequence;
import com.google.devtools.build.lib.syntax.StarlarkList;
import com.google.devtools.build.lib.syntax.StarlarkThread;
import com.google.devtools.build.lib.util.Fingerprint;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
/** Structure of CcLinkingContext. */
public class CcLinkingContext implements CcLinkingContextApi<Artifact> {
public static final CcLinkingContext EMPTY = builder().build();
/** A list of link options contributed by a single configured target/aspect. */
@Immutable
public static final class LinkOptions {
private final ImmutableList<String> linkOptions;
private final Object symbolForEquality;
private LinkOptions(Iterable<String> linkOptions, Object symbolForEquality) {
this.linkOptions = ImmutableList.copyOf(linkOptions);
this.symbolForEquality = Preconditions.checkNotNull(symbolForEquality);
}
public ImmutableList<String> get() {
return linkOptions;
}
public static LinkOptions of(Iterable<String> linkOptions, SymbolGenerator<?> symbolGenerator) {
return new LinkOptions(linkOptions, symbolGenerator.generate());
}
@Override
public int hashCode() {
// Symbol is sufficient for equality check.
return symbolForEquality.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof LinkOptions)) {
return false;
}
LinkOptions that = (LinkOptions) obj;
if (!this.symbolForEquality.equals(that.symbolForEquality)) {
return false;
}
if (this.linkOptions.equals(that.linkOptions)) {
return true;
}
BugReport.sendBugReport(
new IllegalStateException(
"Unexpected inequality with equal symbols: " + this + ", " + that));
return false;
}
@Override
public String toString() {
return '[' + Joiner.on(",").join(linkOptions) + "] (owner: " + symbolForEquality;
}
}
/**
* A linkstamp that also knows about its declared includes.
*
* <p>This object is required because linkstamp files may include other headers which will have to
* be provided during compilation.
*/
public static final class Linkstamp {
private final Artifact artifact;
private final NestedSet<Artifact> declaredIncludeSrcs;
private final byte[] nestedDigest;
// TODO(janakr): if action key context is not available, the digest can be computed lazily,
// only if we are doing an equality comparison and artifacts are equal. That should never
// happen, so doing an expensive digest should be ok then. If this is ever moved to Starlark
// and Starlark doesn't support custom equality or amortized deep equality of nested sets, a
// Symbol can be used as an equality proxy, similar to what LinkOptions does above.
Linkstamp(
Artifact artifact,
NestedSet<Artifact> declaredIncludeSrcs,
ActionKeyContext actionKeyContext) {
this.artifact = Preconditions.checkNotNull(artifact);
this.declaredIncludeSrcs = Preconditions.checkNotNull(declaredIncludeSrcs);
Fingerprint fp = new Fingerprint();
actionKeyContext.addNestedSetToFingerprint(fp, this.declaredIncludeSrcs);
nestedDigest = fp.digestAndReset();
}
/** Returns the linkstamp artifact. */
public Artifact getArtifact() {
return artifact;
}
/** Returns the declared includes. */
public NestedSet<Artifact> getDeclaredIncludeSrcs() {
return declaredIncludeSrcs;
}
@Override
public int hashCode() {
// Artifact should be enough to disambiguate basically all the time.
return artifact.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Linkstamp)) {
return false;
}
Linkstamp other = (Linkstamp) obj;
return artifact.equals(other.artifact)
&& Arrays.equals(this.nestedDigest, other.nestedDigest);
}
}
/**
* Wraps any input to the linker, be it libraries, linker scripts, linkstamps or linking options.
*/
@Immutable
public static class LinkerInput implements LinkerInputApi<LibraryToLink, Artifact> {
// Identifies which target created the LinkerInput. It doesn't have to be unique between
// LinkerInputs.
private final Label owner;
private final ImmutableList<LibraryToLink> libraries;
private final ImmutableList<LinkOptions> userLinkFlags;
private final ImmutableList<Artifact> nonCodeInputs;
private final ImmutableList<Linkstamp> linkstamps;
private LinkerInput(
Label owner,
ImmutableList<LibraryToLink> libraries,
ImmutableList<LinkOptions> userLinkFlags,
ImmutableList<Artifact> nonCodeInputs,
ImmutableList<Linkstamp> linkstamps) {
this.owner = owner;
this.libraries = libraries;
this.userLinkFlags = userLinkFlags;
this.nonCodeInputs = nonCodeInputs;
this.linkstamps = linkstamps;
}
@Override
public Label getSkylarkOwner(Location location) throws EvalException {
if (owner == null) {
throw new EvalException(
location,
"Owner is null. This means that some target upstream is of a rule type that uses the"
+ " old API of create_linking_context");
}
return owner;
}
public Label getOwner() {
return owner;
}
public List<LibraryToLink> getLibraries() {
return libraries;
}
@Override
public Sequence<LibraryToLink> getSkylarkLibrariesToLink(StarlarkThread thread) {
return StarlarkList.immutableCopyOf(getLibraries());
}
public List<LinkOptions> getUserLinkFlags() {
return userLinkFlags;
}
@Override
public Sequence<String> getSkylarkUserLinkFlags() {
return StarlarkList.immutableCopyOf(
getUserLinkFlags().stream()
.map(LinkOptions::get)
.flatMap(Collection::stream)
.collect(ImmutableList.toImmutableList()));
}
public List<Artifact> getNonCodeInputs() {
return nonCodeInputs;
}
@Override
public Sequence<Artifact> getSkylarkNonCodeInputs() {
return StarlarkList.immutableCopyOf(getNonCodeInputs());
}
public List<Linkstamp> getLinkstamps() {
return linkstamps;
}
public static Builder builder() {
return new Builder();
}
/** Builder for {@link LinkerInput} */
public static class Builder {
private Label owner;
private final ImmutableList.Builder<LibraryToLink> libraries = ImmutableList.builder();
private final ImmutableList.Builder<LinkOptions> userLinkFlags = ImmutableList.builder();
private final ImmutableList.Builder<Artifact> nonCodeInputs = ImmutableList.builder();
private final ImmutableList.Builder<Linkstamp> linkstamps = ImmutableList.builder();
public Builder addLibrary(LibraryToLink library) {
this.libraries.add(library);
return this;
}
public Builder addLibraries(List<LibraryToLink> libraries) {
this.libraries.addAll(libraries);
return this;
}
public Builder addUserLinkFlags(List<LinkOptions> userLinkFlags) {
this.userLinkFlags.addAll(userLinkFlags);
return this;
}
public Builder addLinkstamps(List<Linkstamp> linkstamps) {
this.linkstamps.addAll(linkstamps);
return this;
}
public Builder addNonCodeInputs(List<Artifact> nonCodeInputs) {
this.nonCodeInputs.addAll(nonCodeInputs);
return this;
}
public Builder setOwner(Label owner) {
this.owner = owner;
return this;
}
public LinkerInput build() {
return new LinkerInput(
owner,
libraries.build(),
userLinkFlags.build(),
nonCodeInputs.build(),
linkstamps.build());
}
}
@Override
public boolean equals(Object otherObject) {
if (!(otherObject instanceof LinkerInput)) {
return false;
}
LinkerInput other = (LinkerInput) otherObject;
if (this == other) {
return true;
}
return ((this.owner == null && other.owner == null) || this.owner.equals(other.owner))
&& this.libraries.equals(other.libraries)
&& this.userLinkFlags.equals(other.userLinkFlags)
&& this.linkstamps.equals(other.linkstamps)
&& this.nonCodeInputs.equals(other.nonCodeInputs);
}
@Override
public int hashCode() {
return Objects.hashCode(
libraries.hashCode(),
userLinkFlags.hashCode(),
linkstamps.hashCode(),
nonCodeInputs.hashCode());
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("userLinkFlags", userLinkFlags)
.add("linkstamps", linkstamps)
.add("libraries", libraries)
.add("nonCodeInputs", nonCodeInputs)
.toString();
}
}
private final NestedSet<LinkerInput> linkerInputs;
private final ExtraLinkTimeLibraries extraLinkTimeLibraries;
public CcLinkingContext(
NestedSet<LinkerInput> linkerInputs, ExtraLinkTimeLibraries extraLinkTimeLibraries) {
this.linkerInputs = linkerInputs;
this.extraLinkTimeLibraries = extraLinkTimeLibraries;
}
public static CcLinkingContext merge(List<CcLinkingContext> ccLinkingContexts) {
Builder mergedCcLinkingContext = CcLinkingContext.builder();
ExtraLinkTimeLibraries.Builder mergedExtraLinkTimeLibraries = ExtraLinkTimeLibraries.builder();
for (CcLinkingContext ccLinkingContext : ccLinkingContexts) {
mergedCcLinkingContext.addTransitiveLinkerInputs(ccLinkingContext.getLinkerInputs());
if (ccLinkingContext.getExtraLinkTimeLibraries() != null) {
mergedExtraLinkTimeLibraries.addTransitive(ccLinkingContext.getExtraLinkTimeLibraries());
}
}
mergedCcLinkingContext.setExtraLinkTimeLibraries(mergedExtraLinkTimeLibraries.build());
return mergedCcLinkingContext.build();
}
public List<Artifact> getStaticModeParamsForExecutableLibraries() {
ImmutableList.Builder<Artifact> libraryListBuilder = ImmutableList.builder();
for (LibraryToLink libraryToLink : getLibraries()) {
if (libraryToLink.getStaticLibrary() != null) {
libraryListBuilder.add(libraryToLink.getStaticLibrary());
} else if (libraryToLink.getPicStaticLibrary() != null) {
libraryListBuilder.add(libraryToLink.getPicStaticLibrary());
} else if (libraryToLink.getInterfaceLibrary() != null) {
libraryListBuilder.add(libraryToLink.getInterfaceLibrary());
} else {
libraryListBuilder.add(libraryToLink.getDynamicLibrary());
}
}
return libraryListBuilder.build();
}
public List<Artifact> getStaticModeParamsForDynamicLibraryLibraries() {
ImmutableList.Builder<Artifact> artifactListBuilder = ImmutableList.builder();
for (LibraryToLink library : getLibraries()) {
if (library.getPicStaticLibrary() != null) {
artifactListBuilder.add(library.getPicStaticLibrary());
} else if (library.getStaticLibrary() != null) {
artifactListBuilder.add(library.getStaticLibrary());
} else if (library.getInterfaceLibrary() != null) {
artifactListBuilder.add(library.getInterfaceLibrary());
} else {
artifactListBuilder.add(library.getDynamicLibrary());
}
}
return artifactListBuilder.build();
}
public List<Artifact> getDynamicLibrariesForRuntime(boolean linkingStatically) {
return LibraryToLink.getDynamicLibrariesForRuntime(linkingStatically, getLibraries());
}
public NestedSet<LibraryToLink> getLibraries() {
NestedSetBuilder<LibraryToLink> libraries = NestedSetBuilder.linkOrder();
for (LinkerInput linkerInput : linkerInputs) {
libraries.addAll(linkerInput.libraries);
}
return libraries.build();
}
public NestedSet<LinkerInput> getLinkerInputs() {
return linkerInputs;
}
@Override
public Depset getSkylarkLinkerInputs() {
return Depset.of(LinkerInput.class, linkerInputs);
}
@Override
public Sequence<String> getSkylarkUserLinkFlags() {
return StarlarkList.immutableCopyOf(getFlattenedUserLinkFlags());
}
@Override
public Object getSkylarkLibrariesToLink(StarlarkThread thread) {
// TODO(plf): Flag can be removed already.
if (thread.getSemantics().incompatibleDepsetForLibrariesToLinkGetter()) {
return Depset.of(LibraryToLink.class, getLibraries());
} else {
return StarlarkList.immutableCopyOf(getLibraries().toList());
}
}
@Override
public Depset getSkylarkNonCodeInputs() {
return Depset.of(Artifact.TYPE, getNonCodeInputs());
}
public NestedSet<LinkOptions> getUserLinkFlags() {
NestedSetBuilder<LinkOptions> userLinkFlags = NestedSetBuilder.linkOrder();
for (LinkerInput linkerInput : linkerInputs) {
userLinkFlags.addAll(linkerInput.getUserLinkFlags());
}
return userLinkFlags.build();
}
public ImmutableList<String> getFlattenedUserLinkFlags() {
return Streams.stream(getUserLinkFlags())
.map(LinkOptions::get)
.flatMap(Collection::stream)
.collect(ImmutableList.toImmutableList());
}
public NestedSet<Linkstamp> getLinkstamps() {
NestedSetBuilder<Linkstamp> linkstamps = NestedSetBuilder.linkOrder();
for (LinkerInput linkerInput : linkerInputs) {
linkstamps.addAll(linkerInput.getLinkstamps());
}
return linkstamps.build();
}
public NestedSet<Artifact> getNonCodeInputs() {
NestedSetBuilder<Artifact> nonCodeInputs = NestedSetBuilder.linkOrder();
for (LinkerInput linkerInput : linkerInputs) {
nonCodeInputs.addAll(linkerInput.getNonCodeInputs());
}
return nonCodeInputs.build();
}
public ExtraLinkTimeLibraries getExtraLinkTimeLibraries() {
return extraLinkTimeLibraries;
}
public static Builder builder() {
// private to avoid class initialization deadlock between this class and its outer class
return new Builder();
}
/** Builder for {@link CcLinkingContext}. */
public static class Builder {
boolean hasDirectLinkerInput;
LinkerInput.Builder linkerInputBuilder = LinkerInput.builder();
private final NestedSetBuilder<LinkerInput> linkerInputs = NestedSetBuilder.linkOrder();
private ExtraLinkTimeLibraries extraLinkTimeLibraries = null;
public Builder setOwner(Label owner) {
linkerInputBuilder.setOwner(owner);
return this;
}
public Builder addLibrary(LibraryToLink library) {
hasDirectLinkerInput = true;
linkerInputBuilder.addLibrary(library);
return this;
}
public Builder addLibraries(List<LibraryToLink> libraries) {
hasDirectLinkerInput = true;
linkerInputBuilder.addLibraries(libraries);
return this;
}
public Builder addUserLinkFlags(List<LinkOptions> userLinkFlags) {
hasDirectLinkerInput = true;
linkerInputBuilder.addUserLinkFlags(userLinkFlags);
return this;
}
Builder addLinkstamps(List<Linkstamp> linkstamps) {
hasDirectLinkerInput = true;
linkerInputBuilder.addLinkstamps(linkstamps);
return this;
}
Builder addNonCodeInputs(List<Artifact> nonCodeInputs) {
hasDirectLinkerInput = true;
linkerInputBuilder.addNonCodeInputs(nonCodeInputs);
return this;
}
public Builder addTransitiveLinkerInputs(NestedSet<LinkerInput> linkerInputs) {
this.linkerInputs.addTransitive(linkerInputs);
return this;
}
public Builder setExtraLinkTimeLibraries(ExtraLinkTimeLibraries extraLinkTimeLibraries) {
Preconditions.checkState(this.extraLinkTimeLibraries == null);
this.extraLinkTimeLibraries = extraLinkTimeLibraries;
return this;
}
public CcLinkingContext build() {
if (hasDirectLinkerInput) {
linkerInputs.add(linkerInputBuilder.build());
}
return new CcLinkingContext(linkerInputs.build(), extraLinkTimeLibraries);
}
}
@Override
public boolean equals(Object otherObject) {
if (!(otherObject instanceof CcLinkingContext)) {
return false;
}
CcLinkingContext other = (CcLinkingContext) otherObject;
if (this == other) {
return true;
}
return this.linkerInputs.shallowEquals(other.linkerInputs);
}
@Override
public int hashCode() {
return linkerInputs.shallowHashCode();
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this).add("linkerInputs", linkerInputs).toString();
}
}