// 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.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
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.util.Preconditions;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
/**
* 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.
*/
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.
*/
@Immutable
public static final class LinkOptions {
private final ImmutableList linkOptions;
private 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 ExtraLinkTimeLibraries extraLinkTimeLibraries;
private CcLinkParams(NestedSet linkOpts,
NestedSet linkstamps,
NestedSet libraries,
ExtraLinkTimeLibraries extraLinkTimeLibraries) {
this.linkOpts = linkOpts;
this.linkstamps = linkstamps;
this.libraries = libraries;
this.extraLinkTimeLibraries = extraLinkTimeLibraries;
}
/**
* @return the linkopts
*/
public NestedSet getLinkopts() {
return linkOpts;
}
public ImmutableList flattenedLinkopts() {
return ImmutableList.copyOf(Iterables.concat(Iterables.transform(linkOpts,
new Function>() {
@Override
public ImmutableList apply(LinkOptions linkOptions) {
return linkOptions.get();
}
})));
}
/**
* @return the linkstamps
*/
public NestedSet getLinkstamps() {
return linkstamps;
}
/**
* @return the libraries
*/
public NestedSet getLibraries() {
return libraries;
}
/**
* The extra link time libraries; will be null if there are no such libraries.
*/
public ExtraLinkTimeLibraries getExtraLinkTimeLibraries() {
return extraLinkTimeLibraries;
}
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();
/**
* 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 boolean built = false;
private Builder(boolean linkingStatically, boolean linkShared) {
this.linkingStatically = linkingStatically;
this.linkShared = linkShared;
}
/**
* Build 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();
}
return new CcLinkParams(linkOptsBuilder.build(), linkstampsBuilder.build(),
librariesBuilder.build(), extraLinkTimeLibraries);
}
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.
*
* The target should implement {@link CcLinkParamsProvider}. If it does not,
* the method does not do anything.
*/
public Builder addTransitiveTarget(TransitiveInfoCollection target) {
return addTransitiveProvider(target.getProvider(CcLinkParamsProvider.class));
}
/**
* 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 CcLinkParamsProvider provider.
*/
public Builder addTransitiveProvider(CcLinkParamsProvider 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 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());
if (args.getExtraLinkTimeLibraries() != null) {
if (extraLinkTimeLibrariesBuilder == null) {
extraLinkTimeLibrariesBuilder = ExtraLinkTimeLibraries.builder();
}
extraLinkTimeLibrariesBuilder.addTransitive(args.getExtraLinkTimeLibraries());
}
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, CppCompilationContext context) {
for (Artifact linkstamp : linkstamps) {
linkstampsBuilder.add(new Linkstamp(linkstamp, context.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 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;
}
/**
* Processes typical dependencies a C/C++ library.
*
* A helper method that processes getValues() and merges contents of
* getPreferredLibraries() and getLinkOpts() into the current link params
* object.
*/
public Builder addCcLibrary(RuleContext context, boolean neverlink, List linkopts,
CcLinkingOutputs linkingOutputs) {
addTransitiveTargets(
context.getPrerequisites("deps", Mode.TARGET),
CcLinkParamsProvider.TO_LINK_PARAMS, CcSpecificLinkParamsProvider.TO_LINK_PARAMS);
if (!neverlink) {
addLibraries(linkingOutputs.getPreferredLibraries(linkingStatically,
linkShared || context.getFragment(CppConfiguration.class).forcePic()));
addLinkOpts(linkopts);
}
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.
*/
public static final class Linkstamp {
private final Artifact artifact;
private final NestedSet declaredIncludeSrcs;
private 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),
null);
}