public void register(Description<?> description) { Preconditions.checkNotNull(description); BuildRuleType type = description.getBuildRuleType(); types.put(type.getName(), type); factories.put(type, new DescribedRuleFactory<>(description)); descriptions.put(type, description); }
@Test public void shouldConvertCamelCaseFieldNameToSnakeCaseParameter() { class Dto { public String someField; @Hint(name = "all_this_was_fields") public String hintedField; } String definition = buckPyFunction.toPythonFunction(BuildRuleType.of("case"), new Dto()); assertEquals( Joiner.on("\n") .join( "@provide_for_build", "def case(name, all_this_was_fields, some_field, visibility=[], build_env=None):", " add_rule({", " 'buck.type' : 'case',", " 'name' : name,", " 'hintedField' : all_this_was_fields,", " 'someField' : some_field,", " 'visibility' : visibility,", " }, build_env)", "", ""), definition); }
/** * Description for an apple_asset_catalog rule, which identifies an asset catalog for an iOS or Mac * OS X library or binary. */ public class AppleAssetCatalogDescription implements Description<AppleAssetCatalogDescription.Arg> { public static final BuildRuleType TYPE = BuildRuleType.of("apple_asset_catalog"); @Override public BuildRuleType getBuildRuleType() { return TYPE; } @Override public Arg createUnpopulatedConstructorArg() { return new Arg(); } @Override public <A extends Arg> NoopBuildRule createBuildRule( BuildRuleParams params, BuildRuleResolver resolver, A args) { return new NoopBuildRule(params, new SourcePathResolver(resolver)); } @SuppressFieldNotInitialized public static class Arg { public SortedSet<Path> dirs; public Optional<Boolean> copyToBundles; public boolean getCopyToBundles() { return copyToBundles.or(false); } } }
/** Description for an apple_resource rule which copies resource files to the built bundle. */ public class AppleResourceDescription implements Description<AppleResourceDescription.Arg>, Flavored { public static final BuildRuleType TYPE = BuildRuleType.of("apple_resource"); @Override public BuildRuleType getBuildRuleType() { return TYPE; } @Override public Arg createUnpopulatedConstructorArg() { return new Arg(); } @Override public <A extends Arg> BuildRule createBuildRule( TargetGraph targetGraph, BuildRuleParams params, BuildRuleResolver resolver, A args) { return new NoopBuildRule(params, new SourcePathResolver(resolver)); } @Override public boolean hasFlavors(ImmutableSet<Flavor> flavors) { return true; } @SuppressFieldNotInitialized public static class Arg extends AbstractDescriptionArg { public Set<SourcePath> dirs; public Set<SourcePath> files; public Optional<Set<SourcePath>> variants; } }
@Test public void optionalFieldsAreListedAfterMandatoryOnes() { class Either { // Alphabetical ordering is deliberate. public Optional<String> cat; public String dog; public Optional<String> egg; public String fake; } String definition = buckPyFunction.toPythonFunction(BuildRuleType.of("either"), new Either()); assertEquals( Joiner.on("\n") .join( "@provide_for_build", "def either(name, dog, fake, cat=None, egg=None, visibility=[], build_env=None):", " add_rule({", " 'buck.type' : 'either',", " 'name' : name,", " 'dog' : dog,", " 'fake' : fake,", " 'cat' : cat,", " 'egg' : egg,", " 'visibility' : visibility,", " }, build_env)", "", ""), definition); }
@Test(expected = HumanReadableException.class) public void visibilityOptionsMustNotBeSetAsTheyArePassedInBuildRuleParamsLater() { class Visible { public Set<BuildTargetPattern> visibility; } buckPyFunction.toPythonFunction(BuildRuleType.of("nope"), new Visible()); }
@Test(expected = HumanReadableException.class) public void theNameFieldMustBeAString() { class BadName { public int name; } buckPyFunction.toPythonFunction(BuildRuleType.of("nope"), new BadName()); }
@Test public void visibilityWillBeAddedIfMissing() { class NoVis { public String random; } String definition = buckPyFunction.toPythonFunction(BuildRuleType.of("bad"), new NoVis()); assertTrue(definition.contains("visibility=[]")); }
@Test public void nameWillBeAddedIfMissing() { class NoName { public String random; } String definition = buckPyFunction.toPythonFunction(BuildRuleType.of("bad"), new NoName()); assertTrue(definition.contains("name")); }
@Test public void optionalBooleanValuesShouldBeRepresentedByNone() { class Dto { public Optional<Boolean> field; } String definition = buckPyFunction.toPythonFunction(BuildRuleType.of("boolean"), new Dto()); assertTrue(definition, definition.contains(", field=None,")); }
@Test public void optionalFieldsAreGivenSensibleDefaultValues() { class LotsOfOptions { public Optional<String> thing; public Optional<List<BuildTarget>> targets; public Optional<Integer> version; } String definition = buckPyFunction.toPythonFunction(BuildRuleType.of("optional"), new LotsOfOptions()); assertTrue(definition, definition.contains("targets=[], thing=None, version=None")); }
/** * Description for a core_data_model rule, which identifies a model file for use with Apple's Core * Data. */ public class CoreDataModelDescription implements Description<CoreDataModelDescription.Arg>, Flavored { public static final BuildRuleType TYPE = BuildRuleType.of("core_data_model"); private static final String CORE_DATA_MODEL_EXTENSION = "xcdatamodel"; private static final String VERSIONED_CORE_DATA_MODEL_EXTENSION = "xcdatamodeld"; @Override public BuildRuleType getBuildRuleType() { return TYPE; } @Override public Arg createUnpopulatedConstructorArg() { return new Arg(); } @Override public <A extends Arg> CoreDataModel createBuildRule( TargetGraph targetGraph, BuildRuleParams params, BuildRuleResolver resolver, A args) { ProjectFilesystem projectFilesystem = params.getProjectFilesystem(); Supplier<ImmutableCollection<Path>> inputPathsSupplier = RuleUtils.subpathsOfPathsSupplier(projectFilesystem, ImmutableSet.of(args.path)); String extension = Files.getFileExtension(args.path.getFileName().toString()); Preconditions.checkArgument( CORE_DATA_MODEL_EXTENSION.equals(extension) || VERSIONED_CORE_DATA_MODEL_EXTENSION.equals(extension)); return new CoreDataModel( params, new SourcePathResolver(resolver), inputPathsSupplier, args.path); } public static boolean isVersionedDataModel(Arg arg) { return VERSIONED_CORE_DATA_MODEL_EXTENSION.equals( Files.getFileExtension(arg.path.getFileName().toString())); } @SuppressFieldNotInitialized public static class Arg extends AbstractDescriptionArg { public Path path; } @Override public boolean hasFlavors(ImmutableSet<Flavor> flavors) { return true; } }
public class GoBinaryDescription implements Description<GoBinaryDescription.Arg> { private static final BuildRuleType TYPE = BuildRuleType.of("go_binary"); private final GoBuckConfig goBuckConfig; private final CxxPlatform cxxPlatform; public GoBinaryDescription(GoBuckConfig goBuckConfig, CxxPlatform cxxPlatform) { this.goBuckConfig = goBuckConfig; this.cxxPlatform = cxxPlatform; } @Override public BuildRuleType getBuildRuleType() { return TYPE; } @Override public Arg createUnpopulatedConstructorArg() { return new Arg(); } @Override public <A extends Arg> BuildRule createBuildRule( TargetGraph targetGraph, BuildRuleParams params, BuildRuleResolver resolver, A args) { return GoDescriptors.createGoBinaryRule( params, resolver, goBuckConfig, cxxPlatform, args.srcs, args.compilerFlags.or(ImmutableList.<String>of()), args.linkerFlags.or(ImmutableList.<String>of())); } @SuppressFieldNotInitialized public static class Arg { public ImmutableSet<SourcePath> srcs; public Optional<List<String>> compilerFlags; public Optional<List<String>> linkerFlags; public Optional<ImmutableSortedSet<BuildTarget>> deps; } }
@Test public void shouldOnlyIncludeTheNameFieldOnce() { class Named { public String name; } String definition = buckPyFunction.toPythonFunction(BuildRuleType.of("named"), new Named()); assertEquals( Joiner.on("\n") .join( "@provide_for_build", "def named(name, visibility=[], build_env=None):", " add_rule({", " 'buck.type' : 'named',", " 'name' : name,", " 'visibility' : visibility,", " }, build_env)", "", ""), definition); }
@Test public void testHasDefaultName() { @TargetName(name = "lollerskates") class NoName { public String foobar; } String definition = buckPyFunction.toPythonFunction(BuildRuleType.of("noname"), new NoName()); assertEquals( Joiner.on("\n") .join( "@provide_for_build", "def noname(foobar, visibility=[], build_env=None):", " add_rule({", " 'buck.type' : 'noname',", " 'name' : 'lollerskates',", " 'foobar' : foobar,", " 'visibility' : visibility,", " }, build_env)", "", ""), definition); }
public class AndroidBuildConfigDescription implements Description<AndroidBuildConfigDescription.Arg> { public static final BuildRuleType TYPE = new BuildRuleType("android_build_config"); private static final BuildRuleType GEN_JAVA_TYPE = new BuildRuleType("gen_java_android_build_config"); private static final Flavor GEN_JAVA_FLAVOR = new Flavor(GEN_JAVA_TYPE.getName()); @Override public BuildRuleType getBuildRuleType() { return TYPE; } @Override public Arg createUnpopulatedConstructorArg() { return new Arg(); } @Override public <A extends Arg> BuildRule createBuildRule( BuildRuleParams params, BuildRuleResolver resolver, A args) { return createBuildRule( params, args.javaPackage, args.values.get(), args.valuesFile, /* useConstantExpressions */ false); } /** * @param values Collection whose entries identify fields for the generated {@code BuildConfig} * class. The values for fields can be overridden by values from the {@code valuesFile} file, * if present. * @param valuesFile Path to a file with values to override those in {@code values}. */ static AndroidBuildConfigJavaLibrary createBuildRule( BuildRuleParams params, String javaPackage, BuildConfigFields values, Optional<SourcePath> valuesFile, boolean useConstantExpressions) { // Create one build rule to generate BuildConfig.java. BuildRuleParams buildConfigParams = params.copyWithChanges( GEN_JAVA_TYPE, BuildTarget.builder(params.getBuildTarget()).setFlavor(GEN_JAVA_FLAVOR).build(), params.getDeclaredDeps(), params.getExtraDeps()); AndroidBuildConfig androidBuildConfig = new AndroidBuildConfig( buildConfigParams, javaPackage, values, valuesFile, useConstantExpressions); // Create a second build rule to compile BuildConfig.java and expose it as a JavaLibrary. BuildRuleParams javaLibraryParams = params.copyWithChanges( TYPE, params.getBuildTarget(), /* declaredDeps */ ImmutableSortedSet.<BuildRule>of(androidBuildConfig), /* extraDeps */ ImmutableSortedSet.<BuildRule>of()); return new AndroidBuildConfigJavaLibrary(javaLibraryParams, androidBuildConfig); } public static class Arg implements ConstructorArg { @Hint(name = "package") public String javaPackage; /** This will never be absent after this Arg is populated. */ public Optional<BuildConfigFields> values; /** If present, contents of file can override those of {@link #values}. */ public Optional<SourcePath> valuesFile; } }
public class GoBinaryDescription implements Description<GoBinaryDescription.Arg> { private static final BuildRuleType TYPE = BuildRuleType.of("go_binary"); private final GoBuckConfig goBuckConfig; private final CxxPlatform cxxPlatform; public GoBinaryDescription(GoBuckConfig goBuckConfig, CxxPlatform cxxPlatform) { this.goBuckConfig = goBuckConfig; this.cxxPlatform = cxxPlatform; } @Override public BuildRuleType getBuildRuleType() { return TYPE; } @Override public Arg createUnpopulatedConstructorArg() { return new Arg(); } @Override public <A extends Arg> BuildRule createBuildRule( TargetGraph targetGraph, BuildRuleParams params, BuildRuleResolver resolver, A args) { BuildTarget libraryTarget = BuildTarget.builder(params.getBuildTarget()) .addFlavors(ImmutableFlavor.of("compile")) .build(); GoLibrary library = GoDescriptors.createGoLibraryRule( params.copyWithBuildTarget(libraryTarget), resolver, goBuckConfig, Paths.get("main"), args.srcs, args.compilerFlags.or(ImmutableList.<String>of())); resolver.addToIndex(library); BuildRuleParams binaryParams = params.copyWithDeps( Suppliers.ofInstance(ImmutableSortedSet.<BuildRule>of(library)), Suppliers.ofInstance(ImmutableSortedSet.<BuildRule>of())); GoSymlinkTree symlinkTree = GoDescriptors.requireTransitiveSymlinkTreeRule(binaryParams, resolver); return new GoBinary( params.copyWithDeps( Suppliers.ofInstance(ImmutableSortedSet.<BuildRule>of(symlinkTree, library)), Suppliers.ofInstance(ImmutableSortedSet.<BuildRule>of())), new SourcePathResolver(resolver), cxxPlatform.getLd(), symlinkTree, library, goBuckConfig.getGoLinker().get(), ImmutableList.<String>builder() .addAll(goBuckConfig.getLinkerFlags()) .addAll(args.linkerFlags.or(ImmutableList.<String>of())) .build()); } @SuppressFieldNotInitialized public static class Arg { public ImmutableSet<SourcePath> srcs; public Optional<List<String>> compilerFlags; public Optional<List<String>> linkerFlags; public Optional<ImmutableSortedSet<BuildTarget>> deps; } }
public class JavaTestDescription implements Description<JavaTestDescription.Arg>, ImplicitDepsInferringDescription<JavaTestDescription.Arg> { public static final BuildRuleType TYPE = BuildRuleType.of("java_test"); private final JavaOptions javaOptions; private final JavacOptions templateJavacOptions; private final Optional<Long> defaultTestRuleTimeoutMs; private final CxxPlatform cxxPlatform; public JavaTestDescription( JavaOptions javaOptions, JavacOptions templateOptions, Optional<Long> defaultTestRuleTimeoutMs, CxxPlatform cxxPlatform) { this.javaOptions = javaOptions; this.templateJavacOptions = templateOptions; this.defaultTestRuleTimeoutMs = defaultTestRuleTimeoutMs; this.cxxPlatform = cxxPlatform; } @Override public BuildRuleType getBuildRuleType() { return TYPE; } @Override public Arg createUnpopulatedConstructorArg() { return new Arg(); } @Override public <A extends Arg> JavaTest createBuildRule( TargetGraph targetGraph, BuildRuleParams params, BuildRuleResolver resolver, A args) throws NoSuchBuildTargetException { SourcePathResolver pathResolver = new SourcePathResolver(resolver); JavacOptions javacOptions = JavacOptionsFactory.create(templateJavacOptions, params, resolver, pathResolver, args); CxxLibraryEnhancement cxxLibraryEnhancement = new CxxLibraryEnhancement( params, args.useCxxLibraries, resolver, pathResolver, cxxPlatform); params = cxxLibraryEnhancement.updatedParams; BuildTarget abiJarTarget = params.getBuildTarget().withAppendedFlavors(CalculateAbi.FLAVOR); JavaTest test = resolver.addToIndex( new JavaTest( params.appendExtraDeps( Iterables.concat( BuildRules.getExportedRules( Iterables.concat( params.getDeclaredDeps().get(), resolver.getAllRules(args.providedDeps.get()))), pathResolver.filterBuildRuleInputs(javacOptions.getInputs(pathResolver)))), pathResolver, args.srcs.get(), ResourceValidator.validateResources( pathResolver, params.getProjectFilesystem(), args.resources.get()), javacOptions.getGeneratedSourceFolderName(), args.labels.get(), args.contacts.get(), args.proguardConfig.transform( SourcePaths.toSourcePath(params.getProjectFilesystem())), new BuildTargetSourcePath(abiJarTarget), javacOptions.trackClassUsage(), /* additionalClasspathEntries */ ImmutableSet.<Path>of(), args.testType.or(TestType.JUNIT), new JavacToJarStepFactory(javacOptions, JavacOptionsAmender.IDENTITY), javaOptions.getJavaRuntimeLauncher(), args.vmArgs.get(), cxxLibraryEnhancement.nativeLibsEnvironment, validateAndGetSourcesUnderTest( args.sourceUnderTest.get(), params.getBuildTarget(), resolver), args.resourcesRoot, args.mavenCoords, args.testRuleTimeoutMs.or(defaultTestRuleTimeoutMs), args.env.get(), args.getRunTestSeparately(), args.stdOutLogLevel, args.stdErrLogLevel)); resolver.addToIndex( CalculateAbi.of( abiJarTarget, pathResolver, params, new BuildTargetSourcePath(test.getBuildTarget()))); return test; } public static ImmutableSet<BuildRule> validateAndGetSourcesUnderTest( ImmutableSet<BuildTarget> sourceUnderTestTargets, BuildTarget owner, BuildRuleResolver resolver) { ImmutableSet.Builder<BuildRule> sourceUnderTest = ImmutableSet.builder(); for (BuildTarget target : sourceUnderTestTargets) { BuildRule rule = resolver.getRule(target); if (!(rule instanceof JavaLibrary)) { // In this case, the source under test specified in the build file was not a Java library // rule. Since EMMA requires the sources to be in Java, we will throw this exception and // not continue with the tests. throw new HumanReadableException( "Specified source under test for %s is not a Java library: %s (%s).", owner, rule.getFullyQualifiedName(), rule.getType()); } sourceUnderTest.add(rule); } return sourceUnderTest.build(); } @Override public Iterable<BuildTarget> findDepsForTargetFromConstructorArgs( BuildTarget buildTarget, CellPathResolver cellRoots, Arg constructorArg) { ImmutableSet.Builder<BuildTarget> deps = ImmutableSet.builder(); if (constructorArg.useCxxLibraries.or(false)) { deps.addAll(CxxPlatforms.getParseTimeDeps(cxxPlatform)); } return deps.build(); } @SuppressFieldNotInitialized public static class Arg extends JavaLibraryDescription.Arg implements HasSourceUnderTest { public Optional<ImmutableSortedSet<String>> contacts; public Optional<ImmutableSortedSet<Label>> labels; @Hint(isDep = false) public Optional<ImmutableSortedSet<BuildTarget>> sourceUnderTest; public Optional<ImmutableList<String>> vmArgs; public Optional<TestType> testType; public Optional<Boolean> runTestSeparately; public Optional<Level> stdErrLogLevel; public Optional<Level> stdOutLogLevel; public Optional<Boolean> useCxxLibraries; public Optional<Long> testRuleTimeoutMs; public Optional<ImmutableMap<String, String>> env; @Override public ImmutableSortedSet<BuildTarget> getSourceUnderTest() { return sourceUnderTest.get(); } public boolean getRunTestSeparately() { return runTestSeparately.or(false); } } public static class CxxLibraryEnhancement { public final BuildRuleParams updatedParams; public final ImmutableMap<String, String> nativeLibsEnvironment; public CxxLibraryEnhancement( BuildRuleParams params, Optional<Boolean> useCxxLibraries, BuildRuleResolver resolver, SourcePathResolver pathResolver, CxxPlatform cxxPlatform) throws NoSuchBuildTargetException { if (useCxxLibraries.or(false)) { SymlinkTree nativeLibsSymlinkTree = buildNativeLibsSymlinkTreeRule(params, pathResolver, cxxPlatform); updatedParams = params.appendExtraDeps( ImmutableList.<BuildRule>builder() .add(nativeLibsSymlinkTree) // Add all the native libraries as first-order dependencies. // This has two effects: // (1) They become runtime deps because JavaTest adds all first-order deps. // (2) They affect the JavaTest's RuleKey, so changing them will invalidate // the test results cache. .addAll( pathResolver.filterBuildRuleInputs( nativeLibsSymlinkTree.getLinks().values())) .build()); nativeLibsEnvironment = ImmutableMap.of( cxxPlatform.getLd().resolve(resolver).searchPathEnvVar(), nativeLibsSymlinkTree.getRoot().toString()); } else { updatedParams = params; nativeLibsEnvironment = ImmutableMap.of(); } } public static SymlinkTree buildNativeLibsSymlinkTreeRule( BuildRuleParams buildRuleParams, SourcePathResolver pathResolver, CxxPlatform cxxPlatform) throws NoSuchBuildTargetException { return CxxDescriptionEnhancer.createSharedLibrarySymlinkTree( buildRuleParams, pathResolver, cxxPlatform, buildRuleParams.getDeps(), Predicates.or( Predicates.instanceOf(NativeLinkable.class), Predicates.instanceOf(JavaLibrary.class))); } } }
public class AndroidInstrumentationApkDescription implements Description<AndroidInstrumentationApkDescription.Arg> { public static final BuildRuleType TYPE = BuildRuleType.of("android_instrumentation_apk"); private final ProGuardConfig proGuardConfig; private final JavacOptions javacOptions; private final ImmutableMap<NdkCxxPlatforms.TargetCpuType, NdkCxxPlatform> nativePlatforms; private final ListeningExecutorService dxExecutorService; public AndroidInstrumentationApkDescription( ProGuardConfig proGuardConfig, JavacOptions androidJavacOptions, ImmutableMap<NdkCxxPlatforms.TargetCpuType, NdkCxxPlatform> nativePlatforms, ListeningExecutorService dxExecutorService) { this.proGuardConfig = proGuardConfig; this.javacOptions = androidJavacOptions; this.nativePlatforms = nativePlatforms; this.dxExecutorService = dxExecutorService; } @Override public BuildRuleType getBuildRuleType() { return TYPE; } @Override public Arg createUnpopulatedConstructorArg() { return new Arg(); } @Override public <A extends Arg> BuildRule createBuildRule( TargetGraph targetGraph, BuildRuleParams params, BuildRuleResolver resolver, A args) { BuildRule installableApk = resolver.getRule(args.apk); if (!(installableApk instanceof InstallableApk)) { throw new HumanReadableException( "In %s, apk='%s' must be an android_binary() or apk_genrule() but was %s().", params.getBuildTarget(), installableApk.getFullyQualifiedName(), installableApk.getType()); } AndroidBinary apkUnderTest = getUnderlyingApk((InstallableApk) installableApk); ImmutableSortedSet<JavaLibrary> rulesToExcludeFromDex = FluentIterable.from( ImmutableSet.<JavaLibrary>builder() .addAll(apkUnderTest.getRulesToExcludeFromDex()) .addAll( Classpaths.getClasspathEntries(apkUnderTest.getClasspathDeps()).keySet()) .build()) .toSortedSet(HasBuildTarget.BUILD_TARGET_COMPARATOR); // TODO(natthu): Instrumentation APKs should also exclude native libraries and assets from the // apk under test. AndroidPackageableCollection.ResourceDetails resourceDetails = apkUnderTest.getAndroidPackageableCollection().getResourceDetails(); ImmutableSet<BuildTarget> resourcesToExclude = ImmutableSet.copyOf( Iterables.concat( resourceDetails.getResourcesWithNonEmptyResDir(), resourceDetails.getResourcesWithEmptyResButNonEmptyAssetsDir())); Path primaryDexPath = AndroidBinary.getPrimaryDexPath(params.getBuildTarget()); AndroidBinaryGraphEnhancer graphEnhancer = new AndroidBinaryGraphEnhancer( targetGraph, params, resolver, ResourceCompressionMode.DISABLED, FilterResourcesStep.ResourceFilter.EMPTY_FILTER, /* resourceUnionPackage */ Optional.<String>absent(), /* locales */ ImmutableSet.<String>of(), args.manifest, PackageType.INSTRUMENTED, apkUnderTest.getCpuFilters(), /* shouldBuildStringSourceMap */ false, /* shouldPreDex */ false, primaryDexPath, DexSplitMode.NO_SPLIT, FluentIterable.from(rulesToExcludeFromDex).transform(TO_TARGET).toSet(), resourcesToExclude, /* skipCrunchPngs */ false, javacOptions, EnumSet.noneOf(ExopackageMode.class), apkUnderTest.getKeystore(), /* buildConfigValues */ BuildConfigFields.empty(), /* buildConfigValuesFile */ Optional.<SourcePath>absent(), /* xzCompressionLevel */ Optional.<Integer>absent(), nativePlatforms, dxExecutorService); AndroidGraphEnhancementResult enhancementResult = graphEnhancer.createAdditionalBuildables(); return new AndroidInstrumentationApk( params .copyWithExtraDeps(Suppliers.ofInstance(enhancementResult.getFinalDeps())) .appendExtraDeps(rulesToExcludeFromDex), new SourcePathResolver(resolver), proGuardConfig.getProguardJarOverride(), proGuardConfig.getProguardMaxHeapSize(), apkUnderTest, rulesToExcludeFromDex, enhancementResult, dxExecutorService); } private static AndroidBinary getUnderlyingApk(InstallableApk installable) { if (installable instanceof AndroidBinary) { return (AndroidBinary) installable; } else if (installable instanceof ApkGenrule) { return getUnderlyingApk(((ApkGenrule) installable).getInstallableApk()); } else { throw new IllegalStateException( installable.getBuildTarget().getFullyQualifiedName() + " must be backed by either an android_binary() or an apk_genrule()"); } } @SuppressFieldNotInitialized public static class Arg { public SourcePath manifest; public BuildTarget apk; public Optional<ImmutableSortedSet<BuildTarget>> deps; } }
public class CxxPythonExtensionDescription implements Description<CxxPythonExtensionDescription.Arg>, ImplicitDepsInferringDescription<CxxPythonExtensionDescription.Arg> { private enum Type { EXTENSION, } private static final FlavorDomain<Type> LIBRARY_TYPE = new FlavorDomain<>( "C/C++ Library Type", ImmutableMap.of(CxxDescriptionEnhancer.SHARED_FLAVOR, Type.EXTENSION)); public static final BuildRuleType TYPE = BuildRuleType.of("cxx_python_extension"); private final CxxBuckConfig cxxBuckConfig; private final FlavorDomain<CxxPlatform> cxxPlatforms; public CxxPythonExtensionDescription( CxxBuckConfig cxxBuckConfig, FlavorDomain<CxxPlatform> cxxPlatforms) { this.cxxBuckConfig = cxxBuckConfig; this.cxxPlatforms = cxxPlatforms; } @Override public Arg createUnpopulatedConstructorArg() { return new Arg(); } @VisibleForTesting protected BuildTarget getExtensionTarget(BuildTarget target, Flavor platform) { return CxxDescriptionEnhancer.createSharedLibraryBuildTarget(target, platform); } @VisibleForTesting protected String getExtensionName(BuildTarget target) { // .so is used on OS X too (as opposed to dylib). return String.format("%s.so", target.getShortName()); } @VisibleForTesting protected Path getExtensionPath(BuildTarget target, Flavor platform) { return BuildTargets.getGenPath(getExtensionTarget(target, platform), "%s") .resolve(getExtensionName(target)); } private <A extends Arg> BuildRule createExtensionBuildRule( TargetGraph targetGraph, BuildRuleParams params, BuildRuleResolver ruleResolver, CxxPlatform cxxPlatform, A args) { SourcePathResolver pathResolver = new SourcePathResolver(ruleResolver); // Extract all C/C++ sources from the constructor arg. ImmutableMap<String, CxxSource> srcs = CxxDescriptionEnhancer.parseCxxSources(params, ruleResolver, cxxPlatform, args); ImmutableMap<Path, SourcePath> headers = CxxDescriptionEnhancer.parseHeaders(params, ruleResolver, cxxPlatform, args); ImmutableMap<String, SourcePath> lexSrcs = CxxDescriptionEnhancer.parseLexSources(params, ruleResolver, args); ImmutableMap<String, SourcePath> yaccSrcs = CxxDescriptionEnhancer.parseYaccSources(params, ruleResolver, args); CxxHeaderSourceSpec lexYaccSources = CxxDescriptionEnhancer.createLexYaccBuildRules( params, ruleResolver, cxxPlatform, ImmutableList.<String>of(), lexSrcs, ImmutableList.<String>of(), yaccSrcs); // Setup the header symlink tree and combine all the preprocessor input from this rule // and all dependencies. HeaderSymlinkTree headerSymlinkTree = CxxDescriptionEnhancer.requireHeaderSymlinkTree( params, ruleResolver, new SourcePathResolver(ruleResolver), cxxPlatform, /* includeLexYaccHeaders */ true, lexSrcs, yaccSrcs, headers, HeaderVisibility.PRIVATE); ImmutableList<CxxPreprocessorInput> cxxPreprocessorInput = CxxDescriptionEnhancer.collectCxxPreprocessorInput( targetGraph, params, cxxPlatform, CxxFlags.getLanguageFlags( args.preprocessorFlags, args.platformPreprocessorFlags, args.langPreprocessorFlags, cxxPlatform), args.prefixHeaders.get(), ImmutableList.of(headerSymlinkTree), ImmutableSet.<Path>of(), CxxPreprocessables.getTransitiveCxxPreprocessorInput( targetGraph, cxxPlatform, params.getDeps())); ImmutableMap<String, CxxSource> allSources = ImmutableMap.<String, CxxSource>builder() .putAll(srcs) .putAll(lexYaccSources.getCxxSources()) .build(); // Generate rule to build the object files. ImmutableMap<CxxPreprocessAndCompile, SourcePath> picObjects = CxxSourceRuleFactory.requirePreprocessAndCompileRules( params, ruleResolver, pathResolver, cxxPlatform, cxxPreprocessorInput, CxxFlags.getFlags(args.compilerFlags, args.platformCompilerFlags, cxxPlatform), cxxBuckConfig.getPreprocessMode(), allSources, CxxSourceRuleFactory.PicType.PIC); // Setup the rules to link the shared library. String extensionName = getExtensionName(params.getBuildTarget()); Path extensionPath = getExtensionPath(params.getBuildTarget(), cxxPlatform.getFlavor()); return CxxLinkableEnhancer.createCxxLinkableBuildRule( targetGraph, cxxPlatform, params, pathResolver, /* extraLdFlags */ CxxFlags.getFlags( args.linkerFlags, args.platformLinkerFlags, cxxPlatform), getExtensionTarget(params.getBuildTarget(), cxxPlatform.getFlavor()), Linker.LinkType.SHARED, Optional.of(extensionName), extensionPath, picObjects.values(), Linker.LinkableDepType.SHARED, params.getDeps(), args.cxxRuntimeType, Optional.<SourcePath>absent()); } @Override public <A extends Arg> BuildRule createBuildRule( TargetGraph targetGraph, BuildRuleParams params, BuildRuleResolver ruleResolver, A args) { // See if we're building a particular "type" of this library, and if so, extract // it as an enum. Optional<Map.Entry<Flavor, Type>> type; Optional<Map.Entry<Flavor, CxxPlatform>> platform; try { type = LIBRARY_TYPE.getFlavorAndValue(ImmutableSet.copyOf(params.getBuildTarget().getFlavors())); platform = cxxPlatforms.getFlavorAndValue(ImmutableSet.copyOf(params.getBuildTarget().getFlavors())); } catch (FlavorDomainException e) { throw new HumanReadableException("%s: %s", params.getBuildTarget(), e.getMessage()); } // If we *are* building a specific type of this lib, call into the type specific // rule builder methods. Currently, we only support building a shared lib from the // pre-existing static lib, which we do here. if (type.isPresent()) { Preconditions.checkState(type.get().getValue() == Type.EXTENSION); Preconditions.checkState(platform.isPresent()); return createExtensionBuildRule( targetGraph, params, ruleResolver, platform.get().getValue(), args); } // Otherwise, we return the generic placeholder of this library, that dependents can use // get the real build rules via querying the action graph. SourcePathResolver pathResolver = new SourcePathResolver(ruleResolver); Path baseModule = PythonUtil.getBasePath(params.getBuildTarget(), args.baseModule); return new CxxPythonExtension( params, ruleResolver, pathResolver, baseModule.resolve(getExtensionName(params.getBuildTarget()))); } @Override public BuildRuleType getBuildRuleType() { return TYPE; } @Override public Iterable<BuildTarget> findDepsForTargetFromConstructorArgs( BuildTarget buildTarget, Arg constructorArg) { ImmutableSet.Builder<BuildTarget> deps = ImmutableSet.builder(); deps.add(cxxBuckConfig.getPythonDep()); if (constructorArg.lexSrcs.isPresent() && !constructorArg.lexSrcs.get().isEmpty()) { deps.add(cxxBuckConfig.getLexDep()); } return deps.build(); } @SuppressFieldNotInitialized public static class Arg extends CxxConstructorArg { public Optional<String> baseModule; } }
/** * Description for a {@link BuildRule} that generates an {@code .aar} file. * * <p>This represents an Android Library Project packaged as an {@code .aar} bundle as specified by: * <a> http://tools.android.com/tech-docs/new-build-system/aar-format</a>. * * <p>Note that the {@code aar} may be specified as a {@link SourcePath}, so it could be either a * binary {@code .aar} file checked into version control, or a zip file that conforms to the {@code * .aar} specification that is generated by another build rule. */ public class AndroidAarDescription implements Description<AndroidAarDescription.Arg> { public static final BuildRuleType TYPE = BuildRuleType.of("android_aar"); private static final Flavor AAR_ANDROID_MANIFEST_FLAVOR = ImmutableFlavor.of("aar_android_manifest"); private static final Flavor AAR_ASSEMBLE_RESOURCE_FLAVOR = ImmutableFlavor.of("aar_assemble_resource"); private static final Flavor AAR_ASSEMBLE_ASSETS_FLAVOR = ImmutableFlavor.of("aar_assemble_assets"); private static final Flavor AAR_ANDROID_RESOURCE_FLAVOR = ImmutableFlavor.of("aar_android_resource"); private final AndroidManifestDescription androidManifestDescription; private final ImmutableMap<NdkCxxPlatforms.TargetCpuType, NdkCxxPlatform> nativePlatforms; public AndroidAarDescription( AndroidManifestDescription androidManifestDescription, ImmutableMap<NdkCxxPlatforms.TargetCpuType, NdkCxxPlatform> nativePlatforms) { this.androidManifestDescription = androidManifestDescription; this.nativePlatforms = nativePlatforms; } @Override public BuildRuleType getBuildRuleType() { return TYPE; } @Override public Arg createUnpopulatedConstructorArg() { return new Arg(); } @Override public <A extends Arg> BuildRule createBuildRule( TargetGraph targetGraph, BuildRuleParams originalBuildRuleParams, BuildRuleResolver resolver, A args) { UnflavoredBuildTarget originalBuildTarget = originalBuildRuleParams.getBuildTarget().checkUnflavored(); SourcePathResolver pathResolver = new SourcePathResolver(resolver); ImmutableList.Builder<BuildRule> aarExtraDepsBuilder = ImmutableList.<BuildRule>builder().addAll(originalBuildRuleParams.getExtraDeps().get()); /* android_manifest */ AndroidManifestDescription.Arg androidManifestArgs = androidManifestDescription.createUnpopulatedConstructorArg(); androidManifestArgs.skeleton = args.manifestSkeleton; androidManifestArgs.deps = args.deps; BuildRuleParams androidManifestParams = originalBuildRuleParams.copyWithChanges( BuildTargets.createFlavoredBuildTarget( originalBuildTarget, AAR_ANDROID_MANIFEST_FLAVOR), originalBuildRuleParams.getDeclaredDeps(), originalBuildRuleParams.getExtraDeps()); AndroidManifest manifest = androidManifestDescription.createBuildRule( targetGraph, androidManifestParams, resolver, androidManifestArgs); aarExtraDepsBuilder.add(resolver.addToIndex(manifest)); /* assemble dirs */ AndroidPackageableCollector collector = new AndroidPackageableCollector( originalBuildRuleParams.getBuildTarget(), /* buildTargetsToExcludeFromDex */ ImmutableSet.<BuildTarget>of(), /* resourcesToExclude */ ImmutableSet.<BuildTarget>of()); collector.addPackageables( AndroidPackageableCollector.getPackageableRules(originalBuildRuleParams.getDeps())); AndroidPackageableCollection packageableCollection = collector.build(); ImmutableSortedSet<BuildRule> androidResourceDeclaredDeps = AndroidResourceHelper.androidResOnly(originalBuildRuleParams.getDeclaredDeps().get()); ImmutableSortedSet<BuildRule> androidResourceExtraDeps = AndroidResourceHelper.androidResOnly(originalBuildRuleParams.getExtraDeps().get()); BuildRuleParams assembleAssetsParams = originalBuildRuleParams.copyWithChanges( BuildTargets.createFlavoredBuildTarget(originalBuildTarget, AAR_ASSEMBLE_ASSETS_FLAVOR), Suppliers.ofInstance(androidResourceDeclaredDeps), Suppliers.ofInstance(androidResourceExtraDeps)); ImmutableCollection<SourcePath> assetsDirectories = packageableCollection.getAssetsDirectories(); AssembleDirectories assembleAssetsDirectories = new AssembleDirectories(assembleAssetsParams, pathResolver, assetsDirectories); aarExtraDepsBuilder.add(resolver.addToIndex(assembleAssetsDirectories)); BuildRuleParams assembleResourceParams = originalBuildRuleParams.copyWithChanges( BuildTargets.createFlavoredBuildTarget( originalBuildTarget, AAR_ASSEMBLE_RESOURCE_FLAVOR), Suppliers.ofInstance(androidResourceDeclaredDeps), Suppliers.ofInstance(androidResourceExtraDeps)); ImmutableCollection<SourcePath> resDirectories = packageableCollection.getResourceDetails().getResourceDirectories(); MergeAndroidResourceSources assembleResourceDirectories = new MergeAndroidResourceSources(assembleResourceParams, pathResolver, resDirectories); aarExtraDepsBuilder.add(resolver.addToIndex(assembleResourceDirectories)); /* android_resource */ BuildRuleParams androidResourceParams = originalBuildRuleParams.copyWithChanges( BuildTargets.createFlavoredBuildTarget( originalBuildTarget, AAR_ANDROID_RESOURCE_FLAVOR), Suppliers.ofInstance( ImmutableSortedSet.<BuildRule>of( manifest, assembleAssetsDirectories, assembleResourceDirectories)), Suppliers.ofInstance(ImmutableSortedSet.<BuildRule>of())); AndroidResource androidResource = new AndroidResource( androidResourceParams, pathResolver, /* deps */ ImmutableSortedSet.<BuildRule>naturalOrder() .add(assembleAssetsDirectories) .add(assembleResourceDirectories) .addAll(originalBuildRuleParams.getDeclaredDeps().get()) .build(), new BuildTargetSourcePath(assembleResourceDirectories.getBuildTarget()), /* resSrcs */ ImmutableSortedSet.<SourcePath>of(), Optional.<SourcePath>absent(), /* rDotJavaPackage */ null, new BuildTargetSourcePath(assembleAssetsDirectories.getBuildTarget()), /* assetsSrcs */ ImmutableSortedSet.<SourcePath>of(), Optional.<SourcePath>absent(), new BuildTargetSourcePath(manifest.getBuildTarget()), /* hasWhitelistedStrings */ false); aarExtraDepsBuilder.add(resolver.addToIndex(androidResource)); /* native_libraries */ AndroidNativeLibsPackageableGraphEnhancer packageableGraphEnhancer = new AndroidNativeLibsPackageableGraphEnhancer( resolver, originalBuildRuleParams, nativePlatforms, ImmutableSet.<NdkCxxPlatforms.TargetCpuType>of()); Optional<CopyNativeLibraries> nativeLibrariesOptional = packageableGraphEnhancer.getCopyNativeLibraries(targetGraph, packageableCollection); if (nativeLibrariesOptional.isPresent()) { aarExtraDepsBuilder.add(resolver.addToIndex(nativeLibrariesOptional.get())); } Optional<Path> assembledNativeLibsDir = nativeLibrariesOptional.transform( new Function<CopyNativeLibraries, Path>() { @Override public Path apply(CopyNativeLibraries input) { return input.getPathToNativeLibsDir(); } }); BuildRuleParams androidAarParams = originalBuildRuleParams.copyWithExtraDeps( Suppliers.ofInstance(ImmutableSortedSet.copyOf(aarExtraDepsBuilder.build()))); return new AndroidAar( androidAarParams, pathResolver, manifest, androidResource, assembleResourceDirectories.getPathToOutput(), assembleAssetsDirectories.getPathToOutput(), assembledNativeLibsDir, packageableCollection.getNativeLibAssetsDirectories()); } @SuppressFieldNotInitialized public static class Arg extends AndroidLibraryDescription.Arg { public SourcePath manifestSkeleton; } }
public class CxxPythonExtensionDescription implements Description<CxxPythonExtensionDescription.Arg>, ImplicitDepsInferringDescription<CxxPythonExtensionDescription.Arg> { private enum Type { EXTENSION, } private static final FlavorDomain<Type> LIBRARY_TYPE = new FlavorDomain<>( "C/C++ Library Type", ImmutableMap.of(CxxDescriptionEnhancer.SHARED_FLAVOR, Type.EXTENSION)); public static final BuildRuleType TYPE = BuildRuleType.of("cxx_python_extension"); private final FlavorDomain<PythonPlatform> pythonPlatforms; private final CxxBuckConfig cxxBuckConfig; private final FlavorDomain<CxxPlatform> cxxPlatforms; public CxxPythonExtensionDescription( FlavorDomain<PythonPlatform> pythonPlatforms, CxxBuckConfig cxxBuckConfig, FlavorDomain<CxxPlatform> cxxPlatforms) { this.pythonPlatforms = pythonPlatforms; this.cxxBuckConfig = cxxBuckConfig; this.cxxPlatforms = cxxPlatforms; } @Override public Arg createUnpopulatedConstructorArg() { return new Arg(); } @VisibleForTesting protected static BuildTarget getExtensionTarget( BuildTarget target, Flavor pythonPlatform, Flavor platform) { return CxxDescriptionEnhancer.createSharedLibraryBuildTarget( BuildTarget.builder(target).addFlavors(pythonPlatform).build(), platform); } @VisibleForTesting protected static String getExtensionName(BuildTarget target) { // .so is used on OS X too (as opposed to dylib). return String.format("%s.so", target.getShortName()); } @VisibleForTesting protected Path getExtensionPath( ProjectFilesystem filesystem, BuildTarget target, Flavor pythonPlatform, Flavor platform) { return BuildTargets.getGenPath( filesystem, getExtensionTarget(target, pythonPlatform, platform), "%s") .resolve(getExtensionName(target)); } private ImmutableList<com.facebook.buck.rules.args.Arg> getExtensionArgs( BuildRuleParams params, BuildRuleResolver ruleResolver, SourcePathResolver pathResolver, CxxPlatform cxxPlatform, Arg args) throws NoSuchBuildTargetException { // Extract all C/C++ sources from the constructor arg. ImmutableMap<String, CxxSource> srcs = CxxDescriptionEnhancer.parseCxxSources( params.getBuildTarget(), pathResolver, cxxPlatform, args); ImmutableMap<Path, SourcePath> headers = CxxDescriptionEnhancer.parseHeaders( params.getBuildTarget(), pathResolver, Optional.of(cxxPlatform), args); // Setup the header symlink tree and combine all the preprocessor input from this rule // and all dependencies. HeaderSymlinkTree headerSymlinkTree = CxxDescriptionEnhancer.requireHeaderSymlinkTree( params, ruleResolver, new SourcePathResolver(ruleResolver), cxxPlatform, headers, HeaderVisibility.PRIVATE); ImmutableList<CxxPreprocessorInput> cxxPreprocessorInput = CxxDescriptionEnhancer.collectCxxPreprocessorInput( params, cxxPlatform, CxxFlags.getLanguageFlags( args.preprocessorFlags, args.platformPreprocessorFlags, args.langPreprocessorFlags, cxxPlatform), ImmutableList.of(headerSymlinkTree), ImmutableSet.<FrameworkPath>of(), CxxPreprocessables.getTransitiveCxxPreprocessorInput(cxxPlatform, params.getDeps())); // Generate rule to build the object files. ImmutableMap<CxxPreprocessAndCompile, SourcePath> picObjects = CxxSourceRuleFactory.requirePreprocessAndCompileRules( params, ruleResolver, pathResolver, cxxBuckConfig, cxxPlatform, cxxPreprocessorInput, CxxFlags.getLanguageFlags( args.compilerFlags, args.platformCompilerFlags, args.langCompilerFlags, cxxPlatform), args.prefixHeader, cxxBuckConfig.getPreprocessMode(), srcs, CxxSourceRuleFactory.PicType.PIC); ImmutableList.Builder<com.facebook.buck.rules.args.Arg> argsBuilder = ImmutableList.builder(); argsBuilder.addAll( StringArg.from(CxxFlags.getFlags(args.linkerFlags, args.platformLinkerFlags, cxxPlatform))); // Embed a origin-relative library path into the binary so it can find the shared libraries. argsBuilder.addAll( StringArg.from( Linkers.iXlinker( "-rpath", String.format("%s/", cxxPlatform.getLd().resolve(ruleResolver).libOrigin())))); // Add object files into the args. argsBuilder.addAll(SourcePathArg.from(pathResolver, picObjects.values())); return argsBuilder.build(); } private ImmutableList<BuildRule> getPlatformDeps( BuildRuleParams params, BuildRuleResolver ruleResolver, PythonPlatform pythonPlatform, Arg args) { ImmutableList.Builder<BuildRule> rules = ImmutableList.builder(); // Add declared deps. rules.addAll(params.getDeclaredDeps().get()); // Add platform specific deps. rules.addAll( ruleResolver.getAllRules( Iterables.concat( args.platformDeps .or(PatternMatchedCollection.<ImmutableSortedSet<BuildTarget>>of()) .getMatchingValues(pythonPlatform.getFlavor().toString())))); // Add a dep on the python C/C++ library. rules.add(ruleResolver.getRule(pythonPlatform.getCxxLibrary().get().getBuildTarget())); return rules.build(); } private <A extends Arg> BuildRule createExtensionBuildRule( BuildRuleParams params, BuildRuleResolver ruleResolver, PythonPlatform pythonPlatform, CxxPlatform cxxPlatform, A args) throws NoSuchBuildTargetException { SourcePathResolver pathResolver = new SourcePathResolver(ruleResolver); String extensionName = getExtensionName(params.getBuildTarget()); Path extensionPath = getExtensionPath( params.getProjectFilesystem(), params.getBuildTarget(), pythonPlatform.getFlavor(), cxxPlatform.getFlavor()); return CxxLinkableEnhancer.createCxxLinkableBuildRule( cxxBuckConfig, cxxPlatform, params, ruleResolver, pathResolver, getExtensionTarget( params.getBuildTarget(), pythonPlatform.getFlavor(), cxxPlatform.getFlavor()), Linker.LinkType.SHARED, Optional.of(extensionName), extensionPath, Linker.LinkableDepType.SHARED, FluentIterable.from(params.getDeps()).filter(NativeLinkable.class), args.cxxRuntimeType, Optional.<SourcePath>absent(), ImmutableSet.<BuildTarget>of(), NativeLinkableInput.builder() .setArgs(getExtensionArgs(params, ruleResolver, pathResolver, cxxPlatform, args)) .setFrameworks(args.frameworks.or(ImmutableSortedSet.<FrameworkPath>of())) .setLibraries(args.libraries.or(ImmutableSortedSet.<FrameworkPath>of())) .build()); } @Override public <A extends Arg> BuildRule createBuildRule( TargetGraph targetGraph, final BuildRuleParams params, final BuildRuleResolver ruleResolver, final A args) throws NoSuchBuildTargetException { // See if we're building a particular "type" of this library, and if so, extract // it as an enum. final Optional<Map.Entry<Flavor, Type>> type = LIBRARY_TYPE.getFlavorAndValue(params.getBuildTarget()); Optional<Map.Entry<Flavor, CxxPlatform>> platform = cxxPlatforms.getFlavorAndValue(params.getBuildTarget()); final Optional<Map.Entry<Flavor, PythonPlatform>> pythonPlatform = pythonPlatforms.getFlavorAndValue(params.getBuildTarget()); // If we *are* building a specific type of this lib, call into the type specific // rule builder methods. Currently, we only support building a shared lib from the // pre-existing static lib, which we do here. if (type.isPresent() && platform.isPresent() && pythonPlatform.isPresent()) { Preconditions.checkState(type.get().getValue() == Type.EXTENSION); return createExtensionBuildRule( params.copyWithDeps( Suppliers.ofInstance( ImmutableSortedSet.copyOf( getPlatformDeps( params, ruleResolver, pythonPlatform.get().getValue(), args))), Suppliers.ofInstance(ImmutableSortedSet.<BuildRule>of())), ruleResolver, pythonPlatform.get().getValue(), platform.get().getValue(), args); } // Otherwise, we return the generic placeholder of this library, that dependents can use // get the real build rules via querying the action graph. final SourcePathResolver pathResolver = new SourcePathResolver(ruleResolver); Path baseModule = PythonUtil.getBasePath(params.getBuildTarget(), args.baseModule); final Path module = baseModule.resolve(getExtensionName(params.getBuildTarget())); return new CxxPythonExtension(params, pathResolver) { @Override protected BuildRule getExtension(PythonPlatform pythonPlatform, CxxPlatform cxxPlatform) throws NoSuchBuildTargetException { return ruleResolver.requireRule( getBuildTarget() .withFlavors( pythonPlatform.getFlavor(), cxxPlatform.getFlavor(), CxxDescriptionEnhancer.SHARED_FLAVOR)); } @Override public Path getModule() { return module; } @Override public PythonPackageComponents getPythonPackageComponents( PythonPlatform pythonPlatform, CxxPlatform cxxPlatform) throws NoSuchBuildTargetException { BuildRule extension = getExtension(pythonPlatform, cxxPlatform); SourcePath output = new BuildTargetSourcePath(extension.getBuildTarget()); return PythonPackageComponents.of( ImmutableMap.of(module, output), ImmutableMap.<Path, SourcePath>of(), ImmutableMap.<Path, SourcePath>of(), ImmutableSet.<SourcePath>of(), Optional.of(false)); } @Override public SharedNativeLinkTarget getNativeLinkTarget(final PythonPlatform pythonPlatform) { return new SharedNativeLinkTarget() { @Override public BuildTarget getBuildTarget() { return params.getBuildTarget().withAppendedFlavors(pythonPlatform.getFlavor()); } @Override public Iterable<? extends NativeLinkable> getSharedNativeLinkTargetDeps( CxxPlatform cxxPlatform) { return FluentIterable.from(getPlatformDeps(params, ruleResolver, pythonPlatform, args)) .filter(NativeLinkable.class); } @Override public Optional<String> getSharedNativeLinkTargetLibraryName(CxxPlatform cxxPlatform) { return Optional.absent(); } @Override public NativeLinkableInput getSharedNativeLinkTargetInput(CxxPlatform cxxPlatform) throws NoSuchBuildTargetException { return NativeLinkableInput.builder() .addAllArgs( getExtensionArgs( params.copyWithChanges( params .getBuildTarget() .withAppendedFlavors( pythonPlatform.getFlavor(), CxxDescriptionEnhancer.SHARED_FLAVOR), Suppliers.ofInstance( ImmutableSortedSet.copyOf( getPlatformDeps(params, ruleResolver, pythonPlatform, args))), Suppliers.ofInstance(ImmutableSortedSet.<BuildRule>of())), ruleResolver, pathResolver, cxxPlatform, args)) .addAllFrameworks(args.frameworks.or(ImmutableSortedSet.<FrameworkPath>of())) .build(); } }; } @Override public ImmutableSortedSet<BuildRule> getRuntimeDeps() { return getDeclaredDeps(); } }; } @Override public BuildRuleType getBuildRuleType() { return TYPE; } @Override public Iterable<BuildTarget> findDepsForTargetFromConstructorArgs( BuildTarget buildTarget, CellPathResolver cellRoots, Arg constructorArg) { ImmutableSet.Builder<BuildTarget> deps = ImmutableSet.builder(); // Get any parse time deps from the C/C++ platforms. deps.addAll(CxxPlatforms.getParseTimeDeps(cxxPlatforms.getValues())); for (PythonPlatform pythonPlatform : pythonPlatforms.getValues()) { deps.addAll(pythonPlatform.getCxxLibrary().asSet()); } return deps.build(); } @SuppressFieldNotInitialized public static class Arg extends CxxConstructorArg { public Optional<PatternMatchedCollection<ImmutableSortedSet<BuildTarget>>> platformDeps; public Optional<String> baseModule; } }
public Builder register(Description<?> description) { BuildRuleType type = description.getBuildRuleType(); types.put(type.getName(), type); descriptions.put(type, description); return this; }
public class JavaBinaryDescription implements Description<JavaBinaryDescription.Args> { public static final BuildRuleType TYPE = BuildRuleType.of("java_binary"); private static final Flavor FAT_JAR_INNER_JAR_FLAVOR = ImmutableFlavor.of("inner-jar"); private final JavacOptions javacOptions; private final CxxPlatform cxxPlatform; private final Optional<String> javaBinOverride; public JavaBinaryDescription( JavacOptions javacOptions, CxxPlatform cxxPlatform, Optional<String> javaBinOverride) { this.javacOptions = Preconditions.checkNotNull(javacOptions); this.cxxPlatform = Preconditions.checkNotNull(cxxPlatform); this.javaBinOverride = javaBinOverride; } @Override public BuildRuleType getBuildRuleType() { return TYPE; } @Override public Args createUnpopulatedConstructorArg() { return new Args(); } @Override public <A extends Args> BuildRule createBuildRule( TargetGraph targetGraph, BuildRuleParams params, BuildRuleResolver resolver, A args) { SourcePathResolver pathResolver = new SourcePathResolver(resolver); ImmutableMap<String, SourcePath> nativeLibraries = JavaLibraryRules.getNativeLibraries(targetGraph, params.getDeps(), cxxPlatform); BuildRuleParams binaryParams = params; // If we're packaging native libraries, we'll build the binary JAR in a separate rule and // package it into the final fat JAR, so adjust it's params to use a flavored target. if (!nativeLibraries.isEmpty()) { binaryParams = params.copyWithChanges( BuildTarget.builder(params.getBuildTarget()) .addFlavors(FAT_JAR_INNER_JAR_FLAVOR) .build(), params.getDeclaredDeps(), params.getExtraDeps()); } // Construct the build rule to build the binary JAR. ImmutableSetMultimap<JavaLibrary, Path> transitiveClasspathEntries = Classpaths.getClasspathEntries(binaryParams.getDeps()); BuildRule rule = new JavaBinary( binaryParams.appendExtraDeps(transitiveClasspathEntries.keys()), pathResolver, args.mainClass.orNull(), args.manifestFile.orNull(), args.mergeManifests.or(true), args.metaInfDirectory.orNull(), args.blacklist.or(ImmutableSortedSet.<String>of()), new DefaultDirectoryTraverser(), transitiveClasspathEntries, javaBinOverride); // If we're packaging native libraries, construct the rule to build the fat JAR, which packages // up the original binary JAR and any required native libraries. if (!nativeLibraries.isEmpty()) { BuildRule innerJarRule = rule; resolver.addToIndex(innerJarRule); SourcePath innerJar = new BuildTargetSourcePath(innerJarRule.getBuildTarget()); rule = new JarFattener( params.appendExtraDeps( Suppliers.<Iterable<BuildRule>>ofInstance( pathResolver.filterBuildRuleInputs( ImmutableList.<SourcePath>builder() .add(innerJar) .addAll(nativeLibraries.values()) .build()))), pathResolver, javacOptions, innerJar, nativeLibraries, javaBinOverride); } return rule; } @SuppressFieldNotInitialized public static class Args { public Optional<ImmutableSortedSet<BuildTarget>> deps; public Optional<String> mainClass; public Optional<SourcePath> manifestFile; public Optional<Boolean> mergeManifests; @Beta public Optional<Path> metaInfDirectory; @Beta public Optional<ImmutableSortedSet<String>> blacklist; } }
public class AppleBundleDescription implements Description<AppleBundleDescription.Arg>, Flavored, ImplicitDepsInferringDescription<AppleBundleDescription.Arg> { public static final BuildRuleType TYPE = BuildRuleType.of("apple_bundle"); private static final Flavor WATCH = ImmutableFlavor.of("watch"); private static final ImmutableSet<Flavor> SUPPORTED_LIBRARY_FLAVORS = ImmutableSet.of(CxxDescriptionEnhancer.STATIC_FLAVOR, CxxDescriptionEnhancer.SHARED_FLAVOR); private final AppleBinaryDescription appleBinaryDescription; private final AppleLibraryDescription appleLibraryDescription; private final FlavorDomain<CxxPlatform> cxxPlatformFlavorDomain; private final ImmutableMap<Flavor, AppleCxxPlatform> platformFlavorsToAppleCxxPlatforms; private final CxxPlatform defaultCxxPlatform; private final CodeSignIdentityStore codeSignIdentityStore; private final ProvisioningProfileStore provisioningProfileStore; public AppleBundleDescription( AppleBinaryDescription appleBinaryDescription, AppleLibraryDescription appleLibraryDescription, FlavorDomain<CxxPlatform> cxxPlatformFlavorDomain, Map<Flavor, AppleCxxPlatform> platformFlavorsToAppleCxxPlatforms, CxxPlatform defaultCxxPlatform, CodeSignIdentityStore codeSignIdentityStore, ProvisioningProfileStore provisioningProfileStore) { this.appleBinaryDescription = appleBinaryDescription; this.appleLibraryDescription = appleLibraryDescription; this.cxxPlatformFlavorDomain = cxxPlatformFlavorDomain; this.platformFlavorsToAppleCxxPlatforms = ImmutableMap.copyOf(platformFlavorsToAppleCxxPlatforms); this.defaultCxxPlatform = defaultCxxPlatform; this.codeSignIdentityStore = codeSignIdentityStore; this.provisioningProfileStore = provisioningProfileStore; } @Override public BuildRuleType getBuildRuleType() { return TYPE; } @Override public Arg createUnpopulatedConstructorArg() { return new Arg(); } @Override public boolean hasFlavors(final ImmutableSet<Flavor> flavors) { if (appleLibraryDescription.hasFlavors(flavors)) { return true; } ImmutableSet.Builder<Flavor> flavorBuilder = ImmutableSet.builder(); for (Flavor flavor : flavors) { if (flavor.equals(ReactNativeFlavors.DO_NOT_BUNDLE)) { continue; } flavorBuilder.add(flavor); } return appleBinaryDescription.hasFlavors(flavorBuilder.build()); } /** Only works with thin binaries. */ private CxxPlatform getCxxPlatformForBuildTarget(BuildTarget target) { CxxPlatform cxxPlatform; try { cxxPlatform = cxxPlatformFlavorDomain.getValue(target.getFlavors()).or(defaultCxxPlatform); } catch (FlavorDomainException e) { throw new HumanReadableException(e, "%s: %s", target, e.getMessage()); } return cxxPlatform; } private AppleCxxPlatform getAppleCxxPlatformForBuildTarget(BuildTarget target) { Optional<FatBinaryInfo> fatBinaryInfo = FatBinaryInfo.create(platformFlavorsToAppleCxxPlatforms, target); AppleCxxPlatform appleCxxPlatform; if (fatBinaryInfo.isPresent()) { appleCxxPlatform = fatBinaryInfo.get().getRepresentativePlatform(); } else { CxxPlatform cxxPlatform = getCxxPlatformForBuildTarget(target); appleCxxPlatform = platformFlavorsToAppleCxxPlatforms.get(cxxPlatform.getFlavor()); if (appleCxxPlatform == null) { throw new HumanReadableException( "%s: Apple bundle requires an Apple platform, found '%s'", target, cxxPlatform.getFlavor().getName()); } } return appleCxxPlatform; } @Override public <A extends Arg> AppleBundle createBuildRule( TargetGraph targetGraph, BuildRuleParams params, BuildRuleResolver resolver, A args) { AppleCxxPlatform appleCxxPlatform = getAppleCxxPlatformForBuildTarget(params.getBuildTarget()); AppleBundleDestinations destinations = AppleBundleDestinations.platformDestinations( appleCxxPlatform.getAppleSdk().getApplePlatform()); ImmutableSet.Builder<SourcePath> bundleDirsBuilder = ImmutableSet.builder(); ImmutableSet.Builder<SourcePath> dirsContainingResourceDirsBuilder = ImmutableSet.builder(); ImmutableSet.Builder<SourcePath> bundleFilesBuilder = ImmutableSet.builder(); ImmutableSet.Builder<SourcePath> bundleVariantFilesBuilder = ImmutableSet.builder(); AppleResources.collectResourceDirsAndFiles( targetGraph, Preconditions.checkNotNull(targetGraph.get(params.getBuildTarget())), bundleDirsBuilder, dirsContainingResourceDirsBuilder, bundleFilesBuilder, bundleVariantFilesBuilder); ImmutableSet<SourcePath> bundleDirs = bundleDirsBuilder.build(); ImmutableSet<SourcePath> dirsContainingResourceDirs = dirsContainingResourceDirsBuilder.build(); ImmutableSet<SourcePath> bundleFiles = bundleFilesBuilder.build(); ImmutableSet<SourcePath> bundleVariantFiles = bundleVariantFilesBuilder.build(); SourcePathResolver sourcePathResolver = new SourcePathResolver(resolver); Optional<AppleAssetCatalog> assetCatalog = AppleDescriptions.createBuildRuleForTransitiveAssetCatalogDependencies( targetGraph, params, sourcePathResolver, appleCxxPlatform.getAppleSdk().getApplePlatform(), appleCxxPlatform.getActool()); // TODO(user): Sort through the changes needed to make project generation work with // binary being optional. BuildRule flavoredBinaryRule = getFlavoredBinaryRule(targetGraph, params, resolver, args); BuildRuleParams bundleParamsWithFlavoredBinaryDep = getBundleParamsWithUpdatedDeps( params, args.binary, ImmutableSet.<BuildRule>builder() .add(flavoredBinaryRule) .addAll(assetCatalog.asSet()) .addAll( BuildRules.toBuildRulesFor( params.getBuildTarget(), resolver, SourcePaths.filterBuildTargetSourcePaths( Iterables.concat( bundleFiles, bundleDirs, dirsContainingResourceDirs, bundleVariantFiles)))) .build()); ImmutableMap<SourcePath, String> extensionBundlePaths = collectFirstLevelAppleDependencyBundles(params.getDeps(), destinations); return new AppleBundle( bundleParamsWithFlavoredBinaryDep, sourcePathResolver, args.extension, args.productName, args.infoPlist, args.infoPlistSubstitutions.get(), Optional.of(flavoredBinaryRule), destinations, bundleDirs, bundleFiles, dirsContainingResourceDirs, extensionBundlePaths, Optional.of(bundleVariantFiles), appleCxxPlatform.getIbtool(), appleCxxPlatform.getDsymutil(), appleCxxPlatform.getCxxPlatform().getStrip(), assetCatalog, args.getTests(), appleCxxPlatform.getAppleSdk(), codeSignIdentityStore, provisioningProfileStore, AppleBundle.DebugInfoFormat.DSYM); } private <A extends Arg> BuildRule getFlavoredBinaryRule( TargetGraph targetGraph, final BuildRuleParams params, final BuildRuleResolver resolver, A args) { // Cxx targets must have one Platform Flavor set otherwise nothing gets compiled. ImmutableSet<Flavor> flavors = params .getBuildTarget() .withoutFlavors(ImmutableSet.of(ReactNativeFlavors.DO_NOT_BUNDLE)) .getFlavors(); if (!cxxPlatformFlavorDomain.containsAnyOf(flavors)) { flavors = new ImmutableSet.Builder<Flavor>() .addAll(flavors) .add(defaultCxxPlatform.getFlavor()) .build(); } final TargetNode<?> binaryTargetNode = Preconditions.checkNotNull(targetGraph.get(args.binary)); // If the binary target of the AppleBundle is an AppleLibrary then the build flavor // must be specified. if (binaryTargetNode.getDescription() instanceof AppleLibraryDescription && (Sets.intersection( SUPPORTED_LIBRARY_FLAVORS, binaryTargetNode.getBuildTarget().getFlavors()) .size() != 1)) { throw new HumanReadableException( "AppleExtension bundle [%s] must have exactly one of these flavors: [%s].", binaryTargetNode.getBuildTarget().toString(), Joiner.on(", ").join(SUPPORTED_LIBRARY_FLAVORS)); } BuildRuleParams binaryRuleParams = new BuildRuleParams( args.binary, Suppliers.ofInstance( BuildRules.toBuildRulesFor( params.getBuildTarget(), resolver, binaryTargetNode.getDeclaredDeps())), Suppliers.ofInstance( BuildRules.toBuildRulesFor( params.getBuildTarget(), resolver, binaryTargetNode.getExtraDeps())), params.getProjectFilesystem(), params.getCellRoots(), params.getRuleKeyBuilderFactory()); return CxxDescriptionEnhancer.requireBuildRule( targetGraph, binaryRuleParams, resolver, flavors.toArray(new Flavor[0])); } private static BuildRuleParams getBundleParamsWithUpdatedDeps( final BuildRuleParams params, final BuildTarget originalBinaryTarget, final Set<BuildRule> newDeps) { // Remove the unflavored binary rule and add the flavored one instead. final Predicate<BuildRule> notOriginalBinaryRule = Predicates.not(BuildRules.isBuildRuleWithTarget(originalBinaryTarget)); return params.copyWithDeps( Suppliers.ofInstance( FluentIterable.from(params.getDeclaredDeps().get()) .filter(notOriginalBinaryRule) .append(newDeps) .toSortedSet(Ordering.natural())), Suppliers.ofInstance( FluentIterable.from(params.getExtraDeps().get()) .filter(notOriginalBinaryRule) .toSortedSet(Ordering.natural()))); } private ImmutableMap<SourcePath, String> collectFirstLevelAppleDependencyBundles( ImmutableSortedSet<BuildRule> deps, AppleBundleDestinations destinations) { ImmutableMap.Builder<SourcePath, String> extensionBundlePaths = ImmutableMap.builder(); // We only care about the direct layer of dependencies. ExtensionBundles inside ExtensionBundles // do not get pulled in to the top-level Bundle. for (BuildRule rule : deps) { if (rule instanceof AppleBundle) { AppleBundle appleBundle = (AppleBundle) rule; if (AppleBundleExtension.APPEX.toFileExtension().equals(appleBundle.getExtension()) || AppleBundleExtension.APP.toFileExtension().equals(appleBundle.getExtension())) { Path outputPath = Preconditions.checkNotNull( appleBundle.getPathToOutput(), "Path cannot be null for AppleBundle [%s].", appleBundle); SourcePath sourcePath = new BuildTargetSourcePath(appleBundle.getBuildTarget(), outputPath); Path destinationPath; String platformName = appleBundle.getPlatformName(); if ((platformName.equals(ApplePlatform.Name.WATCHOS) || platformName.equals(ApplePlatform.Name.WATCHSIMULATOR)) && appleBundle.getExtension().equals(AppleBundleExtension.APP.toFileExtension())) { destinationPath = destinations.getWatchAppPath(); } else { destinationPath = destinations.getPlugInsPath(); } extensionBundlePaths.put(sourcePath, destinationPath.toString()); } } } return extensionBundlePaths.build(); } /** Propagate the bundle's platform flavors to its dependents. */ @Override public ImmutableSet<BuildTarget> findDepsForTargetFromConstructorArgs( BuildTarget buildTarget, Function<Optional<String>, Path> cellRoots, AppleBundleDescription.Arg constructorArg) { if (!constructorArg.deps.isPresent()) { return ImmutableSet.of(); } if (!cxxPlatformFlavorDomain.containsAnyOf(buildTarget.getFlavors())) { buildTarget = BuildTarget.builder(buildTarget) .addAllFlavors(ImmutableSet.of(defaultCxxPlatform.getFlavor())) .build(); } Optional<FatBinaryInfo> fatBinaryInfo = FatBinaryInfo.create(platformFlavorsToAppleCxxPlatforms, buildTarget); CxxPlatform cxxPlatform; if (fatBinaryInfo.isPresent()) { AppleCxxPlatform appleCxxPlatform = fatBinaryInfo.get().getRepresentativePlatform(); cxxPlatform = appleCxxPlatform.getCxxPlatform(); } else { cxxPlatform = getCxxPlatformForBuildTarget(buildTarget); } String platformName = cxxPlatform.getFlavor().getName(); final Flavor actualWatchFlavor; if (ApplePlatform.isSimulator(platformName)) { actualWatchFlavor = ImmutableFlavor.builder().name("watchsimulator-i386").build(); } else if (platformName.startsWith(ApplePlatform.Name.IPHONEOS) || platformName.startsWith(ApplePlatform.Name.WATCHOS)) { actualWatchFlavor = ImmutableFlavor.builder().name("watchos-armv7k").build(); } else { actualWatchFlavor = ImmutableFlavor.builder().name(platformName).build(); } FluentIterable<BuildTarget> depsExcludingBinary = FluentIterable.from(constructorArg.deps.get()) .filter(Predicates.not(Predicates.equalTo(constructorArg.binary))); FluentIterable<BuildTarget> targetsWithPlatformFlavors = depsExcludingBinary.filter(BuildTargets.containsFlavors(cxxPlatformFlavorDomain)); FluentIterable<BuildTarget> targetsWithoutPlatformFlavors = depsExcludingBinary.filter( Predicates.not(BuildTargets.containsFlavors(cxxPlatformFlavorDomain))); FluentIterable<BuildTarget> watchTargets = targetsWithoutPlatformFlavors .filter(BuildTargets.containsFlavor(WATCH)) .transform( new Function<BuildTarget, BuildTarget>() { @Override public BuildTarget apply(BuildTarget input) { return BuildTarget.builder(input.withoutFlavors(ImmutableSet.of(WATCH))) .addFlavors(actualWatchFlavor) .build(); } }); targetsWithoutPlatformFlavors = targetsWithoutPlatformFlavors.filter(Predicates.not(BuildTargets.containsFlavor(WATCH))); return ImmutableSet.<BuildTarget>builder() .addAll(targetsWithPlatformFlavors) .addAll(watchTargets) .addAll( BuildTargets.propagateFlavorDomains( buildTarget, ImmutableSet.<FlavorDomain<?>>of(cxxPlatformFlavorDomain), targetsWithoutPlatformFlavors)) .build(); } @SuppressFieldNotInitialized public static class Arg implements HasAppleBundleFields, HasTests { public Either<AppleBundleExtension, String> extension; public BuildTarget binary; public SourcePath infoPlist; public Optional<ImmutableMap<String, String>> infoPlistSubstitutions; @Hint(isDep = false) public Optional<ImmutableSortedSet<BuildTarget>> deps; @Hint(isDep = false) public Optional<ImmutableSortedSet<BuildTarget>> tests; public Optional<String> xcodeProductType; public Optional<String> productName; @Override public Either<AppleBundleExtension, String> getExtension() { return extension; } @Override public SourcePath getInfoPlist() { return infoPlist; } @Override public ImmutableSortedSet<BuildTarget> getTests() { return tests.get(); } @Override public Optional<String> getXcodeProductType() { return xcodeProductType; } } }
@Override public BuildRuleType getBuildRuleType() { return BuildRuleType.of("fake_rule"); }