// 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.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. * * <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. */ public final class CcLinkParams { private final NestedSet<ImmutableList<String>> linkOpts; private final NestedSet<Linkstamp> linkstamps; private final NestedSet<LibraryToLink> libraries; private final ExtraLinkTimeLibraries extraLinkTimeLibraries; private CcLinkParams(NestedSet<ImmutableList<String>> linkOpts, NestedSet<Linkstamp> linkstamps, NestedSet<LibraryToLink> libraries, ExtraLinkTimeLibraries extraLinkTimeLibraries) { this.linkOpts = linkOpts; this.linkstamps = linkstamps; this.libraries = libraries; this.extraLinkTimeLibraries = extraLinkTimeLibraries; } /** * @return the linkopts */ public NestedSet<ImmutableList<String>> getLinkopts() { return linkOpts; } public ImmutableList<String> flattenedLinkopts() { return ImmutableList.copyOf(Iterables.concat(linkOpts)); } /** * @return the linkstamps */ public NestedSet<Linkstamp> getLinkstamps() { return linkstamps; } /** * @return the libraries */ public NestedSet<LibraryToLink> 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<String> localLinkoptsBuilder = ImmutableList.builder(); private final NestedSetBuilder<ImmutableList<String>> linkOptsBuilder = NestedSetBuilder.linkOrder(); private final NestedSetBuilder<Linkstamp> linkstampsBuilder = NestedSetBuilder.compileOrder(); private final NestedSetBuilder<LibraryToLink> 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<String> localLinkopts = localLinkoptsBuilder.build(); if (!localLinkopts.isEmpty()) { linkOptsBuilder.add(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. * * <p>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<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 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<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()); 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<String> linkOpts) { localLinkoptsBuilder.addAll(linkOpts); return this; } /** * Adds a collection of linkstamps. */ public Builder addLinkstamps(NestedSet<Artifact> 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<LibraryToLink> 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. * * <p>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<String> 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. * * <p>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<Artifact> declaredIncludeSrcs; private 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.<ImmutableList<String>>emptySet(Order.LINK_ORDER), NestedSetBuilder.<Linkstamp>emptySet(Order.COMPILE_ORDER), NestedSetBuilder.<LibraryToLink>emptySet(Order.LINK_ORDER), null); }