/*
* 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.BuildTargets;
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.OverrideScheduleRule;
import com.facebook.buck.rules.RuleScheduleInfo;
import com.facebook.buck.rules.SourcePath;
import com.facebook.buck.rules.args.Arg;
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.LogContentsOfFileStep;
import com.facebook.buck.step.fs.MakeCleanDirectoryStep;
import com.facebook.buck.step.fs.MkdirStep;
import com.facebook.buck.step.fs.RmStep;
import com.google.common.base.Preconditions;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.nio.file.Path;
import java.util.Optional;
import java.util.logging.Level;
public class CxxLink extends AbstractBuildRule
implements SupportsInputBasedRuleKey, ProvidesLinkedBinaryDeps, OverrideScheduleRule {
@AddToRuleKey private final Linker linker;
@AddToRuleKey(stringify = true)
private final Path output;
@AddToRuleKey private final ImmutableList<Arg> args;
private final Optional<RuleScheduleInfo> ruleScheduleInfo;
private final boolean cacheable;
@AddToRuleKey private boolean thinLto;
public CxxLink(
BuildRuleParams params,
Linker linker,
Path output,
ImmutableList<Arg> args,
Optional<RuleScheduleInfo> ruleScheduleInfo,
boolean cacheable,
boolean thinLto) {
super(params);
this.linker = linker;
this.output = output;
this.args = args;
this.ruleScheduleInfo = ruleScheduleInfo;
this.cacheable = cacheable;
this.thinLto = thinLto;
performChecks(params);
}
private void performChecks(BuildRuleParams params) {
Preconditions.checkArgument(
!params.getBuildTarget().getFlavors().contains(CxxStrip.RULE_FLAVOR)
|| !StripStyle.FLAVOR_DOMAIN.containsAnyOf(params.getBuildTarget().getFlavors()),
"CxxLink should not be created with CxxStrip flavors");
}
@Override
public ImmutableList<Step> getBuildSteps(
BuildContext context, BuildableContext buildableContext) {
buildableContext.recordArtifact(output);
Optional<Path> linkerMapPath = getLinkerMapPath();
if (linkerMapPath.isPresent()
&& LinkerMapMode.isLinkerMapEnabledForBuildTarget(getBuildTarget())) {
buildableContext.recordArtifact(linkerMapPath.get());
}
if (linker instanceof HasThinLTO && thinLto) {
buildableContext.recordArtifact(((HasThinLTO) linker).thinLTOPath(output));
}
Path scratchDir =
BuildTargets.getScratchPath(getProjectFilesystem(), getBuildTarget(), "%s-tmp");
Path argFilePath =
getProjectFilesystem()
.getRootPath()
.resolve(
BuildTargets.getScratchPath(
getProjectFilesystem(), getBuildTarget(), "%s.argsfile"));
Path fileListPath =
getProjectFilesystem()
.getRootPath()
.resolve(
BuildTargets.getScratchPath(
getProjectFilesystem(), getBuildTarget(), "%s__filelist.txt"));
// Try to find all the cell roots used during the link. This isn't technically correct since,
// in theory not all inputs need to come from build rules, but it probably works in practice.
// One way that we know would work is exposing every known cell root paths, since the only rules
// that we built (and therefore need to scrub) will be in one of those roots.
ImmutableSet.Builder<Path> cellRoots = ImmutableSet.builder();
for (BuildRule dep : getBuildDeps()) {
cellRoots.add(dep.getProjectFilesystem().getRootPath());
}
return new ImmutableList.Builder<Step>()
.add(MkdirStep.of(getProjectFilesystem(), output.getParent()))
.addAll(MakeCleanDirectoryStep.of(getProjectFilesystem(), scratchDir))
.add(RmStep.of(getProjectFilesystem(), argFilePath))
.add(RmStep.of(getProjectFilesystem(), fileListPath))
.addAll(
CxxPrepareForLinkStep.create(
argFilePath,
fileListPath,
linker.fileList(fileListPath),
output,
args,
linker,
getBuildTarget().getCellPath(),
context.getSourcePathResolver()))
.add(
new CxxLinkStep(
getProjectFilesystem().getRootPath(),
linker.getEnvironment(context.getSourcePathResolver()),
linker.getCommandPrefix(context.getSourcePathResolver()),
argFilePath,
getProjectFilesystem().getRootPath().resolve(scratchDir)))
.add(
new FileScrubberStep(
getProjectFilesystem(), output, linker.getScrubbers(cellRoots.build())))
.add(new LogContentsOfFileStep(getProjectFilesystem().resolve(argFilePath), Level.FINEST))
.add(RmStep.of(getProjectFilesystem(), argFilePath))
.add(new LogContentsOfFileStep(getProjectFilesystem().resolve(fileListPath), Level.FINEST))
.add(RmStep.of(getProjectFilesystem(), fileListPath))
.add(RmStep.of(getProjectFilesystem(), scratchDir).withRecursive(true))
.build();
}
@Override
public ImmutableSet<BuildRule> getStaticLibraryDeps() {
return FluentIterable.from(getBuildDeps()).filter(Archive.class::isInstance).toSet();
}
@Override
public ImmutableSet<BuildRule> getCompileDeps() {
return FluentIterable.from(getBuildDeps())
.filter(CxxPreprocessAndCompile.class::isInstance)
.toSet();
}
@Override
public SourcePath getSourcePathToOutput() {
return new ExplicitBuildTargetSourcePath(getBuildTarget(), output);
}
@Override
public RuleScheduleInfo getRuleScheduleInfo() {
return ruleScheduleInfo.orElse(RuleScheduleInfo.DEFAULT);
}
@Override
public boolean isCacheable() {
return cacheable;
}
public Optional<Path> getLinkerMapPath() {
if (linker instanceof HasLinkerMap) {
return Optional.of(((HasLinkerMap) linker).linkerMapPath(output));
} else {
return Optional.empty();
}
}
public Linker getLinker() {
return linker;
}
public ImmutableList<Arg> getArgs() {
return args;
}
}