// 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.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.rules.cpp.LinkerInputs.LibraryToLink;
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 and a
* list of libraries to be linked in.
*
*
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 {
private final NestedSet> linkOpts;
private final NestedSet linkstamps;
private final NestedSet libraries;
private CcLinkParams(NestedSet> linkOpts,
NestedSet linkstamps,
NestedSet libraries) {
this.linkOpts = linkOpts;
this.linkstamps = linkstamps;
this.libraries = libraries;
}
/**
* @return the linkopts
*/
public NestedSet> getLinkopts() {
return linkOpts;
}
public ImmutableList flattenedLinkopts() {
return ImmutableList.copyOf(Iterables.concat(linkOpts));
}
/**
* @return the linkstamps
*/
public NestedSet getLinkstamps() {
return linkstamps;
}
/**
* @return the libraries
*/
public NestedSet getLibraries() {
return libraries;
}
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 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(localLinkopts);
}
return new CcLinkParams(linkOptsBuilder.build(), linkstampsBuilder.build(),
librariesBuilder.build());
}
private 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());
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(Iterable linkstamps, CppCompilationContext context) {
ImmutableList declaredIncludeSrcs =
ImmutableList.copyOf(context.getDeclaredIncludeSrcs());
for (Artifact linkstamp : linkstamps) {
linkstampsBuilder.add(new Linkstamp(linkstamp, declaredIncludeSrcs));
}
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;
}
/**
* 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 ImmutableList declaredIncludeSrcs;
private Linkstamp(Artifact artifact, ImmutableList 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 ImmutableList 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));
}