@Test public void depfileBasedRuleKeyAvoidsRecompilingAfterChangeToUnusedHeader() throws Exception { CxxPlatform cxxPlatform = DefaultCxxPlatforms.build(new CxxBuckConfig(FakeBuckConfig.builder().build())); BuildTarget target = BuildTargetFactory.newInstance("//:binary_with_unused_header"); String unusedHeaderName = "unused_header.h"; String sourceName = "source.cpp"; BuildTarget preprocessTarget = getPreprocessTarget(cxxPlatform, target, sourceName, AbstractCxxSource.Type.CXX); // Run the build and verify that the C++ source was preprocessed. workspace.runBuckBuild("--config", "build.depfiles=true", target.toString()).assertSuccess(); BuckBuildLog.BuildLogEntry firstRunEntry = workspace.getBuildLog().getLogEntry(preprocessTarget); assertThat( firstRunEntry.getSuccessType(), equalTo(Optional.of(BuildRuleSuccessType.BUILT_LOCALLY))); // Now modify the unused header. workspace.writeContentsToPath( "static inline int newFunction() { return 20; }", unusedHeaderName); // Run the build again and verify that got a matching depfile rule key, and therefore // didn't recompile. workspace.runBuckBuild("--config", "build.depfiles=true", target.toString()).assertSuccess(); BuckBuildLog.BuildLogEntry secondRunEntry = workspace.getBuildLog().getLogEntry(preprocessTarget); assertThat( secondRunEntry.getSuccessType(), equalTo(Optional.of(BuildRuleSuccessType.MATCHING_DEP_FILE_RULE_KEY))); // Also, make sure the original rule keys are actually different. assertThat(secondRunEntry.getRuleKey(), Matchers.not(equalTo(firstRunEntry.getRuleKey()))); }
@Test public void depfileBasedRuleKeyRebuildsAfterChangeToUsedHeaderUsingFileRelativeInclusion() throws Exception { CxxPlatform cxxPlatform = DefaultCxxPlatforms.build(new CxxBuckConfig(FakeBuckConfig.builder().build())); BuildTarget target = BuildTargetFactory.newInstance("//:binary_with_used_relative_header"); String usedHeaderName = "source_relative_header.h"; String sourceName = "source_relative_header.cpp"; BuildTarget preprocessTarget = getPreprocessTarget(cxxPlatform, target, sourceName, AbstractCxxSource.Type.CXX); // Run the build and verify that the C++ source was preprocessed. workspace.runBuckBuild("--config", "build.depfiles=true", target.toString()).assertSuccess(); BuckBuildLog.BuildLogEntry firstRunEntry = workspace.getBuildLog().getLogEntry(preprocessTarget); assertThat( firstRunEntry.getSuccessType(), equalTo(Optional.of(BuildRuleSuccessType.BUILT_LOCALLY))); // Modify the used header. workspace.writeContentsToPath("static inline int newFunction() { return 20; }", usedHeaderName); // Run the build again and verify that we recompiled as the header caused the depfile rule key // to change. workspace.runBuckBuild("--config", "build.depfiles=true", target.toString()).assertSuccess(); BuckBuildLog.BuildLogEntry secondRunEntry = workspace.getBuildLog().getLogEntry(preprocessTarget); assertThat( secondRunEntry.getSuccessType(), equalTo(Optional.of(BuildRuleSuccessType.BUILT_LOCALLY))); // Also, make sure all three rule keys are actually different. assertThat(secondRunEntry.getRuleKey(), Matchers.not(equalTo(firstRunEntry.getRuleKey()))); }
private AndroidBinaryBuilder(BuildTarget target) { super( new AndroidBinaryDescription( DEFAULT_JAVA_OPTIONS, ANDROID_JAVAC_OPTIONS, new ProGuardConfig(FakeBuckConfig.builder().build()), ImmutableMap.<NdkCxxPlatforms.TargetCpuType, NdkCxxPlatform>of(), MoreExecutors.newDirectExecutorService(), CxxPlatformUtils.DEFAULT_CONFIG), target); }
@Test public void inputBasedRuleKeyAvoidsRerunningIfGeneratedSourceDoesNotChange() throws Exception { CxxPlatform cxxPlatform = DefaultCxxPlatforms.build(new CxxBuckConfig(FakeBuckConfig.builder().build())); BuildTarget target = BuildTargetFactory.newInstance(workspace.getDestPath(), "//:binary_using_generated_source"); String unusedGenruleInput = "unused.dat"; BuildTarget genrule = BuildTargetFactory.newInstance("//:gensource"); String sourceName = "bar.cpp"; CxxSourceRuleFactory cxxSourceRuleFactory = CxxSourceRuleFactoryHelper.of(workspace.getDestPath(), target, cxxPlatform); BuildTarget preprocessTarget = cxxSourceRuleFactory.createPreprocessBuildTarget( sourceName, AbstractCxxSource.Type.CXX, CxxSourceRuleFactory.PicType.PDC); BuildTarget compileTarget = cxxSourceRuleFactory.createCompileBuildTarget(sourceName, CxxSourceRuleFactory.PicType.PDC); // Run the build and verify that the C++ source was (preprocessed and) compiled. workspace.runBuckBuild(target.toString()).assertSuccess(); if (mode == CxxPreprocessMode.SEPARATE) { assertThat( workspace.getBuildLog().getLogEntry(preprocessTarget).getSuccessType(), equalTo(Optional.of(BuildRuleSuccessType.BUILT_LOCALLY))); } assertThat( workspace.getBuildLog().getLogEntry(compileTarget).getSuccessType(), equalTo(Optional.of(BuildRuleSuccessType.BUILT_LOCALLY))); // Now modify the unused genrule input. workspace.writeContentsToPath("SOMETHING ELSE", unusedGenruleInput); // Run the build again and verify that got a matching input-based rule key, and therefore // didn't recompile. workspace.runBuckBuild(target.toString()).assertSuccess(); // Verify that the genrule actually re-ran. assertThat( workspace.getBuildLog().getLogEntry(genrule).getSuccessType(), equalTo(Optional.of(BuildRuleSuccessType.BUILT_LOCALLY))); // Verify that the (preprocess and) compile rules aren't re-run. if (mode == CxxPreprocessMode.SEPARATE) { assertThat( workspace.getBuildLog().getLogEntry(preprocessTarget).getSuccessType(), equalTo(Optional.of(BuildRuleSuccessType.MATCHING_INPUT_BASED_RULE_KEY))); } assertThat( workspace.getBuildLog().getLogEntry(compileTarget).getSuccessType(), equalTo(Optional.of(BuildRuleSuccessType.MATCHING_INPUT_BASED_RULE_KEY))); }
public static void assumeGoCompilerAvailable() throws InterruptedException, IOException { Throwable exception = null; try { ProcessExecutor executor = new ProcessExecutor(new TestConsole()); new GoBuckConfig( FakeBuckConfig.builder().build(), executor, FlavorDomain.from("Cxx", ImmutableSet.<CxxPlatform>of())) .getCompiler(); } catch (HumanReadableException e) { exception = e; } assumeNoException(exception); }
@Test public void inputBasedRuleKeyAvoidsRecompilingAfterChangeToUnusedHeader() throws Exception { // This test is only meant to check the separate flow, as we want to avoid recompiling if only // unused headers have changed. assumeTrue("only tests \"separate\" preprocess mode", mode == CxxPreprocessMode.SEPARATE); CxxPlatform cxxPlatform = DefaultCxxPlatforms.build(new CxxBuckConfig(FakeBuckConfig.builder().build())); BuildTarget target = BuildTargetFactory.newInstance("//:binary_with_unused_header"); CxxSourceRuleFactory cxxSourceRuleFactory = CxxSourceRuleFactoryHelper.of(workspace.getDestPath(), target, cxxPlatform); String unusedHeaderName = "unused_header.h"; String sourceName = "source.cpp"; BuildTarget compileTarget = cxxSourceRuleFactory.createCompileBuildTarget(sourceName, CxxSourceRuleFactory.PicType.PDC); // Run the build and verify that the C++ source was compiled. workspace.runBuckBuild(target.toString()); BuckBuildLog.BuildLogEntry firstRunEntry = workspace.getBuildLog().getLogEntry(compileTarget); assertThat( firstRunEntry.getSuccessType(), equalTo(Optional.of(BuildRuleSuccessType.BUILT_LOCALLY))); // Now modify the unused header. workspace.writeContentsToPath( "static inline int newFunction() { return 20; }", unusedHeaderName); // Run the build again and verify that got a matching input-based rule key, and therefore // didn't recompile. workspace.runBuckBuild(target.toString()); BuckBuildLog.BuildLogEntry secondRunEntry = workspace.getBuildLog().getLogEntry(compileTarget); assertThat( secondRunEntry.getSuccessType(), equalTo(Optional.of(BuildRuleSuccessType.MATCHING_INPUT_BASED_RULE_KEY))); // Also, make sure the original rule keys are actually different. assertThat(secondRunEntry.getRuleKey(), Matchers.not(equalTo(firstRunEntry.getRuleKey()))); }
@Test public void testScriptBuildPhaseWithReactNative() throws NoSuchBuildTargetException { NewNativeTargetProjectMutator mutator = mutatorWithCommonDefaults(); BuildTarget depBuildTarget = BuildTargetFactory.newInstance("//foo:dep"); ProjectFilesystem filesystem = new AllExistingProjectFilesystem(); ReactNativeBuckConfig buckConfig = new ReactNativeBuckConfig( FakeBuckConfig.builder() .setSections( ImmutableMap.of( "react-native", ImmutableMap.of("packager_worker", "react-native/packager.sh"))) .setFilesystem(filesystem) .build()); TargetNode<?> reactNativeNode = IosReactNativeLibraryBuilder.builder(depBuildTarget, buckConfig) .setBundleName("Apps/Foo/FooBundle.js") .setEntryPath(new PathSourcePath(filesystem, Paths.get("js/FooApp.js"))) .build(); mutator.setPostBuildRunScriptPhasesFromTargetNodes( ImmutableList.<TargetNode<?>>of(reactNativeNode)); NewNativeTargetProjectMutator.Result result = mutator.buildTargetAndAddToProject(generatedProject); PBXShellScriptBuildPhase phase = getSingletonPhaseByType(result.target, PBXShellScriptBuildPhase.class); String shellScript = phase.getShellScript(); assertThat( shellScript, startsWith( "BASE_DIR=${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}\n" + "JS_OUT=${BASE_DIR}/Apps/Foo/FooBundle.js\n" + "SOURCE_MAP=${TEMP_DIR}/rn_source_map/Apps/Foo/FooBundle.js.map\n")); }
@Test public void createLexYaccBuildRules() throws IOException { BuildRuleResolver resolver = new BuildRuleResolver(); // Setup our C++ buck config with the paths to the lex/yacc binaries. FakeProjectFilesystem filesystem = new FakeProjectFilesystem(); Path lexPath = Paths.get("lex"); filesystem.touch(lexPath); Path yaccPath = Paths.get("yacc"); filesystem.touch(yaccPath); BuckConfig buckConfig = FakeBuckConfig.builder() .setSections( ImmutableMap.of( "cxx", ImmutableMap.of( "lex", lexPath.toString(), "yacc", yaccPath.toString()))) .setFilesystem(filesystem) .build(); CxxPlatform cxxBuckConfig = DefaultCxxPlatforms.build(new CxxBuckConfig(buckConfig)); // Setup the target name and build params. UnflavoredBuildTarget target = BuildTargetFactory.newInstance("//:test").getUnflavoredBuildTarget(); BuildRuleParams params = new FakeBuildRuleParamsBuilder(BuildTarget.of(target)).build(); // Setup a genrule that generates our lex source. String lexSourceName = "test.ll"; BuildTarget genruleTarget = BuildTargetFactory.newInstance("//:genrule_lex"); Genrule genrule = (Genrule) GenruleBuilder.newGenruleBuilder(genruleTarget).setOut(lexSourceName).build(resolver); SourcePath lexSource = new BuildTargetSourcePath(genrule.getBuildTarget()); // Use a regular path for our yacc source. String yaccSourceName = "test.yy"; SourcePath yaccSource = new TestSourcePath(yaccSourceName); // Build the rules. CxxHeaderSourceSpec actual = CxxDescriptionEnhancer.createLexYaccBuildRules( params, resolver, cxxBuckConfig, ImmutableList.<String>of(), ImmutableMap.of(lexSourceName, lexSource), ImmutableList.<String>of(), ImmutableMap.of(yaccSourceName, yaccSource)); // Grab the generated lex rule and verify it has the genrule as a dep. Lex lex = (Lex) resolver.getRule(CxxDescriptionEnhancer.createLexBuildTarget(target, lexSourceName)); assertNotNull(lex); assertEquals(ImmutableSortedSet.<BuildRule>of(genrule), lex.getDeps()); // Grab the generated yacc rule and verify it has no deps. Yacc yacc = (Yacc) resolver.getRule(CxxDescriptionEnhancer.createYaccBuildTarget(target, yaccSourceName)); assertNotNull(yacc); assertEquals(ImmutableSortedSet.<BuildRule>of(), yacc.getDeps()); // Check the header/source spec is correct. Path lexOutputSource = CxxDescriptionEnhancer.getLexSourceOutputPath(target, lexSourceName); Path lexOutputHeader = CxxDescriptionEnhancer.getLexHeaderOutputPath(target, lexSourceName); Path yaccOutputPrefix = CxxDescriptionEnhancer.getYaccOutputPrefix( target, Files.getNameWithoutExtension(yaccSourceName)); Path yaccOutputSource = Yacc.getSourceOutputPath(yaccOutputPrefix); Path yaccOutputHeader = Yacc.getHeaderOutputPath(yaccOutputPrefix); CxxHeaderSourceSpec expected = CxxHeaderSourceSpec.of( ImmutableMap.<Path, SourcePath>of( target.getBasePath().resolve(lexSourceName + ".h"), new BuildTargetSourcePath(lex.getBuildTarget(), lexOutputHeader), target.getBasePath().resolve(yaccSourceName + ".h"), new BuildTargetSourcePath(yacc.getBuildTarget(), yaccOutputHeader)), ImmutableMap.of( lexSourceName + ".cc", CxxSource.of( CxxSource.Type.CXX, new BuildTargetSourcePath(lex.getBuildTarget(), lexOutputSource), ImmutableList.<String>of()), yaccSourceName + ".cc", CxxSource.of( CxxSource.Type.CXX, new BuildTargetSourcePath(yacc.getBuildTarget(), yaccOutputSource), ImmutableList.<String>of()))); assertEquals(expected, actual); }
@Test public void shouldBeAbleToConstructACxxLibraryFromThrift() throws Exception { ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(this, "cxx", tmp); workspace.setUp(); BuckEventBus eventBus = BuckEventBusFactory.newInstance(); ProjectFilesystem filesystem = new ProjectFilesystem(workspace.getDestPath()); Path compiler = new ExecutableFinder() .getExecutable(Paths.get("echo"), ImmutableMap.copyOf(System.getenv())); BuckConfig config = FakeBuckConfig.builder() .setFilesystem(filesystem) .setSections( "[thrift]", "compiler = " + compiler, "compiler2 = " + compiler, "cpp_library = //thrift:fake", "cpp_reflection_library = //thrift:fake") .build(); TypeCoercerFactory typeCoercerFactory = new DefaultTypeCoercerFactory(); Parser parser = new Parser( new ParserConfig(config), typeCoercerFactory, new ConstructorArgMarshaller(typeCoercerFactory)); Cell cell = Cell.createCell( filesystem, new TestConsole(), Watchman.NULL_WATCHMAN, config, new KnownBuildRuleTypesFactory( new ProcessExecutor(new TestConsole()), new FakeAndroidDirectoryResolver(), Optional.<Path>absent()), new FakeAndroidDirectoryResolver(), new DefaultClock()); BuildTarget target = BuildTargetFactory.newInstance(filesystem, "//thrift:exe"); TargetGraph targetGraph = parser.buildTargetGraph( eventBus, cell, false, Executors.newSingleThreadExecutor(), ImmutableSet.of(target)); TargetNodeToBuildRuleTransformer transformer = new BuildTargetNodeToBuildRuleTransformer(); // There was a case where the cxx library being generated wouldn't put the header into the tree // with the right flavour. This catches this case without us needing to stick a working thrift // compiler into buck's own source. Pair<ActionGraph, BuildRuleResolver> actionGraphAndResolver = new TargetGraphToActionGraph(eventBus, transformer).apply(targetGraph); // This is to cover the case where we weren't passing flavors around correctly, which ended // making the binary depend 'placeholder' BuildRules instead of real ones. This is the // regression test for that case. BuildRuleResolver ruleResolver = actionGraphAndResolver.getSecond(); BuildTarget binaryFlavor = target.withFlavors(ImmutableFlavor.of("binary")); ImmutableSortedSet<BuildRule> deps = ruleResolver.getRule(binaryFlavor).getDeps(); assertThat( FluentIterable.from(deps) .anyMatch( new Predicate<BuildRule>() { @Override public boolean apply(BuildRule input) { return input instanceof NoopBuildRule; } }), Matchers.is(false)); }