/* * Copyright 2014-present Facebook, Inc. * * 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.facebook.buck.cxx; import com.facebook.buck.model.BuildTarget; import com.facebook.buck.rules.AbstractBuildRule; import com.facebook.buck.rules.AddToRuleKey; import com.facebook.buck.rules.BuildContext; import com.facebook.buck.rules.BuildRule; import com.facebook.buck.rules.BuildRuleParams; import com.facebook.buck.rules.BuildableContext; import com.facebook.buck.rules.ExplicitBuildTargetSourcePath; import com.facebook.buck.rules.SourcePath; import com.facebook.buck.rules.SourcePathResolver; import com.facebook.buck.rules.SourcePathRuleFinder; import com.facebook.buck.rules.Tool; import com.facebook.buck.rules.args.Arg; import com.facebook.buck.rules.args.SourcePathArg; import com.facebook.buck.rules.keys.SupportsInputBasedRuleKey; import com.facebook.buck.step.Step; import com.facebook.buck.step.fs.FileScrubberStep; import com.facebook.buck.step.fs.MkdirStep; import com.facebook.buck.step.fs.RmStep; import com.facebook.buck.util.MoreCollectors; import com.google.common.base.Preconditions; import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSortedSet; import java.nio.file.Path; /** * A {@link com.facebook.buck.rules.BuildRule} which builds an "ar" archive from input files * represented as {@link com.facebook.buck.rules.SourcePath}. */ public class Archive extends AbstractBuildRule implements SupportsInputBasedRuleKey { @AddToRuleKey private final Archiver archiver; @AddToRuleKey private ImmutableList<String> archiverFlags; @AddToRuleKey private final Tool ranlib; @AddToRuleKey private ImmutableList<String> ranlibFlags; @AddToRuleKey private final Contents contents; @AddToRuleKey(stringify = true) private final Path output; @AddToRuleKey private final ImmutableList<SourcePath> inputs; private Archive( BuildRuleParams params, Archiver archiver, ImmutableList<String> archiverFlags, Tool ranlib, ImmutableList<String> ranlibFlags, Contents contents, Path output, ImmutableList<SourcePath> inputs) { super(params); Preconditions.checkState( contents == Contents.NORMAL || archiver.supportsThinArchives(), "%s: archive tool for this platform does not support thin archives", getBuildTarget()); Preconditions.checkArgument( !LinkerMapMode.FLAVOR_DOMAIN.containsAnyOf(params.getBuildTarget().getFlavors()), "Static archive rule %s should not have any Linker Map Mode flavors", this); this.archiver = archiver; this.archiverFlags = archiverFlags; this.ranlib = ranlib; this.ranlibFlags = ranlibFlags; this.contents = contents; this.output = output; this.inputs = inputs; } public static Archive from( BuildTarget target, BuildRuleParams baseParams, SourcePathRuleFinder ruleFinder, CxxPlatform platform, Contents contents, Path output, ImmutableList<SourcePath> inputs) { return from( target, baseParams, ruleFinder, platform.getAr(), platform.getArflags(), platform.getRanlib(), platform.getRanlibflags(), contents, output, inputs); } /** * Construct an {@link com.facebook.buck.cxx.Archive} from a {@link * com.facebook.buck.rules.BuildRuleParams} object representing a target node. In particular, make * sure to trim dependencies to *only* those that provide the input {@link * com.facebook.buck.rules.SourcePath}. */ public static Archive from( BuildTarget target, BuildRuleParams baseParams, SourcePathRuleFinder ruleFinder, Archiver archiver, ImmutableList<String> arFlags, Tool ranlib, ImmutableList<String> ranlibFlags, Contents contents, Path output, ImmutableList<SourcePath> inputs) { // Convert the input build params into ones specialized for this archive build rule. // In particular, we only depend on BuildRules directly from the input file SourcePaths. BuildRuleParams archiveParams = baseParams .withBuildTarget(target) .copyReplacingDeclaredAndExtraDeps( Suppliers.ofInstance(ImmutableSortedSet.of()), Suppliers.ofInstance( ImmutableSortedSet.<BuildRule>naturalOrder() .addAll(ruleFinder.filterBuildRuleInputs(inputs)) .addAll(archiver.getDeps(ruleFinder)) .build())); return new Archive( archiveParams, archiver, arFlags, ranlib, ranlibFlags, contents, output, inputs); } @Override public ImmutableList<Step> getBuildSteps( BuildContext context, BuildableContext buildableContext) { // Cache the archive we built. buildableContext.recordArtifact(output); SourcePathResolver resolver = context.getSourcePathResolver(); // We only support packaging inputs that use the same filesystem root as the output, as thin // archives embed relative paths from output to input inside the archive. If this becomes a // limitation, we could make this rule uncacheable and allow thin archives to embed absolute // paths. for (SourcePath input : inputs) { Preconditions.checkState( resolver.getFilesystem(input).getRootPath().equals(getProjectFilesystem().getRootPath())); } ImmutableList.Builder<Step> builder = ImmutableList.builder(); builder.add( MkdirStep.of(getProjectFilesystem(), output.getParent()), RmStep.of(getProjectFilesystem(), output), new ArchiveStep( getProjectFilesystem(), archiver.getEnvironment(resolver), archiver.getCommandPrefix(resolver), archiverFlags, archiver.getArchiveOptions(contents == Contents.THIN), output, inputs .stream() .map(resolver::getRelativePath) .collect(MoreCollectors.toImmutableList()), archiver)); if (archiver.isRanLibStepRequired()) { builder.add( new RanlibStep( getProjectFilesystem(), ranlib.getEnvironment(resolver), ranlib.getCommandPrefix(resolver), ranlibFlags, output)); } if (!archiver.getScrubbers().isEmpty()) { builder.add(new FileScrubberStep(getProjectFilesystem(), output, archiver.getScrubbers())); } return builder.build(); } /** * @return the {@link Arg} to use when using this archive. When thin archives are used, this will * ensure that the inputs are also propagated as build time deps to whatever rule uses this * archive. */ public Arg toArg() { SourcePath archive = getSourcePathToOutput(); return contents == Contents.NORMAL ? SourcePathArg.of(archive) : ThinArchiveArg.of(archive, inputs); } @Override public SourcePath getSourcePathToOutput() { return new ExplicitBuildTargetSourcePath(getBuildTarget(), output); } public Contents getContents() { return contents; } /** How this archive packages its contents. */ public enum Contents { /** This archive packages a copy of its inputs and can be used independently of its inputs. */ NORMAL, /** * This archive only packages the relative paths to its inputs and so can only be used when its * inputs are available. */ THIN, } }