blob: 5811cd95074a20c4e792f0546464d98b0e5dcdd4 [file] [log] [blame]
package com.google.devtools.build.lib.rules.cpp;
// Copyright 2018 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.
import com.google.auto.value.AutoValue;
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.ImmutableMap;
import com.google.common.collect.ImmutableSet;
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.analysis.skylark.SymbolGenerator;
import com.google.devtools.build.lib.bugreport.BugReport;
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.skylarkbuildapi.cpp.CcLinkingContextApi;
import com.google.devtools.build.lib.skylarkbuildapi.cpp.LibraryToLinkApi;
import com.google.devtools.build.lib.syntax.SkylarkList;
import com.google.devtools.build.lib.util.Fingerprint;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import javax.annotation.Nullable;
/**
* Encapsulates information for linking a library.
*
* <p>TODO(b/118663806): This class which shall be renamed later to LibraryToLink (once the old
* LibraryToLink implementation is removed) will have all the information necessary for linking a
* library in all of its variants : static params for executable, static params for dynamic library,
* dynamic params for executable and dynamic params for dynamic library.
*/
@AutoValue
public abstract class LibraryToLink implements LibraryToLinkApi<Artifact> {
public Artifact getDynamicLibraryForRuntimeOrNull(boolean linkingStatically) {
if (getDynamicLibrary() == null) {
return null;
}
if (linkingStatically && (getStaticLibrary() != null || getPicStaticLibrary() != null)) {
return null;
}
return getDynamicLibrary();
}
/** Structure of CcLinkingContext. */
public static class CcLinkingContext implements CcLinkingContextApi {
public static final CcLinkingContext EMPTY = CcLinkingContext.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);
}
}
private final NestedSet<LibraryToLink> libraries;
private final NestedSet<LinkOptions> userLinkFlags;
private final NestedSet<Linkstamp> linkstamps;
private final NestedSet<Artifact> nonCodeInputs;
private final ExtraLinkTimeLibraries extraLinkTimeLibraries;
public CcLinkingContext(
NestedSet<LibraryToLink> libraries,
NestedSet<LinkOptions> userLinkFlags,
NestedSet<Linkstamp> linkstamps,
NestedSet<Artifact> nonCodeInputs,
ExtraLinkTimeLibraries extraLinkTimeLibraries) {
this.libraries = libraries;
this.userLinkFlags = userLinkFlags;
this.linkstamps = linkstamps;
this.nonCodeInputs = nonCodeInputs;
this.extraLinkTimeLibraries = extraLinkTimeLibraries;
}
public static CcLinkingContext merge(List<CcLinkingContext> ccLinkingContexts) {
CcLinkingContext.Builder mergedCcLinkingContext = CcLinkingContext.builder();
ExtraLinkTimeLibraries.Builder mergedExtraLinkTimeLibraries =
ExtraLinkTimeLibraries.builder();
for (CcLinkingContext ccLinkingContext : ccLinkingContexts) {
mergedCcLinkingContext
.addLibraries(ccLinkingContext.getLibraries())
.addUserLinkFlags(ccLinkingContext.getUserLinkFlags())
.addLinkstamps(ccLinkingContext.getLinkstamps())
.addNonCodeInputs(ccLinkingContext.getNonCodeInputs());
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> getDynamicModeParamsForExecutableLibraries() {
ImmutableList.Builder<Artifact> artifactListBuilder = ImmutableList.builder();
for (LibraryToLink library : getLibraries()) {
if (library.getInterfaceLibrary() != null) {
artifactListBuilder.add(library.getInterfaceLibrary());
} else if (library.getDynamicLibrary() != null) {
artifactListBuilder.add(library.getDynamicLibrary());
} else if (library.getStaticLibrary() != null) {
artifactListBuilder.add(library.getStaticLibrary());
} else if (library.getPicStaticLibrary() != null) {
artifactListBuilder.add(library.getPicStaticLibrary());
}
}
return artifactListBuilder.build();
}
public List<Artifact> getDynamicModeParamsForDynamicLibraryLibraries() {
ImmutableList.Builder<Artifact> artifactListBuilder = ImmutableList.builder();
for (LibraryToLink library : getLibraries()) {
if (library.getInterfaceLibrary() != null) {
artifactListBuilder.add(library.getInterfaceLibrary());
} else if (library.getDynamicLibrary() != null) {
artifactListBuilder.add(library.getDynamicLibrary());
} else if (library.getPicStaticLibrary() != null) {
artifactListBuilder.add(library.getPicStaticLibrary());
} else if (library.getStaticLibrary() != null) {
artifactListBuilder.add(library.getStaticLibrary());
}
}
return artifactListBuilder.build();
}
public List<Artifact> getDynamicLibrariesForRuntime(boolean linkingStatically) {
return LibraryToLink.getDynamicLibrariesForRuntime(linkingStatically, libraries);
}
public NestedSet<LibraryToLink> getLibraries() {
return libraries;
}
@Override
public SkylarkList<String> getSkylarkUserLinkFlags() {
return SkylarkList.createImmutable(getFlattenedUserLinkFlags());
}
@Override
public SkylarkList<LibraryToLinkApi> getSkylarkLibrariesToLink() {
return SkylarkList.createImmutable(libraries.toList());
}
public NestedSet<LinkOptions> getUserLinkFlags() {
return userLinkFlags;
}
public ImmutableList<String> getFlattenedUserLinkFlags() {
return Streams.stream(userLinkFlags)
.map(LinkOptions::get)
.flatMap(Collection::stream)
.collect(ImmutableList.toImmutableList());
}
public NestedSet<Linkstamp> getLinkstamps() {
return linkstamps;
}
public NestedSet<Artifact> getNonCodeInputs() {
return nonCodeInputs;
}
public ExtraLinkTimeLibraries getExtraLinkTimeLibraries() {
return extraLinkTimeLibraries;
}
public static Builder builder() {
return new Builder();
}
/** Builder for {@link CcLinkingContext}. */
public static class Builder {
private final NestedSetBuilder<LibraryToLink> libraries = NestedSetBuilder.linkOrder();
private final NestedSetBuilder<LinkOptions> userLinkFlags = NestedSetBuilder.linkOrder();
private final NestedSetBuilder<Linkstamp> linkstamps = NestedSetBuilder.compileOrder();
private final NestedSetBuilder<Artifact> nonCodeInputs = NestedSetBuilder.linkOrder();
private ExtraLinkTimeLibraries extraLinkTimeLibraries = null;
public Builder addLibraries(NestedSet<LibraryToLink> libraries) {
this.libraries.addTransitive(libraries);
return this;
}
public Builder addUserLinkFlags(NestedSet<LinkOptions> userLinkFlags) {
this.userLinkFlags.addTransitive(userLinkFlags);
return this;
}
Builder addLinkstamps(NestedSet<Linkstamp> linkstamps) {
this.linkstamps.addTransitive(linkstamps);
return this;
}
Builder addNonCodeInputs(NestedSet<Artifact> nonCodeInputs) {
this.nonCodeInputs.addTransitive(nonCodeInputs);
return this;
}
public Builder setExtraLinkTimeLibraries(ExtraLinkTimeLibraries extraLinkTimeLibraries) {
Preconditions.checkState(this.extraLinkTimeLibraries == null);
this.extraLinkTimeLibraries = extraLinkTimeLibraries;
return this;
}
public CcLinkingContext build() {
return new CcLinkingContext(
libraries.build(),
userLinkFlags.build(),
linkstamps.build(),
nonCodeInputs.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.libraries.shallowEquals(other.libraries)
&& this.userLinkFlags.shallowEquals(other.userLinkFlags)
&& this.linkstamps.shallowEquals(other.linkstamps)
&& this.nonCodeInputs.shallowEquals(other.nonCodeInputs);
}
@Override
public int hashCode() {
return Objects.hashCode(
libraries.shallowHashCode(),
userLinkFlags.shallowHashCode(),
linkstamps.shallowHashCode(),
nonCodeInputs.shallowHashCode());
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("userLinkFlags", userLinkFlags)
.add("linkstamps", linkstamps)
.add("libraries", libraries)
.add("nonCodeInputs", nonCodeInputs)
.toString();
}
}
private LinkerInputs.LibraryToLink picStaticLibraryToLink;
private LinkerInputs.LibraryToLink staticLibraryToLink;
private LinkerInputs.LibraryToLink dynamicLibraryToLink;
private LinkerInputs.LibraryToLink interfaceLibraryToLink;
public abstract String getLibraryIdentifier();
@Nullable
@Override
public abstract Artifact getStaticLibrary();
@Nullable
public abstract ImmutableList<Artifact> getObjectFiles();
@Nullable
public abstract ImmutableMap<Artifact, LtoBackendArtifacts> getSharedNonLtoBackends();
@Nullable
public abstract LtoCompilationContext getLtoCompilationContext();
@Nullable
@Override
public abstract Artifact getPicStaticLibrary();
@Nullable
public abstract ImmutableList<Artifact> getPicObjectFiles();
@Nullable
public abstract ImmutableMap<Artifact, LtoBackendArtifacts> getPicSharedNonLtoBackends();
@Nullable
public abstract LtoCompilationContext getPicLtoCompilationContext();
@Nullable
@Override
public abstract Artifact getDynamicLibrary();
@Nullable
public abstract Artifact getResolvedSymlinkDynamicLibrary();
@Nullable
@Override
public abstract Artifact getInterfaceLibrary();
@Nullable
public abstract Artifact getResolvedSymlinkInterfaceLibrary();
@Override
public abstract boolean getAlwayslink();
// TODO(plf): This is just needed for Go, do not expose to Skylark and try to remove it. This was
// introduced to let a linker input declare that it needs debug info in the executable.
// Specifically, this was introduced for linking Go into a C++ binary when using the gccgo
// compiler.
abstract boolean getMustKeepDebug();
public static Builder builder() {
return new AutoValue_LibraryToLink.Builder().setMustKeepDebug(false).setAlwayslink(false);
}
LinkerInputs.LibraryToLink getStaticLibraryToLink() {
Preconditions.checkNotNull(getStaticLibrary(), this);
if (staticLibraryToLink != null) {
return staticLibraryToLink;
}
staticLibraryToLink =
LinkerInputs.newInputLibrary(
getStaticLibrary(),
getAlwayslink()
? ArtifactCategory.ALWAYSLINK_STATIC_LIBRARY
: ArtifactCategory.STATIC_LIBRARY,
getLibraryIdentifier(),
getObjectFiles(),
getLtoCompilationContext(),
getSharedNonLtoBackends(),
getMustKeepDebug());
return staticLibraryToLink;
}
LinkerInputs.LibraryToLink getPicStaticLibraryToLink() {
Preconditions.checkNotNull(getPicStaticLibrary(), this);
if (picStaticLibraryToLink != null) {
return picStaticLibraryToLink;
}
picStaticLibraryToLink =
LinkerInputs.newInputLibrary(
getPicStaticLibrary(),
getAlwayslink()
? ArtifactCategory.ALWAYSLINK_STATIC_LIBRARY
: ArtifactCategory.STATIC_LIBRARY,
getLibraryIdentifier(),
getPicObjectFiles(),
getPicLtoCompilationContext(),
getPicSharedNonLtoBackends(),
getMustKeepDebug());
return picStaticLibraryToLink;
}
LinkerInputs.LibraryToLink getDynamicLibraryToLink() {
Preconditions.checkNotNull(getDynamicLibrary(), this);
if (dynamicLibraryToLink != null) {
return dynamicLibraryToLink;
}
if (getResolvedSymlinkDynamicLibrary() != null) {
dynamicLibraryToLink =
LinkerInputs.solibLibraryToLink(
getDynamicLibrary(), getResolvedSymlinkDynamicLibrary(), getLibraryIdentifier());
} else {
dynamicLibraryToLink =
LinkerInputs.newInputLibrary(
getDynamicLibrary(),
ArtifactCategory.DYNAMIC_LIBRARY,
getLibraryIdentifier(),
/* objectFiles */ ImmutableSet.of(),
LtoCompilationContext.EMPTY,
/* sharedNonLtoBackends */ ImmutableMap.of(),
getMustKeepDebug());
}
return dynamicLibraryToLink;
}
LinkerInputs.LibraryToLink getInterfaceLibraryToLink() {
Preconditions.checkNotNull(getInterfaceLibrary());
if (interfaceLibraryToLink != null) {
return interfaceLibraryToLink;
}
if (getResolvedSymlinkInterfaceLibrary() != null) {
interfaceLibraryToLink =
LinkerInputs.solibLibraryToLink(
getInterfaceLibrary(), getResolvedSymlinkInterfaceLibrary(), getLibraryIdentifier());
} else {
interfaceLibraryToLink =
LinkerInputs.newInputLibrary(
getInterfaceLibrary(),
ArtifactCategory.INTERFACE_LIBRARY,
getLibraryIdentifier(),
/* objectFiles */ ImmutableSet.of(),
LtoCompilationContext.EMPTY,
/* sharedNonLtoBackends */ ImmutableMap.of(),
getMustKeepDebug());
}
return interfaceLibraryToLink;
}
public static List<Artifact> getDynamicLibrariesForRuntime(
boolean linkingStatically, Iterable<LibraryToLink> libraries) {
ImmutableList.Builder<Artifact> dynamicLibrariesForRuntimeBuilder = ImmutableList.builder();
for (LibraryToLink libraryToLink : libraries) {
Artifact artifact = libraryToLink.getDynamicLibraryForRuntimeOrNull(linkingStatically);
if (artifact != null) {
dynamicLibrariesForRuntimeBuilder.add(artifact);
}
}
return dynamicLibrariesForRuntimeBuilder.build();
}
public static List<Artifact> getDynamicLibrariesForLinking(Iterable<LibraryToLink> libraries) {
ImmutableList.Builder<Artifact> dynamicLibrariesForLinkingBuilder = ImmutableList.builder();
for (LibraryToLink libraryToLink : libraries) {
if (libraryToLink.getInterfaceLibrary() != null) {
dynamicLibrariesForLinkingBuilder.add(libraryToLink.getInterfaceLibrary());
} else if (libraryToLink.getDynamicLibrary() != null) {
dynamicLibrariesForLinkingBuilder.add(libraryToLink.getDynamicLibrary());
}
}
return dynamicLibrariesForLinkingBuilder.build();
}
/** Builder for LibraryToLink. */
@AutoValue.Builder
public abstract static class Builder {
public abstract Builder setLibraryIdentifier(String libraryIdentifier);
public abstract Builder setStaticLibrary(Artifact staticLibrary);
public abstract Builder setObjectFiles(ImmutableList<Artifact> objectFiles);
abstract Builder setLtoCompilationContext(LtoCompilationContext ltoCompilationContext);
abstract Builder setSharedNonLtoBackends(
ImmutableMap<Artifact, LtoBackendArtifacts> sharedNonLtoBackends);
abstract Builder setPicStaticLibrary(Artifact picStaticLibrary);
abstract Builder setPicObjectFiles(ImmutableList<Artifact> picObjectFiles);
abstract Builder setPicLtoCompilationContext(LtoCompilationContext picLtoCompilationContext);
abstract Builder setPicSharedNonLtoBackends(
ImmutableMap<Artifact, LtoBackendArtifacts> picSharedNonLtoBackends);
public abstract Builder setDynamicLibrary(Artifact dynamicLibrary);
abstract Builder setResolvedSymlinkDynamicLibrary(Artifact resolvedSymlinkDynamicLibrary);
abstract Builder setInterfaceLibrary(Artifact interfaceLibrary);
abstract Builder setResolvedSymlinkInterfaceLibrary(Artifact resolvedSymlinkInterfaceLibrary);
public abstract Builder setAlwayslink(boolean alwayslink);
public abstract Builder setMustKeepDebug(boolean mustKeepDebug);
// Methods just for validation, not to be called externally.
abstract LibraryToLink autoBuild();
abstract String getLibraryIdentifier();
abstract Artifact getStaticLibrary();
abstract ImmutableList<Artifact> getObjectFiles();
abstract ImmutableMap<Artifact, LtoBackendArtifacts> getSharedNonLtoBackends();
abstract LtoCompilationContext getLtoCompilationContext();
abstract Artifact getPicStaticLibrary();
abstract ImmutableList<Artifact> getPicObjectFiles();
abstract ImmutableMap<Artifact, LtoBackendArtifacts> getPicSharedNonLtoBackends();
abstract LtoCompilationContext getPicLtoCompilationContext();
abstract Artifact getDynamicLibrary();
abstract Artifact getResolvedSymlinkDynamicLibrary();
abstract Artifact getInterfaceLibrary();
abstract Artifact getResolvedSymlinkInterfaceLibrary();
public LibraryToLink build() {
Preconditions.checkNotNull(getLibraryIdentifier());
Preconditions.checkState(
(getObjectFiles() == null
&& getLtoCompilationContext() == null
&& getSharedNonLtoBackends() == null)
|| getStaticLibrary() != null);
Preconditions.checkState(
(getPicObjectFiles() == null
&& getPicLtoCompilationContext() == null
&& getPicSharedNonLtoBackends() == null)
|| getPicStaticLibrary() != null);
Preconditions.checkState(
getResolvedSymlinkDynamicLibrary() == null || getDynamicLibrary() != null);
Preconditions.checkState(
getResolvedSymlinkInterfaceLibrary() == null
|| getResolvedSymlinkInterfaceLibrary() != null);
Preconditions.checkState(
getStaticLibrary() != null
|| getPicStaticLibrary() != null
|| getDynamicLibrary() != null
|| getInterfaceLibrary() != null);
return autoBuild();
}
}
}