blob: 4ad7a1b030dc731e07bcdb9029b95d6d4e749c55 [file] [log] [blame]
// 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) {
CcLinkingInfo ccLinkingInfo = target.get(CcLinkingInfo.PROVIDER);
CcLinkParamsInfo ccLinkParamsInfo =
ccLinkingInfo == null ? null : ccLinkingInfo.getCcLinkParamsInfo();
return addTransitiveProvider(ccLinkParamsInfo);
}
/**
* 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, 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<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);
}