// Copyright 2014 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.Function; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.analysis.RuleContext; import com.google.devtools.build.lib.analysis.TransitiveInfoCollection; import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode; import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.collect.nestedset.Order; import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; import com.google.devtools.build.lib.rules.cpp.LinkerInputs.LibraryToLink; import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec; import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec.VisibleForSerialization; import com.google.devtools.build.lib.skylarkbuildapi.cpp.CcLinkParamsApi; import com.google.devtools.build.lib.syntax.SkylarkNestedSet; import java.util.Collection; import java.util.Objects; import javax.annotation.Nullable; /** * Parameters to be passed to the linker. * *

The parameters concerned are the link options (strings) passed to the linker, linkstamps, a * list of libraries to be linked in, and a list of libraries to build at link time. * *

Items in the collections are stored in nested sets. Link options and libraries are stored in * link order (preorder) and linkstamps are sorted. */ @AutoCodec public final class CcLinkParams implements CcLinkParamsApi { /** * A list of link options contributed by a single configured target. * *

WARNING: Do not implement {@code #equals()} in the obvious way. This class must be * checked for equality by object identity because otherwise if two configured targets contribute * the same link options, they will be de-duplicated, which is not the desirable behavior. */ @AutoCodec @Immutable public static final class LinkOptions { private final ImmutableList linkOptions; @VisibleForSerialization LinkOptions(Iterable linkOptions) { this.linkOptions = ImmutableList.copyOf(linkOptions); } public ImmutableList get() { return linkOptions; } public static LinkOptions of(Iterable linkOptions) { return new LinkOptions(linkOptions); } } private final NestedSet linkOpts; private final NestedSet linkstamps; private final NestedSet libraries; private final NestedSet dynamicLibrariesForRuntime; private final ExtraLinkTimeLibraries extraLinkTimeLibraries; private final NestedSet nonCodeInputs; @AutoCodec.Instantiator @VisibleForSerialization CcLinkParams( NestedSet linkOpts, NestedSet linkstamps, NestedSet libraries, NestedSet dynamicLibrariesForRuntime, ExtraLinkTimeLibraries extraLinkTimeLibraries, NestedSet nonCodeInputs) { this.linkOpts = linkOpts; this.linkstamps = linkstamps; this.libraries = libraries; this.dynamicLibrariesForRuntime = dynamicLibrariesForRuntime; this.extraLinkTimeLibraries = extraLinkTimeLibraries; this.nonCodeInputs = nonCodeInputs; } /** * Returns the linkopts */ public NestedSet getLinkopts() { return linkOpts; } public ImmutableList flattenedLinkopts() { return ImmutableList.copyOf(Iterables.concat(Iterables.transform(linkOpts, LinkOptions::get))); } @Override public SkylarkNestedSet getSkylarkLinkopts() { // TODO(plf): Shouldn't flatten nested set. Remove LinkOptions class and just have a nested set // of strings. return SkylarkNestedSet.of( String.class, NestedSetBuilder.wrap(Order.COMPILE_ORDER, flattenedLinkopts())); } /** * Returns the linkstamps */ public NestedSet getLinkstamps() { return linkstamps; } /** * Returns the libraries */ public NestedSet getLibraries() { return libraries; } @Override public SkylarkNestedSet getSkylarkLibrariesToLink() { return SkylarkNestedSet.of(LibraryToLink.class, libraries); } /** Returns the dynamicLibrariesForRuntime. */ public NestedSet getDynamicLibrariesForRuntime() { return dynamicLibrariesForRuntime; } @Override public SkylarkNestedSet getSkylarkDynamicLibrariesForRuntime() { return SkylarkNestedSet.of(Artifact.class, dynamicLibrariesForRuntime); } /** * The extra link time libraries; will be null if there are no such libraries. */ public @Nullable ExtraLinkTimeLibraries getExtraLinkTimeLibraries() { return extraLinkTimeLibraries; } /** * Returns the non-code inputs, e.g. linker scripts; will be null if none. */ public @Nullable NestedSet getNonCodeInputs() { return nonCodeInputs; } public static final Builder builder(boolean linkingStatically, boolean linkShared) { return new Builder(linkingStatically, linkShared); } public static final Builder builder() { return new Builder(); } /** * Builder for {@link CcLinkParams}. */ public static final class Builder { /** * linkingStatically is true when we're linking this target in either FULLY STATIC mode * (linkopts=["-static"]) or MOSTLY STATIC mode (linkstatic=1). When this is true, we want to * use static versions of any libraries that this target depends on (except possibly system * libraries, which are not handled by CcLinkParams). When this is false, we want to use dynamic * versions of any libraries that this target depends on. */ private boolean linkingStatically; /** linkShared is true when we're linking with "-shared" (linkshared=1). */ private boolean linkShared; // TODO(plf): Ideally the two booleans above are removed from this Builder. We would pass the // specific instances of CcLinkParams that are needed from transitive dependencies instead of // calling the convenience methods that dig them out from the CcLinkParamsStore using these // booleans. private boolean linkingStaticallyLinkSharedSet; private ImmutableList.Builder localLinkoptsBuilder = ImmutableList.builder(); private final NestedSetBuilder linkOptsBuilder = NestedSetBuilder.linkOrder(); private final NestedSetBuilder linkstampsBuilder = NestedSetBuilder.compileOrder(); private final NestedSetBuilder librariesBuilder = NestedSetBuilder.linkOrder(); private final NestedSetBuilder dynamicLibrariesForRuntimeBuilder = NestedSetBuilder.stableOrder(); /** * A builder for the list of link time libraries. Most builds * won't have any such libraries, so save space by leaving the * default as null. */ private ExtraLinkTimeLibraries.Builder extraLinkTimeLibrariesBuilder = null; private NestedSetBuilder nonCodeInputsBuilder = null; private boolean built = false; /** The static builder methods of {@link CcLinkParams} should be used for instantiation. */ private Builder(boolean linkingStatically, boolean linkShared) { this.linkingStatically = linkingStatically; this.linkShared = linkShared; this.linkingStaticallyLinkSharedSet = true; } private Builder() {} /** * Builds a {@link CcLinkParams} object. */ public CcLinkParams build() { Preconditions.checkState(!built); // Not thread-safe, but builders should not be shared across threads. built = true; ImmutableList localLinkopts = localLinkoptsBuilder.build(); if (!localLinkopts.isEmpty()) { linkOptsBuilder.add(LinkOptions.of(localLinkopts)); } ExtraLinkTimeLibraries extraLinkTimeLibraries = null; if (extraLinkTimeLibrariesBuilder != null) { extraLinkTimeLibraries = extraLinkTimeLibrariesBuilder.build(); } NestedSet nonCodeInputs = null; if (nonCodeInputsBuilder != null) { nonCodeInputs = nonCodeInputsBuilder.build(); } return new CcLinkParams( linkOptsBuilder.build(), linkstampsBuilder.build(), librariesBuilder.build(), dynamicLibrariesForRuntimeBuilder.build(), extraLinkTimeLibraries, nonCodeInputs); } public boolean add(AbstractCcLinkParamsStore store) { Preconditions.checkState(linkingStaticallyLinkSharedSet); if (store != null) { CcLinkParams args = store.get(linkingStatically, linkShared); addTransitiveArgs(args); } return store != null; } /** * Includes link parameters from a collection of dependency targets. */ public Builder addTransitiveTargets(Iterable targets) { for (TransitiveInfoCollection target : targets) { addTransitiveTarget(target); } return this; } /** * Includes link parameters from a dependency target. * *

The target should implement {@link CcLinkParamsStore}. If it does not, the method does not * do anything. */ public Builder addTransitiveTarget(TransitiveInfoCollection target) { CcLinkingInfo ccLinkingInfo = target.get(CcLinkingInfo.PROVIDER); CcLinkParamsStore ccLinkParamsStore = ccLinkingInfo == null ? null : ccLinkingInfo.getCcLinkParamsStore(); if (ccLinkParamsStore != null) { add(ccLinkParamsStore); } return this; } /** * Includes link parameters from a dependency target. The target is checked for the given * mappings in the order specified, and the first mapping that returns a non-null result is * added. */ @SafeVarargs public final Builder addTransitiveTarget( TransitiveInfoCollection target, Function firstMapping, @SuppressWarnings("unchecked") // Java arrays don't preserve generic arguments. Function... remainingMappings) { if (add(firstMapping.apply(target))) { return this; } for (Function mapping : remainingMappings) { if (add(mapping.apply(target))) { return this; } } return this; } /** * Includes link parameters from the given targets. Each target is checked for the given * mappings in the order specified, and the first mapping that returns a non-null result is * added. */ @SafeVarargs public final Builder addTransitiveTargets( Iterable targets, Function firstMapping, @SuppressWarnings("unchecked") // Java arrays don't preserve generic arguments. Function... remainingMappings) { for (TransitiveInfoCollection target : targets) { addTransitiveTarget(target, firstMapping, remainingMappings); } return this; } /** * Merges the other {@link CcLinkParams} object into this one. */ public Builder addTransitiveArgs(CcLinkParams args) { linkOptsBuilder.addTransitive(args.getLinkopts()); linkstampsBuilder.addTransitive(args.getLinkstamps()); librariesBuilder.addTransitive(args.getLibraries()); dynamicLibrariesForRuntimeBuilder.addTransitive(args.getDynamicLibrariesForRuntime()); if (args.getExtraLinkTimeLibraries() != null) { if (extraLinkTimeLibrariesBuilder == null) { extraLinkTimeLibrariesBuilder = ExtraLinkTimeLibraries.builder(); } extraLinkTimeLibrariesBuilder.addTransitive(args.getExtraLinkTimeLibraries()); } if (args.getNonCodeInputs() != null) { if (nonCodeInputsBuilder == null) { nonCodeInputsBuilder = NestedSetBuilder.linkOrder(); } nonCodeInputsBuilder.addTransitive(args.getNonCodeInputs()); } return this; } /** * Adds a collection of link options. */ public Builder addLinkOpts(Collection linkOpts) { localLinkoptsBuilder.addAll(linkOpts); return this; } /** Adds a collection of linkstamps. */ public Builder addLinkstamps( NestedSet linkstamps, CcCompilationContext ccCompilationContext) { for (Artifact linkstamp : linkstamps) { linkstampsBuilder.add( new Linkstamp(linkstamp, ccCompilationContext.getDeclaredIncludeSrcs())); } return this; } /** * Adds a library artifact. */ public Builder addLibrary(LibraryToLink library) { librariesBuilder.add(library); return this; } /** * Adds a collection of library artifacts. */ public Builder addLibraries(Iterable libraries) { librariesBuilder.addAll(libraries); return this; } /** Adds a collection of library artifacts. */ public Builder addDynamicLibrariesForRuntime(Iterable libraries) { dynamicLibrariesForRuntimeBuilder.addAll(libraries); return this; } /** * Adds an extra link time library, a library that is actually * built at link time. */ public Builder addExtraLinkTimeLibrary(ExtraLinkTimeLibrary e) { if (extraLinkTimeLibrariesBuilder == null) { extraLinkTimeLibrariesBuilder = ExtraLinkTimeLibraries.builder(); } extraLinkTimeLibrariesBuilder.add(e); return this; } /** * Adds a collection of non-code inputs. */ public Builder addNonCodeInputs(Iterable nonCodeInputs) { if (nonCodeInputsBuilder == null) { nonCodeInputsBuilder = NestedSetBuilder.linkOrder(); } nonCodeInputsBuilder.addAll(nonCodeInputs); return this; } /** Processes typical dependencies of a C/C++ library. */ public Builder addCcLibrary(RuleContext context) { addTransitiveTargets( context.getPrerequisites("deps", Mode.TARGET), CcLinkParamsStore.TO_LINK_PARAMS); return this; } } /** * A linkstamp that also knows about its declared includes. * *

This object is required because linkstamp files may include other headers which will have to * be provided during compilation. */ @AutoCodec public static final class Linkstamp { private final Artifact artifact; private final NestedSet declaredIncludeSrcs; @VisibleForSerialization Linkstamp(Artifact artifact, NestedSet declaredIncludeSrcs) { this.artifact = Preconditions.checkNotNull(artifact); this.declaredIncludeSrcs = Preconditions.checkNotNull(declaredIncludeSrcs); } /** * Returns the linkstamp artifact. */ public Artifact getArtifact() { return artifact; } /** * Returns the declared includes. */ public NestedSet getDeclaredIncludeSrcs() { return declaredIncludeSrcs; } @Override public int hashCode() { return Objects.hash(artifact, declaredIncludeSrcs); } @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) && declaredIncludeSrcs.equals(other.declaredIncludeSrcs); } } /** Empty CcLinkParams. */ public static final CcLinkParams EMPTY = new CcLinkParams( NestedSetBuilder.emptySet(Order.LINK_ORDER), NestedSetBuilder.emptySet(Order.COMPILE_ORDER), NestedSetBuilder.emptySet(Order.LINK_ORDER), NestedSetBuilder.emptySet(Order.STABLE_ORDER), null, null); }