/*
* 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.apple;
import com.facebook.buck.cxx.CxxBinary;
import com.facebook.buck.cxx.CxxLink;
import com.facebook.buck.cxx.LinkerMapMode;
import com.facebook.buck.cxx.ProvidesLinkedBinaryDeps;
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.SourcePathRuleFinder;
import com.facebook.buck.rules.Tool;
import com.facebook.buck.shell.DefaultShellStep;
import com.facebook.buck.step.Step;
import com.facebook.buck.step.fs.CopyStep;
import com.facebook.buck.step.fs.MakeCleanDirectoryStep;
import com.facebook.buck.step.fs.MkdirStep;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;
import java.util.SortedSet;
/** Puts together multiple thin library/binaries into a multi-arch file. */
public class MultiarchFile extends AbstractBuildRule implements ProvidesLinkedBinaryDeps {
private final SourcePathRuleFinder ruleFinder;
@AddToRuleKey private final Tool lipo;
@AddToRuleKey private final ImmutableSortedSet<SourcePath> thinBinaries;
@AddToRuleKey(stringify = true)
private final Path output;
public MultiarchFile(
BuildRuleParams buildRuleParams,
SourcePathRuleFinder ruleFinder,
Tool lipo,
SortedSet<SourcePath> thinBinaries,
Path output) {
super(buildRuleParams);
this.ruleFinder = ruleFinder;
this.lipo = lipo;
this.thinBinaries = ImmutableSortedSet.copyOf(thinBinaries);
this.output = output;
}
@Override
public ImmutableList<Step> getBuildSteps(
BuildContext context, BuildableContext buildableContext) {
buildableContext.recordArtifact(output);
ImmutableList.Builder<Step> steps = ImmutableList.builder();
steps.add(MkdirStep.of(getProjectFilesystem(), output.getParent()));
lipoBinaries(context, steps);
copyLinkMaps(buildableContext, steps);
return steps.build();
}
private void copyLinkMaps(BuildableContext buildableContext, ImmutableList.Builder<Step> steps) {
Path linkMapDir = Paths.get(output + "-LinkMap");
steps.addAll(MakeCleanDirectoryStep.of(getProjectFilesystem(), linkMapDir));
for (SourcePath thinBinary : thinBinaries) {
Optional<BuildRule> maybeRule = ruleFinder.getRule(thinBinary);
if (maybeRule.isPresent()) {
BuildRule rule = maybeRule.get();
if (rule instanceof CxxBinary) {
rule = ((CxxBinary) rule).getLinkRule();
}
if (rule instanceof CxxLink
&& !rule.getBuildTarget()
.getFlavors()
.contains(LinkerMapMode.NO_LINKER_MAP.getFlavor())) {
Optional<Path> maybeLinkerMapPath = ((CxxLink) rule).getLinkerMapPath();
if (maybeLinkerMapPath.isPresent()) {
Path source = maybeLinkerMapPath.get();
Path dest = linkMapDir.resolve(source.getFileName());
steps.add(CopyStep.forFile(getProjectFilesystem(), source, dest));
buildableContext.recordArtifact(dest);
}
}
}
}
}
private void lipoBinaries(BuildContext context, ImmutableList.Builder<Step> steps) {
ImmutableList.Builder<String> commandBuilder = ImmutableList.builder();
commandBuilder.addAll(lipo.getCommandPrefix(context.getSourcePathResolver()));
commandBuilder.add("-create", "-output", getProjectFilesystem().resolve(output).toString());
for (SourcePath thinBinary : thinBinaries) {
commandBuilder.add(context.getSourcePathResolver().getAbsolutePath(thinBinary).toString());
}
steps.add(
new DefaultShellStep(
getProjectFilesystem().getRootPath(),
commandBuilder.build(),
lipo.getEnvironment(context.getSourcePathResolver())));
}
@Override
public SourcePath getSourcePathToOutput() {
return new ExplicitBuildTargetSourcePath(getBuildTarget(), output);
}
@Override
public ImmutableSet<BuildRule> getStaticLibraryDeps() {
ImmutableSet.Builder<BuildRule> builder = ImmutableSet.builder();
for (BuildRule dep : getBuildDeps()) {
if (dep instanceof ProvidesLinkedBinaryDeps) {
builder.addAll(((ProvidesLinkedBinaryDeps) dep).getStaticLibraryDeps());
}
}
return builder.build();
}
@Override
public ImmutableSet<BuildRule> getCompileDeps() {
ImmutableSet.Builder<BuildRule> builder = ImmutableSet.builder();
for (BuildRule dep : getBuildDeps()) {
if (dep instanceof ProvidesLinkedBinaryDeps) {
builder.addAll(((ProvidesLinkedBinaryDeps) dep).getCompileDeps());
}
}
return builder.build();
}
}