// 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. * *

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 { /** * 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 executionDynamicLibraries; private final ExtraLinkTimeLibraries extraLinkTimeLibraries; private final NestedSet nonCodeInputs; @AutoCodec.Instantiator @VisibleForSerialization CcLinkParams( NestedSet linkOpts, NestedSet linkstamps, NestedSet libraries, NestedSet executionDynamicLibraries, ExtraLinkTimeLibraries extraLinkTimeLibraries, NestedSet nonCodeInputs) { this.linkOpts = linkOpts; this.linkstamps = linkstamps; this.libraries = libraries; this.executionDynamicLibraries = executionDynamicLibraries; 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))); } /** * Returns the linkstamps */ public NestedSet getLinkstamps() { return linkstamps; } /** * Returns the libraries */ public NestedSet getLibraries() { return libraries; } /** * Returns the executionDynamicLibraries. */ public NestedSet 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 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 localLinkoptsBuilder = ImmutableList.builder(); private final NestedSetBuilder linkOptsBuilder = NestedSetBuilder.linkOrder(); private final NestedSetBuilder linkstampsBuilder = NestedSetBuilder.compileOrder(); private final NestedSetBuilder librariesBuilder = NestedSetBuilder.linkOrder(); private final NestedSetBuilder 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 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 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(), 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 targets) { for (TransitiveInfoCollection target : targets) { addTransitiveTarget(target); } return this; } /** * Includes link parameters from a dependency target. * *

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 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 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 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()); 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 linkOpts) { localLinkoptsBuilder.addAll(linkOpts); return this; } /** Adds a collection of linkstamps. */ public Builder addLinkstamps( NestedSet linkstamps, CcCompilationContextInfo ccCompilationContextInfo) { for (Artifact linkstamp : linkstamps) { linkstampsBuilder.add( new Linkstamp(linkstamp, ccCompilationContextInfo.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 addExecutionDynamicLibraries(Iterable 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 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. * *

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); }