| // 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 java.util.Collection; |
| import java.util.Objects; |
| import javax.annotation.Nullable; |
| |
| /** |
| * Parameters to be passed to the linker. |
| * |
| * <p>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. |
| * |
| * <p>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 { |
| /** |
| * A list of link options contributed by a single configured target. |
| * |
| * <p><b>WARNING:</b> 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<String> linkOptions; |
| |
| @VisibleForSerialization |
| LinkOptions(Iterable<String> linkOptions) { |
| this.linkOptions = ImmutableList.copyOf(linkOptions); |
| } |
| |
| public ImmutableList<String> get() { |
| return linkOptions; |
| } |
| |
| public static LinkOptions of(Iterable<String> linkOptions) { |
| return new LinkOptions(linkOptions); |
| } |
| } |
| |
| private final NestedSet<LinkOptions> linkOpts; |
| private final NestedSet<Linkstamp> linkstamps; |
| private final NestedSet<LibraryToLink> libraries; |
| private final NestedSet<Artifact> executionDynamicLibraries; |
| private final ExtraLinkTimeLibraries extraLinkTimeLibraries; |
| private final NestedSet<Artifact> nonCodeInputs; |
| |
| @AutoCodec.Instantiator |
| @VisibleForSerialization |
| CcLinkParams( |
| NestedSet<LinkOptions> linkOpts, |
| NestedSet<Linkstamp> linkstamps, |
| NestedSet<LibraryToLink> libraries, |
| NestedSet<Artifact> executionDynamicLibraries, |
| ExtraLinkTimeLibraries extraLinkTimeLibraries, |
| NestedSet<Artifact> nonCodeInputs) { |
| this.linkOpts = linkOpts; |
| this.linkstamps = linkstamps; |
| this.libraries = libraries; |
| this.executionDynamicLibraries = executionDynamicLibraries; |
| this.extraLinkTimeLibraries = extraLinkTimeLibraries; |
| this.nonCodeInputs = nonCodeInputs; |
| } |
| |
| /** |
| * Returns the linkopts |
| */ |
| public NestedSet<LinkOptions> getLinkopts() { |
| return linkOpts; |
| } |
| |
| public ImmutableList<String> flattenedLinkopts() { |
| return ImmutableList.copyOf(Iterables.concat(Iterables.transform(linkOpts, LinkOptions::get))); |
| } |
| |
| /** |
| * Returns the linkstamps |
| */ |
| public NestedSet<Linkstamp> getLinkstamps() { |
| return linkstamps; |
| } |
| |
| /** |
| * Returns the libraries |
| */ |
| public NestedSet<LibraryToLink> getLibraries() { |
| return libraries; |
| } |
| |
| /** |
| * Returns the executionDynamicLibraries. |
| */ |
| public NestedSet<Artifact> getExecutionDynamicLibraries() { |
| return executionDynamicLibraries; |
| } |
| |
| /** |
| * 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<Artifact> getNonCodeInputs() { |
| return nonCodeInputs; |
| } |
| |
| public static final Builder builder(boolean linkingStatically, boolean linkShared) { |
| return new Builder(linkingStatically, linkShared); |
| } |
| |
| /** |
| * 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 final boolean linkingStatically; |
| |
| /** |
| * linkShared is true when we're linking with "-shared" (linkshared=1). |
| */ |
| private final boolean linkShared; |
| |
| private ImmutableList.Builder<String> localLinkoptsBuilder = ImmutableList.builder(); |
| |
| private final NestedSetBuilder<LinkOptions> linkOptsBuilder = |
| NestedSetBuilder.linkOrder(); |
| private final NestedSetBuilder<Linkstamp> linkstampsBuilder = |
| NestedSetBuilder.compileOrder(); |
| private final NestedSetBuilder<LibraryToLink> librariesBuilder = |
| NestedSetBuilder.linkOrder(); |
| private final NestedSetBuilder<Artifact> executionDynamicLibrariesBuilder = |
| 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<Artifact> nonCodeInputsBuilder = null; |
| |
| private boolean built = false; |
| |
| private Builder(boolean linkingStatically, boolean linkShared) { |
| this.linkingStatically = linkingStatically; |
| this.linkShared = linkShared; |
| } |
| |
| /** |
| * 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<String> localLinkopts = localLinkoptsBuilder.build(); |
| if (!localLinkopts.isEmpty()) { |
| linkOptsBuilder.add(LinkOptions.of(localLinkopts)); |
| } |
| ExtraLinkTimeLibraries extraLinkTimeLibraries = null; |
| if (extraLinkTimeLibrariesBuilder != null) { |
| extraLinkTimeLibraries = extraLinkTimeLibrariesBuilder.build(); |
| } |
| NestedSet<Artifact> nonCodeInputs = null; |
| if (nonCodeInputsBuilder != null) { |
| nonCodeInputs = nonCodeInputsBuilder.build(); |
| } |
| return new CcLinkParams( |
| linkOptsBuilder.build(), |
| linkstampsBuilder.build(), |
| librariesBuilder.build(), |
| executionDynamicLibrariesBuilder.build(), |
| extraLinkTimeLibraries, |
| nonCodeInputs); |
| } |
| |
| public boolean add(CcLinkParamsStore store) { |
| 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<? extends TransitiveInfoCollection> targets) { |
| for (TransitiveInfoCollection target : targets) { |
| addTransitiveTarget(target); |
| } |
| return this; |
| } |
| |
| /** |
| * Includes link parameters from a dependency target. |
| * |
| * <p>The target should implement {@link CcLinkParamsInfo}. If it does not, |
| * the method does not do anything. |
| */ |
| public Builder addTransitiveTarget(TransitiveInfoCollection target) { |
| return addTransitiveProvider(target.get(CcLinkParamsInfo.PROVIDER)); |
| } |
| |
| /** |
| * 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<TransitiveInfoCollection, CcLinkParamsStore> firstMapping, |
| @SuppressWarnings("unchecked") // Java arrays don't preserve generic arguments. |
| Function<TransitiveInfoCollection, CcLinkParamsStore>... remainingMappings) { |
| if (add(firstMapping.apply(target))) { |
| return this; |
| } |
| for (Function<TransitiveInfoCollection, CcLinkParamsStore> mapping : remainingMappings) { |
| if (add(mapping.apply(target))) { |
| return this; |
| } |
| } |
| return this; |
| } |
| |
| /** |
| * Includes link parameters from a CcLinkParamsInfo provider. |
| */ |
| public Builder addTransitiveProvider(CcLinkParamsInfo provider) { |
| if (provider != null) { |
| add(provider.getCcLinkParamsStore()); |
| } |
| 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<? extends TransitiveInfoCollection> targets, |
| Function<TransitiveInfoCollection, CcLinkParamsStore> firstMapping, |
| @SuppressWarnings("unchecked") // Java arrays don't preserve generic arguments. |
| Function<TransitiveInfoCollection, CcLinkParamsStore>... 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()); |
| executionDynamicLibrariesBuilder.addTransitive(args.getExecutionDynamicLibraries()); |
| 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<String> linkOpts) { |
| localLinkoptsBuilder.addAll(linkOpts); |
| return this; |
| } |
| |
| /** Adds a collection of linkstamps. */ |
| public Builder addLinkstamps( |
| NestedSet<Artifact> linkstamps, CcCompilationInfo ccCompilationInfo) { |
| for (Artifact linkstamp : linkstamps) { |
| linkstampsBuilder.add(new Linkstamp(linkstamp, ccCompilationInfo.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<LibraryToLink> libraries) { |
| librariesBuilder.addAll(libraries); |
| return this; |
| } |
| |
| /** Adds a collection of library artifacts. */ |
| public Builder addExecutionDynamicLibraries(Iterable<Artifact> libraries) { |
| executionDynamicLibrariesBuilder.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<Artifact> 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), |
| CcLinkParamsInfo.TO_LINK_PARAMS, |
| CcSpecificLinkParamsProvider.TO_LINK_PARAMS); |
| return this; |
| } |
| } |
| |
| /** |
| * 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. |
| */ |
| @AutoCodec |
| public static final class Linkstamp { |
| private final Artifact artifact; |
| private final NestedSet<Artifact> declaredIncludeSrcs; |
| |
| @VisibleForSerialization |
| Linkstamp(Artifact artifact, NestedSet<Artifact> 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<Artifact> 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.<LinkOptions>emptySet(Order.LINK_ORDER), |
| NestedSetBuilder.<Linkstamp>emptySet(Order.COMPILE_ORDER), |
| NestedSetBuilder.<LibraryToLink>emptySet(Order.LINK_ORDER), |
| NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER), |
| null, |
| null); |
| } |