/*
* Copyright 2016-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.lua;
import com.facebook.buck.cxx.AbstractCxxLibrary;
import com.facebook.buck.cxx.CxxBuckConfig;
import com.facebook.buck.cxx.CxxLink;
import com.facebook.buck.cxx.CxxLinkableEnhancer;
import com.facebook.buck.cxx.CxxPlatform;
import com.facebook.buck.cxx.CxxPreprocessAndCompile;
import com.facebook.buck.cxx.CxxPreprocessables;
import com.facebook.buck.cxx.CxxPreprocessorDep;
import com.facebook.buck.cxx.CxxPreprocessorInput;
import com.facebook.buck.cxx.CxxSource;
import com.facebook.buck.cxx.CxxSourceRuleFactory;
import com.facebook.buck.cxx.HeaderVisibility;
import com.facebook.buck.cxx.Linker;
import com.facebook.buck.cxx.Linkers;
import com.facebook.buck.cxx.NativeLinkTarget;
import com.facebook.buck.cxx.NativeLinkTargetMode;
import com.facebook.buck.cxx.NativeLinkable;
import com.facebook.buck.cxx.NativeLinkableInput;
import com.facebook.buck.file.WriteFile;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.BuildTargets;
import com.facebook.buck.model.InternalFlavor;
import com.facebook.buck.parser.NoSuchBuildTargetException;
import com.facebook.buck.rules.BuildRule;
import com.facebook.buck.rules.BuildRuleParams;
import com.facebook.buck.rules.BuildRuleResolver;
import com.facebook.buck.rules.SourcePath;
import com.facebook.buck.rules.SourcePathResolver;
import com.facebook.buck.rules.SourcePathRuleFinder;
import com.facebook.buck.rules.WriteStringTemplateRule;
import com.facebook.buck.rules.args.SourcePathArg;
import com.facebook.buck.rules.args.StringArg;
import com.facebook.buck.util.Escaper;
import com.facebook.buck.util.immutables.BuckStyleTuple;
import com.google.common.base.Charsets;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicates;
import com.google.common.base.Suppliers;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables;
import com.google.common.io.Resources;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Optional;
import org.immutables.value.Value;
/** {@link Starter} implementation which builds a starter as a native executable. */
@Value.Immutable
@BuckStyleTuple
abstract class AbstractNativeExecutableStarter implements Starter, NativeLinkTarget {
private static final String NATIVE_STARTER_CXX_SOURCE =
"com/facebook/buck/lua/native-starter.cpp.in";
abstract BuildRuleParams getBaseParams();
abstract BuildRuleResolver getRuleResolver();
abstract SourcePathResolver getPathResolver();
abstract SourcePathRuleFinder getRuleFinder();
abstract LuaConfig getLuaConfig();
abstract CxxBuckConfig getCxxBuckConfig();
abstract CxxPlatform getCxxPlatform();
abstract BuildTarget getTarget();
abstract Path getOutput();
abstract String getMainModule();
abstract Optional<BuildTarget> getNativeStarterLibrary();
abstract Optional<Path> getRelativeModulesDir();
abstract Optional<Path> getRelativePythonModulesDir();
abstract Optional<Path> getRelativeNativeLibsDir();
private String getNativeStarterCxxSourceTemplate() {
try {
return Resources.toString(Resources.getResource(NATIVE_STARTER_CXX_SOURCE), Charsets.UTF_8);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private CxxSource getNativeStarterCxxSource() {
BuildTarget target =
BuildTarget.builder(getBaseParams().getBuildTarget())
.addFlavors(InternalFlavor.of("native-starter-cxx-source"))
.build();
BuildRule rule;
Optional<BuildRule> maybeRule = getRuleResolver().getRuleOptional(target);
if (maybeRule.isPresent()) {
rule = maybeRule.get();
} else {
BuildTarget templateTarget =
BuildTarget.builder(getBaseParams().getBuildTarget())
.addFlavors(InternalFlavor.of("native-starter-cxx-source-template"))
.build();
WriteFile templateRule =
getRuleResolver()
.addToIndex(
new WriteFile(
getBaseParams()
.withBuildTarget(templateTarget)
.copyReplacingDeclaredAndExtraDeps(
Suppliers.ofInstance(ImmutableSortedSet.of()),
Suppliers.ofInstance(ImmutableSortedSet.of())),
getNativeStarterCxxSourceTemplate(),
BuildTargets.getGenPath(
getBaseParams().getProjectFilesystem(),
templateTarget,
"%s/native-starter.cpp.in"),
/* executable */ false));
Path output =
BuildTargets.getGenPath(
getBaseParams().getProjectFilesystem(), target, "%s/native-starter.cpp");
rule =
getRuleResolver()
.addToIndex(
WriteStringTemplateRule.from(
getBaseParams(),
getRuleFinder(),
target,
output,
templateRule.getSourcePathToOutput(),
ImmutableMap.of(
"MAIN_MODULE",
Escaper.escapeAsPythonString(getMainModule()),
"MODULES_DIR",
getRelativeModulesDir().isPresent()
? Escaper.escapeAsPythonString(
getRelativeModulesDir().get().toString())
: "NULL",
"PY_MODULES_DIR",
getRelativePythonModulesDir().isPresent()
? Escaper.escapeAsPythonString(
getRelativePythonModulesDir().get().toString())
: "NULL",
"EXT_SUFFIX",
Escaper.escapeAsPythonString(
getCxxPlatform().getSharedLibraryExtension())),
/* executable */ false));
}
return CxxSource.of(
CxxSource.Type.CXX,
Preconditions.checkNotNull(rule.getSourcePathToOutput()),
ImmutableList.of());
}
private ImmutableList<CxxPreprocessorInput> getTransitiveCxxPreprocessorInput(
CxxPlatform cxxPlatform, Iterable<? extends CxxPreprocessorDep> deps)
throws NoSuchBuildTargetException {
ImmutableList.Builder<CxxPreprocessorInput> inputs = ImmutableList.builder();
inputs.addAll(
CxxPreprocessables.getTransitiveCxxPreprocessorInput(
cxxPlatform, FluentIterable.from(deps).filter(BuildRule.class)));
for (CxxPreprocessorDep dep :
Iterables.filter(deps, Predicates.not(BuildRule.class::isInstance))) {
inputs.add(dep.getCxxPreprocessorInput(cxxPlatform, HeaderVisibility.PUBLIC));
}
return inputs.build();
}
public Iterable<? extends AbstractCxxLibrary> getNativeStarterDeps() {
return ImmutableList.of(
getNativeStarterLibrary().isPresent()
? getRuleResolver()
.getRuleWithType(getNativeStarterLibrary().get(), AbstractCxxLibrary.class)
: getLuaConfig().getLuaCxxLibrary(getRuleResolver()));
}
private NativeLinkableInput getNativeLinkableInput() throws NoSuchBuildTargetException {
Iterable<? extends AbstractCxxLibrary> nativeStarterDeps = getNativeStarterDeps();
ImmutableMap<CxxPreprocessAndCompile, SourcePath> objects =
CxxSourceRuleFactory.requirePreprocessAndCompileRules(
getBaseParams(),
getRuleResolver(),
getPathResolver(),
getRuleFinder(),
getCxxBuckConfig(),
getCxxPlatform(),
ImmutableList.<CxxPreprocessorInput>builder()
.add(
CxxPreprocessorInput.builder()
.putAllPreprocessorFlags(
CxxSource.Type.CXX,
getNativeStarterLibrary().isPresent()
? ImmutableList.of()
: ImmutableList.of("-DBUILTIN_NATIVE_STARTER"))
.build())
.addAll(getTransitiveCxxPreprocessorInput(getCxxPlatform(), nativeStarterDeps))
.build(),
ImmutableMultimap.of(),
Optional.empty(),
Optional.empty(),
ImmutableMap.of("native-starter.cpp", getNativeStarterCxxSource()),
CxxSourceRuleFactory.PicType.PDC,
Optional.empty());
return NativeLinkableInput.builder()
.addAllArgs(
getRelativeNativeLibsDir().isPresent()
? StringArg.from(
Linkers.iXlinker(
"-rpath",
String.format(
"%s/%s",
getCxxPlatform().getLd().resolve(getRuleResolver()).origin(),
getRelativeNativeLibsDir().get().toString())))
: ImmutableList.of())
.addAllArgs(SourcePathArg.from(objects.values()))
.build();
}
@Override
public SourcePath build() throws NoSuchBuildTargetException {
BuildTarget linkTarget = getTarget();
CxxLink linkRule =
getRuleResolver()
.addToIndex(
CxxLinkableEnhancer.createCxxLinkableBuildRule(
getCxxBuckConfig(),
getCxxPlatform(),
getBaseParams(),
getRuleResolver(),
getPathResolver(),
getRuleFinder(),
linkTarget,
Linker.LinkType.EXECUTABLE,
Optional.empty(),
getOutput(),
Linker.LinkableDepType.SHARED,
/* thinLto */ false,
getNativeStarterDeps(),
Optional.empty(),
Optional.empty(),
ImmutableSet.of(),
getNativeLinkableInput()));
return linkRule.getSourcePathToOutput();
}
@Override
public BuildTarget getBuildTarget() {
return getBaseParams().getBuildTarget();
}
@Override
public NativeLinkTargetMode getNativeLinkTargetMode(CxxPlatform cxxPlatform) {
return NativeLinkTargetMode.executable();
}
@Override
public Iterable<? extends NativeLinkable> getNativeLinkTargetDeps(CxxPlatform cxxPlatform) {
return getNativeStarterDeps();
}
@Override
public NativeLinkableInput getNativeLinkTargetInput(CxxPlatform cxxPlatform)
throws NoSuchBuildTargetException {
return getNativeLinkableInput();
}
@Override
public Optional<Path> getNativeLinkTargetOutputPath(CxxPlatform cxxPlatform) {
return Optional.of(getOutput());
}
}