/* * Copyright 2015-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.CxxBuckConfig; import com.facebook.buck.cxx.CxxPlatform; import com.facebook.buck.cxx.NativeLinkStrategy; import com.facebook.buck.cxx.NativeLinkTarget; import com.facebook.buck.cxx.NativeLinkTargetMode; import com.facebook.buck.cxx.NativeLinkable; import com.facebook.buck.cxx.NativeLinkables; import com.facebook.buck.cxx.Omnibus; import com.facebook.buck.cxx.OmnibusLibraries; import com.facebook.buck.cxx.OmnibusLibrary; import com.facebook.buck.cxx.OmnibusRoot; import com.facebook.buck.cxx.OmnibusRoots; import com.facebook.buck.graph.AbstractBreadthFirstThrowingTraversal; import com.facebook.buck.io.MorePaths; import com.facebook.buck.io.ProjectFilesystem; import com.facebook.buck.model.BuildTarget; import com.facebook.buck.model.BuildTargets; import com.facebook.buck.model.Flavor; import com.facebook.buck.model.FlavorDomain; import com.facebook.buck.model.InternalFlavor; import com.facebook.buck.parser.NoSuchBuildTargetException; import com.facebook.buck.python.CxxPythonExtension; import com.facebook.buck.python.PythonBinaryDescription; import com.facebook.buck.python.PythonPackagable; import com.facebook.buck.python.PythonPackageComponents; import com.facebook.buck.python.PythonPlatform; import com.facebook.buck.rules.BuildRule; import com.facebook.buck.rules.BuildRuleParams; import com.facebook.buck.rules.BuildRuleResolver; import com.facebook.buck.rules.CellPathResolver; import com.facebook.buck.rules.CommandTool; import com.facebook.buck.rules.CommonDescriptionArg; import com.facebook.buck.rules.Description; import com.facebook.buck.rules.HasDeclaredDeps; import com.facebook.buck.rules.ImplicitDepsInferringDescription; import com.facebook.buck.rules.RuleKeyObjectSink; import com.facebook.buck.rules.SourcePath; import com.facebook.buck.rules.SourcePathResolver; import com.facebook.buck.rules.SourcePathRuleFinder; import com.facebook.buck.rules.SymlinkTree; import com.facebook.buck.rules.TargetGraph; import com.facebook.buck.rules.Tool; import com.facebook.buck.rules.args.SourcePathArg; import com.facebook.buck.util.HumanReadableException; import com.facebook.buck.util.MoreMaps; import com.facebook.buck.util.OptionalCompat; import com.facebook.buck.util.immutables.BuckStyleImmutable; import com.facebook.buck.versions.VersionRoot; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.base.Splitter; import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.immutables.value.Value; public class LuaBinaryDescription implements Description<LuaBinaryDescriptionArg>, ImplicitDepsInferringDescription<LuaBinaryDescription.AbstractLuaBinaryDescriptionArg>, VersionRoot<LuaBinaryDescriptionArg> { private static final Flavor BINARY_FLAVOR = InternalFlavor.of("binary"); private final LuaConfig luaConfig; private final CxxBuckConfig cxxBuckConfig; private final CxxPlatform defaultCxxPlatform; private final FlavorDomain<CxxPlatform> cxxPlatforms; private final FlavorDomain<PythonPlatform> pythonPlatforms; public LuaBinaryDescription( LuaConfig luaConfig, CxxBuckConfig cxxBuckConfig, CxxPlatform defaultCxxPlatform, FlavorDomain<CxxPlatform> cxxPlatforms, FlavorDomain<PythonPlatform> pythonPlatforms) { this.luaConfig = luaConfig; this.cxxBuckConfig = cxxBuckConfig; this.defaultCxxPlatform = defaultCxxPlatform; this.cxxPlatforms = cxxPlatforms; this.pythonPlatforms = pythonPlatforms; } @Override public Class<LuaBinaryDescriptionArg> getConstructorArgType() { return LuaBinaryDescriptionArg.class; } @VisibleForTesting protected static BuildTarget getNativeLibsSymlinkTreeTarget(BuildTarget target) { return target.withAppendedFlavors(InternalFlavor.of("native-libs-link-tree")); } private static Path getNativeLibsSymlinkTreeRoot( BuildTarget target, ProjectFilesystem filesystem) { return BuildTargets.getGenPath(filesystem, getNativeLibsSymlinkTreeTarget(target), "%s"); } private static BuildTarget getModulesSymlinkTreeTarget(BuildTarget target) { return target.withAppendedFlavors(InternalFlavor.of("modules-link-tree")); } private static Path getModulesSymlinkTreeRoot(BuildTarget target, ProjectFilesystem filesystem) { return BuildTargets.getGenPath(filesystem, getModulesSymlinkTreeTarget(target), "%s"); } private static BuildTarget getPythonModulesSymlinkTreeTarget(BuildTarget target) { return target.withAppendedFlavors(InternalFlavor.of("python-modules-link-tree")); } private static Path getPythonModulesSymlinkTreeRoot( BuildTarget target, ProjectFilesystem filesystem) { return BuildTargets.getGenPath(filesystem, getPythonModulesSymlinkTreeTarget(target), "%s"); } private Path getOutputPath(BuildTarget target, ProjectFilesystem filesystem) { return BuildTargets.getGenPath(filesystem, target, "%s" + luaConfig.getExtension()); } private Iterable<BuildTarget> getNativeStarterDepTargets() { Optional<BuildTarget> nativeStarterLibrary = luaConfig.getNativeStarterLibrary(); return ImmutableSet.copyOf( nativeStarterLibrary.isPresent() ? OptionalCompat.asSet(nativeStarterLibrary) : OptionalCompat.asSet(luaConfig.getLuaCxxLibraryTarget())); } private Starter getStarter( BuildRuleParams baseParams, BuildRuleResolver ruleResolver, SourcePathResolver pathResolver, SourcePathRuleFinder ruleFinder, CxxPlatform cxxPlatform, BuildTarget target, Path output, StarterType starterType, Optional<BuildTarget> nativeStarterLibrary, String mainModule, Optional<Path> relativeModulesDir, Optional<Path> relativePythonModulesDir, Optional<Path> relativeNativeLibsDir) { switch (starterType) { case PURE: if (relativeNativeLibsDir.isPresent()) { throw new HumanReadableException( "%s: cannot use pure starter with native libraries", baseParams.getBuildTarget()); } return LuaScriptStarter.of( baseParams, ruleResolver, pathResolver, ruleFinder, luaConfig, cxxPlatform, target, output, mainModule, relativeModulesDir, relativePythonModulesDir); case NATIVE: return NativeExecutableStarter.of( baseParams, ruleResolver, pathResolver, ruleFinder, luaConfig, cxxBuckConfig, cxxPlatform, target, output, mainModule, nativeStarterLibrary, relativeModulesDir, relativePythonModulesDir, relativeNativeLibsDir); } throw new IllegalStateException( String.format( "%s: unexpected starter type %s", baseParams.getBuildTarget(), luaConfig.getStarterType())); } private StarterType getStarterType(boolean mayHaveNativeCode) { return luaConfig .getStarterType() .orElse(mayHaveNativeCode ? StarterType.NATIVE : StarterType.PURE); } /** @return the {@link Starter} used to build the Lua binary entry point. */ private Starter createStarter( BuildRuleParams baseParams, BuildRuleResolver ruleResolver, SourcePathResolver pathResolver, SourcePathRuleFinder ruleFinder, final CxxPlatform cxxPlatform, Optional<BuildTarget> nativeStarterLibrary, String mainModule, LuaConfig.PackageStyle packageStyle, boolean mayHaveNativeCode) { Path output = getOutputPath(baseParams.getBuildTarget(), baseParams.getProjectFilesystem()); StarterType starterType = getStarterType(mayHaveNativeCode); // The relative paths from the starter to the various components. Optional<Path> relativeModulesDir = Optional.empty(); Optional<Path> relativePythonModulesDir = Optional.empty(); Optional<Path> relativeNativeLibsDir = Optional.empty(); // For in-place binaries, set the relative paths to the symlink trees holding the components. if (packageStyle == LuaConfig.PackageStyle.INPLACE) { relativeModulesDir = Optional.of( output .getParent() .relativize( getModulesSymlinkTreeRoot( baseParams.getBuildTarget(), baseParams.getProjectFilesystem()))); relativePythonModulesDir = Optional.of( output .getParent() .relativize( getPythonModulesSymlinkTreeRoot( baseParams.getBuildTarget(), baseParams.getProjectFilesystem()))); // We only need to setup a native lib link tree if we're using a native starter. if (starterType == StarterType.NATIVE) { relativeNativeLibsDir = Optional.of( output .getParent() .relativize( getNativeLibsSymlinkTreeRoot( baseParams.getBuildTarget(), baseParams.getProjectFilesystem()))); } } // Build the starter. return getStarter( baseParams, ruleResolver, pathResolver, ruleFinder, cxxPlatform, baseParams .getBuildTarget() .withAppendedFlavors( packageStyle == LuaConfig.PackageStyle.STANDALONE ? InternalFlavor.of("starter") : BINARY_FLAVOR), packageStyle == LuaConfig.PackageStyle.STANDALONE ? output.resolveSibling(output.getFileName() + "-starter") : output, starterType, nativeStarterLibrary, mainModule, relativeModulesDir, relativePythonModulesDir, relativeNativeLibsDir); } private LuaBinaryPackageComponents getPackageComponentsFromDeps( BuildRuleParams baseParams, BuildRuleResolver ruleResolver, SourcePathResolver pathResolver, SourcePathRuleFinder ruleFinder, final CxxPlatform cxxPlatform, final PythonPlatform pythonPlatform, Optional<BuildTarget> nativeStarterLibrary, String mainModule, LuaConfig.PackageStyle packageStyle, Iterable<BuildRule> deps) throws NoSuchBuildTargetException { final LuaPackageComponents.Builder builder = LuaPackageComponents.builder(); final OmnibusRoots.Builder omnibusRoots = OmnibusRoots.builder(cxxPlatform, ImmutableSet.of()); final Map<BuildTarget, NativeLinkable> nativeLinkableRoots = new LinkedHashMap<>(); final Map<BuildTarget, CxxLuaExtension> luaExtensions = new LinkedHashMap<>(); final Map<BuildTarget, CxxPythonExtension> pythonExtensions = new LinkedHashMap<>(); // Walk the deps to find all Lua packageables and native linkables. new AbstractBreadthFirstThrowingTraversal<BuildRule, NoSuchBuildTargetException>(deps) { private final ImmutableSet<BuildRule> empty = ImmutableSet.of(); @Override public Iterable<BuildRule> visit(BuildRule rule) throws NoSuchBuildTargetException { Iterable<BuildRule> deps = empty; if (rule instanceof LuaPackageable) { LuaPackageable packageable = (LuaPackageable) rule; LuaPackageComponents components = packageable.getLuaPackageComponents(); LuaPackageComponents.addComponents(builder, components); if (components.hasNativeCode(cxxPlatform)) { for (BuildRule dep : rule.getBuildDeps()) { if (dep instanceof NativeLinkable) { NativeLinkable linkable = (NativeLinkable) dep; nativeLinkableRoots.put(linkable.getBuildTarget(), linkable); omnibusRoots.addExcludedRoot(linkable); } } } deps = rule.getBuildDeps(); } else if (rule instanceof CxxPythonExtension) { CxxPythonExtension extension = (CxxPythonExtension) rule; NativeLinkTarget target = extension.getNativeLinkTarget(pythonPlatform); pythonExtensions.put(target.getBuildTarget(), (CxxPythonExtension) rule); omnibusRoots.addIncludedRoot(target); } else if (rule instanceof PythonPackagable) { PythonPackagable packageable = (PythonPackagable) rule; PythonPackageComponents components = packageable.getPythonPackageComponents(pythonPlatform, cxxPlatform); builder.putAllPythonModules( MoreMaps.transformKeys(components.getModules(), Object::toString)); builder.putAllNativeLibraries( MoreMaps.transformKeys(components.getNativeLibraries(), Object::toString)); deps = packageable.getPythonPackageDeps(pythonPlatform, cxxPlatform); if (components.hasNativeCode(cxxPlatform)) { for (BuildRule dep : deps) { if (dep instanceof NativeLinkable) { NativeLinkable linkable = (NativeLinkable) dep; nativeLinkableRoots.put(linkable.getBuildTarget(), linkable); omnibusRoots.addExcludedRoot(linkable); } } } } else if (rule instanceof CxxLuaExtension) { CxxLuaExtension extension = (CxxLuaExtension) rule; luaExtensions.put(extension.getBuildTarget(), extension); omnibusRoots.addIncludedRoot(extension); } else if (rule instanceof NativeLinkable) { NativeLinkable linkable = (NativeLinkable) rule; nativeLinkableRoots.put(linkable.getBuildTarget(), linkable); omnibusRoots.addPotentialRoot(linkable); } return deps; } }.start(); // Build the starter. Starter starter = createStarter( baseParams, ruleResolver, pathResolver, ruleFinder, cxxPlatform, nativeStarterLibrary, mainModule, packageStyle, !nativeLinkableRoots.isEmpty() || !omnibusRoots.isEmpty()); SourcePath starterPath = null; if (luaConfig.getNativeLinkStrategy() == NativeLinkStrategy.MERGED) { // If we're using a native starter, include it in omnibus linking. if (starter instanceof NativeExecutableStarter) { NativeExecutableStarter nativeStarter = (NativeExecutableStarter) starter; omnibusRoots.addIncludedRoot(nativeStarter); } // Build the omnibus libraries. OmnibusRoots roots = omnibusRoots.build(); OmnibusLibraries libraries = Omnibus.getSharedLibraries( baseParams, ruleResolver, ruleFinder, cxxBuckConfig, cxxPlatform, ImmutableList.of(), roots.getIncludedRoots().values(), roots.getExcludedRoots().values()); // Add all the roots from the omnibus link. If it's an extension, add it as a module. for (Map.Entry<BuildTarget, OmnibusRoot> root : libraries.getRoots().entrySet()) { // If it's a Lua extension add it as a module. CxxLuaExtension luaExtension = luaExtensions.get(root.getKey()); if (luaExtension != null) { builder.putModules(luaExtension.getModule(cxxPlatform), root.getValue().getPath()); continue; } // If it's a Python extension, add it as a python module. CxxPythonExtension pythonExtension = pythonExtensions.get(root.getKey()); if (pythonExtension != null) { builder.putPythonModules( pythonExtension.getModule().toString(), root.getValue().getPath()); continue; } // A root named after the top-level target is our native starter. if (root.getKey().equals(baseParams.getBuildTarget())) { starterPath = root.getValue().getPath(); continue; } // Otherwise, add it as a native library. NativeLinkTarget target = Preconditions.checkNotNull( roots.getIncludedRoots().get(root.getKey()), "%s: linked unexpected omnibus root: %s", baseParams.getBuildTarget(), root.getKey()); NativeLinkTargetMode mode = target.getNativeLinkTargetMode(cxxPlatform); String soname = Preconditions.checkNotNull( mode.getLibraryName().orElse(null), "%s: omnibus library for %s was built without soname", baseParams.getBuildTarget(), root.getKey()); builder.putNativeLibraries(soname, root.getValue().getPath()); } // Add all remaining libraries as native libraries. for (OmnibusLibrary library : libraries.getLibraries()) { builder.putNativeLibraries(library.getSoname(), library.getPath()); } } else { // For regular linking, add all Lua extensions as modules and their deps as native linkable // roots. for (Map.Entry<BuildTarget, CxxLuaExtension> entry : luaExtensions.entrySet()) { CxxLuaExtension extension = entry.getValue(); builder.putModules(extension.getModule(cxxPlatform), extension.getExtension(cxxPlatform)); nativeLinkableRoots.putAll( Maps.uniqueIndex( extension.getNativeLinkTargetDeps(cxxPlatform), NativeLinkable::getBuildTarget)); } // Add in native executable deps. if (starter instanceof NativeExecutableStarter) { NativeExecutableStarter executableStarter = (NativeExecutableStarter) starter; nativeLinkableRoots.putAll( Maps.uniqueIndex( executableStarter.getNativeStarterDeps(), NativeLinkable::getBuildTarget)); } // For regular linking, add all extensions via the package components interface and their // python-platform specific deps to the native linkables. for (Map.Entry<BuildTarget, CxxPythonExtension> entry : pythonExtensions.entrySet()) { PythonPackageComponents components = entry.getValue().getPythonPackageComponents(pythonPlatform, cxxPlatform); builder.putAllPythonModules( MoreMaps.transformKeys(components.getModules(), Object::toString)); builder.putAllNativeLibraries( MoreMaps.transformKeys(components.getNativeLibraries(), Object::toString)); nativeLinkableRoots.putAll( Maps.uniqueIndex( entry .getValue() .getNativeLinkTarget(pythonPlatform) .getNativeLinkTargetDeps(cxxPlatform), NativeLinkable::getBuildTarget)); } // Add shared libraries from all native linkables. for (NativeLinkable nativeLinkable : NativeLinkables.getTransitiveNativeLinkables(cxxPlatform, nativeLinkableRoots.values()) .values()) { NativeLinkable.Linkage linkage = nativeLinkable.getPreferredLinkage(cxxPlatform); if (linkage != NativeLinkable.Linkage.STATIC) { builder.putAllNativeLibraries(nativeLinkable.getSharedLibraries(cxxPlatform)); } } } // If an explicit starter path override hasn't been set (e.g. from omnibus linking), default to // building one directly from the starter. if (starterPath == null) { starterPath = starter.build(); } return LuaBinaryPackageComponents.of(starterPath, builder.build()); } private SymlinkTree createSymlinkTree( BuildTarget linkTreeTarget, ProjectFilesystem filesystem, BuildRuleResolver resolver, SourcePathRuleFinder ruleFinder, Path root, ImmutableMap<String, SourcePath> components) { return resolver.addToIndex( new SymlinkTree( linkTreeTarget, filesystem, root, MoreMaps.transformKeys(components, MorePaths.toPathFn(root.getFileSystem())), ruleFinder)); } /** * @return the native library map with additional entries for library names with the version * suffix stripped (e.g. libfoo.so.1.0 -> libfoo.so) to appease LuaJIT, which wants to load * libraries using the build-time name. */ private ImmutableSortedMap<String, SourcePath> addVersionLessLibraries( CxxPlatform cxxPlatform, ImmutableSortedMap<String, SourcePath> libraries) { Pattern versionedExtension = Pattern.compile( Joiner.on("[.\\d]*") .join( Iterables.transform( Splitter.on("%s") .split(cxxPlatform.getSharedLibraryVersionedExtensionFormat()), input -> input.isEmpty() ? input : Pattern.quote(input)))); Map<String, SourcePath> librariesPaths = new HashMap<>(); for (Map.Entry<String, SourcePath> ent : libraries.entrySet()) { String name = ent.getKey(); if (librariesPaths.containsKey(name) && librariesPaths.get(name) != ent.getValue()) { throw new HumanReadableException( "Library %s has multiple possible paths: %s and %s", name, ent.getValue(), librariesPaths.get(name)); } librariesPaths.put(name, ent.getValue()); Matcher matcher = versionedExtension.matcher(name); String versionLessName = matcher.replaceAll(cxxPlatform.getSharedLibraryExtension()); if (!versionLessName.equals(ent.getKey()) && !libraries.containsKey(versionLessName)) { librariesPaths.put(versionLessName, ent.getValue()); } } return ImmutableSortedMap.copyOf(librariesPaths); } private Tool getInPlaceBinary( BuildRuleParams params, BuildRuleResolver resolver, SourcePathRuleFinder ruleFinder, CxxPlatform cxxPlatform, final SourcePath starter, final LuaPackageComponents components) { final List<SourcePath> extraInputs = new ArrayList<>(); final SymlinkTree modulesLinkTree = resolver.addToIndex( createSymlinkTree( getModulesSymlinkTreeTarget(params.getBuildTarget()), params.getProjectFilesystem(), resolver, ruleFinder, getModulesSymlinkTreeRoot(params.getBuildTarget(), params.getProjectFilesystem()), components.getModules())); final List<SymlinkTree> pythonModulesLinktree = new ArrayList<>(); if (!components.getPythonModules().isEmpty()) { // Add in any missing init modules into the python components. SourcePath emptyInit = PythonBinaryDescription.createEmptyInitModule(params, resolver); extraInputs.add(emptyInit); ImmutableMap<String, SourcePath> pythonModules = MoreMaps.transformKeys( PythonBinaryDescription.addMissingInitModules( MoreMaps.transformKeys( components.getPythonModules(), MorePaths.toPathFn( params.getProjectFilesystem().getRootPath().getFileSystem())), emptyInit), Object::toString); final SymlinkTree symlinkTree = resolver.addToIndex( createSymlinkTree( getPythonModulesSymlinkTreeTarget(params.getBuildTarget()), params.getProjectFilesystem(), resolver, ruleFinder, getPythonModulesSymlinkTreeRoot( params.getBuildTarget(), params.getProjectFilesystem()), pythonModules)); pythonModulesLinktree.add(symlinkTree); } final List<SymlinkTree> nativeLibsLinktree = new ArrayList<>(); if (!components.getNativeLibraries().isEmpty()) { SymlinkTree symlinkTree = resolver.addToIndex( createSymlinkTree( getNativeLibsSymlinkTreeTarget(params.getBuildTarget()), params.getProjectFilesystem(), resolver, ruleFinder, getNativeLibsSymlinkTreeRoot( params.getBuildTarget(), params.getProjectFilesystem()), addVersionLessLibraries(cxxPlatform, components.getNativeLibraries()))); nativeLibsLinktree.add(symlinkTree); } return new Tool() { @Override public ImmutableCollection<BuildRule> getDeps(SourcePathRuleFinder ruleFinder) { return ImmutableSortedSet.<BuildRule>naturalOrder() .addAll(ruleFinder.filterBuildRuleInputs(starter)) .addAll(components.getDeps(ruleFinder)) .add(modulesLinkTree) .addAll(nativeLibsLinktree) .addAll(pythonModulesLinktree) .addAll(ruleFinder.filterBuildRuleInputs(extraInputs)) .build(); } @Override public ImmutableCollection<SourcePath> getInputs() { return ImmutableSortedSet.<SourcePath>naturalOrder() .add(starter) .addAll(components.getInputs()) .addAll(extraInputs) .build(); } @Override public ImmutableList<String> getCommandPrefix(SourcePathResolver resolver) { return ImmutableList.of(resolver.getAbsolutePath(starter).toString()); } @Override public ImmutableMap<String, String> getEnvironment(SourcePathResolver resolver) { return ImmutableMap.of(); } @Override public void appendToRuleKey(RuleKeyObjectSink sink) { sink.setReflectively("starter", starter).setReflectively("components", components); } }; } private Tool getStandaloneBinary( BuildRuleParams params, BuildRuleResolver resolver, SourcePathRuleFinder ruleFinder, SourcePath starter, String mainModule, final LuaPackageComponents components) { Path output = getOutputPath(params.getBuildTarget(), params.getProjectFilesystem()); Tool lua = luaConfig.getLua(resolver); Tool packager = luaConfig.getPackager().resolve(resolver); LuaStandaloneBinary binary = resolver.addToIndex( new LuaStandaloneBinary( params .withAppendedFlavor(BINARY_FLAVOR) .copyReplacingDeclaredAndExtraDeps( Suppliers.ofInstance( ImmutableSortedSet.<BuildRule>naturalOrder() .addAll(ruleFinder.filterBuildRuleInputs(starter)) .addAll(components.getDeps(ruleFinder)) .addAll(lua.getDeps(ruleFinder)) .addAll(packager.getDeps(ruleFinder)) .build()), Suppliers.ofInstance(ImmutableSortedSet.of())), packager, ImmutableList.of(), output, Optional.of(starter), components, mainModule, lua, luaConfig.shouldCacheBinaries())); return new CommandTool.Builder() .addArg(SourcePathArg.of(binary.getSourcePathToOutput())) .build(); } private Tool getBinary( BuildRuleParams params, BuildRuleResolver resolver, SourcePathRuleFinder ruleFinder, CxxPlatform cxxPlatform, String mainModule, SourcePath starter, final LuaPackageComponents components, LuaConfig.PackageStyle packageStyle) { switch (packageStyle) { case STANDALONE: return getStandaloneBinary(params, resolver, ruleFinder, starter, mainModule, components); case INPLACE: return getInPlaceBinary(params, resolver, ruleFinder, cxxPlatform, starter, components); } throw new IllegalStateException( String.format("%s: unexpected package style %s", params.getBuildTarget(), packageStyle)); } @Override public BuildRule createBuildRule( TargetGraph targetGraph, BuildRuleParams params, final BuildRuleResolver resolver, CellPathResolver cellRoots, LuaBinaryDescriptionArg args) throws NoSuchBuildTargetException { SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver); SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder); CxxPlatform cxxPlatform = cxxPlatforms.getValue(params.getBuildTarget()).orElse(defaultCxxPlatform); PythonPlatform pythonPlatform = pythonPlatforms .getValue(params.getBuildTarget()) .orElse( pythonPlatforms.getValue( args.getPythonPlatform() .<Flavor>map(InternalFlavor::of) .orElse(pythonPlatforms.getFlavors().iterator().next()))); LuaBinaryPackageComponents components = getPackageComponentsFromDeps( params, resolver, pathResolver, ruleFinder, cxxPlatform, pythonPlatform, args.getNativeStarterLibrary() .map(Optional::of) .orElse(luaConfig.getNativeStarterLibrary()), args.getMainModule(), args.getPackageStyle().orElse(luaConfig.getPackageStyle()), params.getDeclaredDeps().get()); LuaConfig.PackageStyle packageStyle = args.getPackageStyle().orElse(luaConfig.getPackageStyle()); Tool binary = getBinary( params, resolver, ruleFinder, cxxPlatform, args.getMainModule(), components.getStarter(), components.getComponents(), packageStyle); return new LuaBinary( params.copyAppendingExtraDeps(binary.getDeps(ruleFinder)), ruleFinder, getOutputPath(params.getBuildTarget(), params.getProjectFilesystem()), binary, args.getMainModule(), components.getComponents(), luaConfig.getLua(resolver), packageStyle); } @Override public void findDepsForTargetFromConstructorArgs( BuildTarget buildTarget, CellPathResolver cellRoots, AbstractLuaBinaryDescriptionArg constructorArg, ImmutableCollection.Builder<BuildTarget> extraDepsBuilder, ImmutableCollection.Builder<BuildTarget> targetGraphOnlyDepsBuilder) { if (luaConfig.getPackageStyle() == LuaConfig.PackageStyle.STANDALONE) { extraDepsBuilder.addAll(luaConfig.getPackager().getParseTimeDeps()); } extraDepsBuilder.addAll(getNativeStarterDepTargets()); } @Override public boolean isVersionRoot(ImmutableSet<Flavor> flavors) { return true; } public enum StarterType { PURE, NATIVE, } @BuckStyleImmutable @Value.Immutable interface AbstractLuaBinaryDescriptionArg extends CommonDescriptionArg, HasDeclaredDeps { String getMainModule(); Optional<BuildTarget> getNativeStarterLibrary(); Optional<String> getPythonPlatform(); Optional<LuaConfig.PackageStyle> getPackageStyle(); } }