private ImmutableMap<Path, Path> getExpandedSourcePaths( ExecutionContext context, ImmutableMap<Path, Path> paths) throws IOException { ProjectFilesystem projectFilesystem = context.getProjectFilesystem(); ImmutableMap.Builder<Path, Path> sources = ImmutableMap.builder(); for (ImmutableMap.Entry<Path, Path> ent : paths.entrySet()) { if (ent.getValue().toString().endsWith(SRC_ZIP)) { Path destinationDirectory = projectFilesystem.resolve(tempDir.resolve(ent.getKey())); Files.createDirectories(destinationDirectory); ImmutableList<Path> zipPaths = Unzip.extractZipFile( projectFilesystem.resolve(ent.getValue()), destinationDirectory, Unzip.ExistingFileMode.OVERWRITE); for (Path path : zipPaths) { Path modulePath = destinationDirectory.relativize(path); sources.put(modulePath, path); } } else { sources.put(ent.getKey(), ent.getValue()); } } return sources.build(); }
@Test public void testCreateRelativeSymlinkToFilesInRoot() throws IOException { ProjectFilesystem projectFilesystem = new ProjectFilesystem(tmp.getRoot()); tmp.newFile("biz.txt"); Path pathToDesiredLinkUnderProjectRoot = Paths.get("gamma.txt"); Path pathToExistingFileUnderProjectRoot = Paths.get("biz.txt"); Path relativePath = MorePaths.createRelativeSymlink( pathToDesiredLinkUnderProjectRoot, pathToExistingFileUnderProjectRoot, projectFilesystem); assertEquals("biz.txt", relativePath.toString()); Path absolutePathToDesiredLinkUnderProjectRoot = projectFilesystem.resolve(pathToDesiredLinkUnderProjectRoot); assertTrue(Files.isSymbolicLink(absolutePathToDesiredLinkUnderProjectRoot)); Path targetOfSymbolicLink = Files.readSymbolicLink(absolutePathToDesiredLinkUnderProjectRoot); assertEquals(relativePath, targetOfSymbolicLink); Path absolutePathToExistingFileUnderProjectRoot = projectFilesystem.resolve(pathToExistingFileUnderProjectRoot); Files.write(absolutePathToExistingFileUnderProjectRoot, "Hello, World!".getBytes()); String dataReadFromSymlink = new String(Files.readAllBytes(absolutePathToDesiredLinkUnderProjectRoot)); assertEquals("Hello, World!", dataReadFromSymlink); }
private void assertCompDir(Path compDir, Optional<String> failure) throws Exception { ProjectFilesystem filesystem = new ProjectFilesystem(tmp.getRoot().toPath()); CxxPlatform platform = DefaultCxxPlatforms.build(new CxxBuckConfig(new FakeBuckConfig())); // Build up the paths to various files the archive step will use. ImmutableList<String> compiler = platform.getCc().getCommandPrefix(new SourcePathResolver(new BuildRuleResolver())); Path output = filesystem.resolve(Paths.get("output.o")); Path relativeInput = Paths.get("input.c"); Path input = filesystem.resolve(relativeInput); filesystem.writeContentsToPath("int main() {}", relativeInput); ImmutableList.Builder<String> preprocessorCommand = ImmutableList.builder(); preprocessorCommand.addAll(compiler); ImmutableList.Builder<String> compilerCommand = ImmutableList.builder(); compilerCommand.addAll(compiler); compilerCommand.add("-g"); DebugPathSanitizer sanitizer = new DebugPathSanitizer(200, File.separatorChar, compDir, ImmutableBiMap.<Path, Path>of()); // Build an archive step. CxxPreprocessAndCompileStep step = new CxxPreprocessAndCompileStep( CxxPreprocessAndCompileStep.Operation.COMPILE_MUNGE_DEBUGINFO, output, relativeInput, CxxSource.Type.C, Optional.of(preprocessorCommand.build()), Optional.of(compilerCommand.build()), ImmutableMap.<Path, Path>of(), sanitizer); // Execute the archive step and verify it ran successfully. ExecutionContext executionContext = TestExecutionContext.newBuilder() .setProjectFilesystem(new ProjectFilesystem(tmp.getRoot().toPath())) .build(); TestConsole console = (TestConsole) executionContext.getConsole(); int exitCode = step.execute(executionContext); if (failure.isPresent()) { assertNotEquals("compile step succeeded", 0, exitCode); assertThat( console.getTextWrittenToStdErr(), console.getTextWrittenToStdErr(), Matchers.containsString(failure.get())); } else { assertEquals("compile step failed: " + console.getTextWrittenToStdErr(), 0, exitCode); // Verify that we find the expected compilation dir embedded in the file. String contents = new String(Files.readAllBytes(output)); assertThat(contents, Matchers.containsString(sanitizer.getCompilationDirectory())); } // Cleanup. Files.delete(input); Files.deleteIfExists(output); }
@Test public void testGetBuildStepsWhenThereAreNoClassesToDex() throws IOException, InterruptedException { JavaLibrary javaLibrary = createMock(JavaLibrary.class); expect(javaLibrary.getClassNamesToHashes()) .andReturn(ImmutableSortedMap.<String, HashCode>of()); BuildContext context = createMock(BuildContext.class); FakeBuildableContext buildableContext = new FakeBuildableContext(); ProjectFilesystem projectFilesystem = createMock(ProjectFilesystem.class); replayAll(); BuildTarget buildTarget = BuildTargetFactory.newInstance("//foo:bar"); BuildRuleParams params = new FakeBuildRuleParamsBuilder(buildTarget).setProjectFilesystem(projectFilesystem).build(); DexProducedFromJavaLibrary preDex = new DexProducedFromJavaLibrary( params, new SourcePathResolver(new BuildRuleResolver()), javaLibrary); List<Step> steps = preDex.getBuildSteps(context, buildableContext); verifyAll(); resetAll(); expect(projectFilesystem.resolve(Paths.get("buck-out/gen/foo"))) .andReturn(Paths.get("/home/user/buck-out/gen/foo")); expect(projectFilesystem.resolve(Paths.get("buck-out/gen/foo/bar.dex.jar"))) .andReturn(Paths.get("/home/user/buck-out/gen/foo/bar.dex.jar")); replayAll(); ExecutionContext executionContext = TestExecutionContext.newBuilder().build(); MoreAsserts.assertSteps( "Do not generate a .dex.jar file.", ImmutableList.of( String.format("rm -f %s", Paths.get("/home/user/buck-out/gen/foo/bar.dex.jar")), String.format("mkdir -p %s", Paths.get("/home/user/buck-out/gen/foo")), "record_empty_dx"), steps, executionContext); verifyAll(); resetAll(); replayAll(); Step recordArtifactAndMetadataStep = steps.get(2); assertThat(recordArtifactAndMetadataStep.getShortName(), startsWith("record_")); int exitCode = recordArtifactAndMetadataStep.execute(executionContext); assertEquals(0, exitCode); verifyAll(); }
@Override public StepExecutionResult execute(ExecutionContext context) throws InterruptedException { // Build the process, redirecting output to the provided output file. In general, // it's undesirable that both stdout and stderr are being redirected to the same // input stream. However, due to the nature of OS pipe buffering, we can't really // maintain the natural interleaving of multiple output streams in a way that we // can correctly associate both stdout/stderr streams to the one correct test out // of the many that ran. So, our best bet is to just combine them all into stdout, // so they get properly interleaved with the test start and end messages that we // use when we parse the test output. ProcessBuilder builder = new ProcessBuilder(); builder.command(command); builder.redirectOutput(filesystem.resolve(output).toFile()); builder.redirectErrorStream(true); Process process; try { process = BgProcessKiller.startProcess(builder); } catch (IOException e) { context.logError(e, "Error starting command %s", command); return StepExecutionResult.ERROR; } // Run the test process, saving the exit code. ProcessExecutor executor = context.getProcessExecutor(); ImmutableSet<ProcessExecutor.Option> options = ImmutableSet.of(ProcessExecutor.Option.EXPECTING_STD_OUT); ProcessExecutor.Result result = executor.execute( process, options, /* stdin */ Optional.<String>absent(), /* timeOutMs */ testRuleTimeoutMs, /* timeOutHandler */ Optional.<Function<Process, Void>>absent()); if (result.isTimedOut()) { throw new HumanReadableException( "Timed out after %d ms running test command %s", testRuleTimeoutMs.or(-1L), command); } // Since test binaries return a non-zero exit code when unittests fail, save the exit code // to a file rather than signalling a step failure. try (FileOutputStream stream = new FileOutputStream(filesystem.resolve(exitCode).toFile())) { stream.write((Integer.toString(result.getExitCode())).getBytes()); } catch (IOException e) { context.logError(e, "Error saving exit code to %s", exitCode); return StepExecutionResult.ERROR; } return StepExecutionResult.SUCCESS; }
@Test public void testOutputFailed() throws IOException { ProjectFilesystem projectFilesystem = new ProjectFilesystem(tmpDir.getRoot().toPath()); ChromeTraceBuildListener listener = new ChromeTraceBuildListener( projectFilesystem, new FakeClock(1409702151000000000L), new ObjectMapper(), Locale.US, TimeZone.getTimeZone("America/Los_Angeles"), /* tracesToKeep */ 3, false); try { assumeTrue("Can make the root directory read-only", tmpDir.getRoot().setReadOnly()); listener.outputTrace(new BuildId("BUILD_ID")); fail("Expected an exception."); } catch (HumanReadableException e) { assertEquals( "Unable to write trace file: java.nio.file.AccessDeniedException: " + projectFilesystem.resolve(BuckConstant.BUCK_OUTPUT_PATH), e.getMessage()); } finally { tmpDir.getRoot().setWritable(true); } }
@Test public void ruleKeyDoesNotChangeWhenOnlyDependencyRuleKeyChanges() throws Exception { ProjectFilesystem filesystem = new FakeProjectFilesystem(); BuildRuleResolver resolver = new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer()); SourcePathResolver pathResolver = new SourcePathResolver(resolver); Path depOutput = Paths.get("output"); FakeBuildRule dep = resolver.addToIndex( new FakeBuildRule(BuildTargetFactory.newInstance("//:dep"), filesystem, pathResolver)); dep.setOutputFile(depOutput.toString()); filesystem.writeContentsToPath("hello", dep.getPathToOutput()); FakeFileHashCache hashCache = new FakeFileHashCache(ImmutableMap.of(filesystem.resolve(depOutput), HashCode.fromInt(0))); BuildRule rule = GenruleBuilder.newGenruleBuilder(BuildTargetFactory.newInstance("//:rule")) .setOut("out") .setSrcs(ImmutableList.<SourcePath>of(new BuildTargetSourcePath(dep.getBuildTarget()))) .build(resolver, filesystem); RuleKey inputKey1 = new InputBasedRuleKeyBuilderFactory(0, hashCache, pathResolver).build(rule); RuleKey inputKey2 = new InputBasedRuleKeyBuilderFactory(0, hashCache, pathResolver).build(rule); assertThat(inputKey1, Matchers.equalTo(inputKey2)); }
private static boolean inputFilesUnderSymlink( // We use Collection<Path> instead of Iterable<Path> to prevent // accidentally passing in Path, since Path itself is Iterable<Path>. Collection<Path> inputs, ProjectFilesystem projectFilesystem, Map<Path, Path> symlinkExistenceCache, Map<Path, Path> newSymlinksEncountered) throws IOException { boolean result = false; for (Path input : inputs) { for (int i = 1; i < input.getNameCount(); i++) { Path subpath = input.subpath(0, i); Path resolvedSymlink = symlinkExistenceCache.get(subpath); if (resolvedSymlink != null) { LOG.debug("Detected cached symlink %s -> %s", subpath, resolvedSymlink); newSymlinksEncountered.put(subpath, resolvedSymlink); result = true; } else if (projectFilesystem.isSymLink(subpath)) { Path symlinkTarget = projectFilesystem.resolve(subpath).toRealPath(); Path relativeSymlinkTarget = projectFilesystem.getPathRelativeToProjectRoot(symlinkTarget).or(symlinkTarget); LOG.debug("Detected symbolic link %s -> %s", subpath, relativeSymlinkTarget); newSymlinksEncountered.put(subpath, relativeSymlinkTarget); symlinkExistenceCache.put(subpath, relativeSymlinkTarget); result = true; } } } return result; }
@Test public void usesCorrectCommandForPreprocess() { // Setup some dummy values for inputs to the CxxPreprocessAndCompile. SourcePathResolver pathResolver = new SourcePathResolver( new BuildRuleResolver(TargetGraph.EMPTY, new BuildTargetNodeToBuildRuleTransformer())); BuildTarget target = BuildTargetFactory.newInstance("//foo:bar"); BuildRuleParams params = new FakeBuildRuleParamsBuilder(target).build(); ProjectFilesystem filesystem = new FakeProjectFilesystem(); ImmutableList<String> platformFlags = ImmutableList.of("-Dtest=blah"); ImmutableList<String> ruleFlags = ImmutableList.of("-Dfoo=bar"); Path output = Paths.get("test.ii"); Path depFile = Paths.get("test.ii.dep"); Path input = Paths.get("test.cpp"); Path prefixHeader = Paths.get("prefix.pch"); CxxPreprocessAndCompile buildRule = CxxPreprocessAndCompile.preprocess( params, pathResolver, new PreprocessorDelegate( pathResolver, DEFAULT_SANITIZER, DEFAULT_WORKING_DIR, DEFAULT_PREPROCESSOR, platformFlags, ruleFlags, ImmutableSet.<Path>of(), ImmutableSet.<Path>of(), ImmutableSet.<Path>of(), DEFAULT_FRAMEWORK_ROOTS, DEFAULT_FRAMEWORK_PATH_SEARCH_PATH_FUNCTION, Optional.<SourcePath>of(new FakeSourcePath(filesystem, prefixHeader.toString())), ImmutableList.of(CxxHeaders.builder().build())), output, new FakeSourcePath(input.toString()), DEFAULT_INPUT_TYPE, DEFAULT_SANITIZER); // Verify it uses the expected command. ImmutableList<String> expectedPreprocessCommand = ImmutableList.<String>builder() .add("preprocessor") .add("-Dtest=blah") .add("-Dfoo=bar") .add("-include") .add(filesystem.resolve(prefixHeader).toString()) .add("-x", "c++") .add("-E") .add("-MD") .add("-MF") .add(depFile.toString() + ".tmp") .add(input.toString()) .build(); ImmutableList<String> actualPreprocessCommand = buildRule.makeMainStep().getCommand(); assertEquals(expectedPreprocessCommand, actualPreprocessCommand); }
@Test public void usesFirstCache() throws IOException { ProjectFilesystem filesystem = FakeProjectFilesystem.createJavaOnlyFilesystem(); Path path = Paths.get("world.txt"); filesystem.touch(path); Path fullPath = filesystem.resolve(path); DefaultFileHashCache innerCache = new DefaultFileHashCache(filesystem); StackedFileHashCache cache = new StackedFileHashCache(ImmutableList.of(innerCache)); cache.get(fullPath); assertTrue(innerCache.willGet(path)); }
public static FakeFileHashCache createFromStrings( ProjectFilesystem filesystem, Map<String, String> pathsToHashes) { Map<Path, HashCode> cachedValues = new HashMap<>(); for (Map.Entry<String, String> entry : pathsToHashes.entrySet()) { // Retain the original behaviour cachedValues.put(Paths.get(entry.getKey()), HashCode.fromString(entry.getValue())); // And ensure that the absolute path is also present. if (!entry.getKey().startsWith("/")) { cachedValues.put(filesystem.resolve(entry.getKey()), HashCode.fromString(entry.getValue())); } } return new FakeFileHashCache(cachedValues); }
@Test public void shouldSetSrcAndOutToNameParameterIfNeitherAreSet() throws IOException { ProjectFilesystem projectFilesystem = FakeProjectFilesystem.createJavaOnlyFilesystem(); ExportFile exportFile = (ExportFile) ExportFileBuilder.newExportFileBuilder(target) .build(new BuildRuleResolver(), projectFilesystem); List<Step> steps = exportFile.getBuildSteps(context, new FakeBuildableContext()); MoreAsserts.assertSteps( "The output directory should be created and then the file should be copied there.", ImmutableList.of( "mkdir -p /opt/src/buck/buck-out/gen", "cp " + projectFilesystem.resolve("example.html") + " buck-out/gen/example.html"), steps, TestExecutionContext.newInstance()); assertEquals(Paths.get("buck-out/gen/example.html"), exportFile.getPathToOutput()); }
@Test public void recursiveIgnorePaths() throws IOException, InterruptedException { Path ignoredBuildFile = Paths.get("a", "b", "BUCK"); ImmutableSet<Path> ignore = ImmutableSet.of(ignoredBuildFile.getParent()); ProjectFilesystem filesystem = new ProjectFilesystem(tmp.getRoot().toPath(), ignore); Path buildFile = Paths.get("a", "BUCK"); filesystem.mkdirs(buildFile.getParent()); filesystem.writeContentsToPath("", buildFile); filesystem.mkdirs(ignoredBuildFile.getParent()); filesystem.writeContentsToPath("", ignoredBuildFile); // Test a recursive spec with an ignored dir. BuildFileSpec recursiveSpec = BuildFileSpec.fromRecursivePath(buildFile.getParent()); ImmutableSet<Path> expectedBuildFiles = ImmutableSet.of(filesystem.resolve(buildFile)); Cell cell = new TestCellBuilder().setFilesystem(filesystem).build(); ImmutableSet<Path> actualBuildFiles = recursiveSpec.findBuildFiles(cell); assertEquals(expectedBuildFiles, actualBuildFiles); }
@Override public int execute(ExecutionContext context) throws IOException, InterruptedException { MoreFiles.makeExecutable(filesystem.resolve(file)); return 0; }
@Override protected ImmutableList<String> getShellCommandInternal(ExecutionContext context) { Optional<Path> ndkRoot = context.getAndroidPlatformTarget().getNdkDirectory(); if (!ndkRoot.isPresent()) { throw new HumanReadableException( "Must define a local.properties file" + " with a property named 'ndk.dir' that points to the absolute path of" + " your Android NDK directory, or set ANDROID_NDK."); } Optional<Path> ndkBuild = new ExecutableFinder().getOptionalExecutable(Paths.get("ndk-build"), ndkRoot.get()); if (!ndkBuild.isPresent()) { throw new HumanReadableException("Unable to find ndk-build"); } ConcurrencyLimit concurrencyLimit = context.getConcurrencyLimit(); ImmutableList.Builder<String> builder = ImmutableList.builder(); builder.add( ndkBuild.get().toAbsolutePath().toString(), "-j", // TODO(user): using -j here is wrong. It lets make run too many work when we do other // work in parallel. Instead, implement the GNU Make job server so make and Buck can // coordinate job concurrency. Integer.toString(concurrencyLimit.threadLimit), "-C", this.root.toString()); if (concurrencyLimit.loadLimit < Double.POSITIVE_INFINITY) { builder.add("--load-average", Double.toString(concurrencyLimit.loadLimit)); } Iterable<String> flags = Iterables.transform(this.flags, macroExpander); builder.addAll(flags); ProjectFilesystem projectFilesystem = context.getProjectFilesystem(); Function<Path, Path> absolutifier = projectFilesystem.getAbsolutifier(); // We want relative, not absolute, paths in the debug-info for binaries we build using // ndk_library. Absolute paths are machine-specific, but relative ones should be the // same everywhere. Path relativePathToProject = absolutifier.apply(this.root).relativize(projectFilesystem.getRootPath()); builder.add( "APP_PROJECT_PATH=" + absolutifier.apply(buildArtifactsDirectory) + File.separatorChar, "APP_BUILD_SCRIPT=" + absolutifier.apply(makefile), "NDK_OUT=" + absolutifier.apply(buildArtifactsDirectory) + File.separatorChar, "NDK_LIBS_OUT=" + projectFilesystem.resolve(binDirectory), "BUCK_PROJECT_DIR=" + relativePathToProject); // Suppress the custom build step messages (e.g. "Compile++ ..."). if (Platform.detect() == Platform.WINDOWS) { builder.add("host-echo-build-step=@REM"); } else { builder.add("host-echo-build-step=@#"); } // If we're running verbosely, force all the subcommands from the ndk build to be printed out. if (context.getVerbosity().shouldPrintCommand()) { builder.add("V=1"); // Otherwise, suppress everything, including the "make: entering directory..." messages. } else { builder.add("--silent"); } return builder.build(); }
private void runCombinedTest( CxxPreprocessMode strategy, ImmutableList<String> expectedArguments) { BuildTarget testBuildTarget = BuildTarget.builder(BuildTargetFactory.newInstance("//foo:baz")) .addAllFlavors(ImmutableSet.of(CxxCompilationDatabase.COMPILATION_DATABASE)) .build(); final String root = "/Users/user/src"; final Path fakeRoot = Paths.get(root); ProjectFilesystem filesystem = new FakeProjectFilesystem(fakeRoot); BuildRuleParams testBuildRuleParams = new FakeBuildRuleParamsBuilder(testBuildTarget).setProjectFilesystem(filesystem).build(); BuildRuleResolver testBuildRuleResolver = new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer()); SourcePathResolver testSourcePathResolver = new SourcePathResolver(testBuildRuleResolver); BuildTarget preprocessTarget = BuildTarget.builder(testBuildRuleParams.getBuildTarget().getUnflavoredBuildTarget()) .addFlavors(ImmutableFlavor.of("preprocess-test.cpp")) .build(); BuildTarget compileTarget = BuildTarget.builder(testBuildRuleParams.getBuildTarget().getUnflavoredBuildTarget()) .addFlavors(ImmutableFlavor.of("compile-test.cpp")) .build(); PreprocessorFlags preprocessorFlags = PreprocessorFlags.builder() .addSystemIncludePaths(filesystem.resolve("foo/bar"), filesystem.resolve("test")) .build(); ImmutableSortedSet.Builder<CxxPreprocessAndCompile> rules = ImmutableSortedSet.naturalOrder(); BuildRuleParams compileBuildRuleParams; switch (strategy) { case SEPARATE: CxxPreprocessAndCompile preprocessRule = CxxPreprocessAndCompile.preprocess( new FakeBuildRuleParamsBuilder(preprocessTarget) .setProjectFilesystem(filesystem) .build(), testSourcePathResolver, new PreprocessorDelegate( testSourcePathResolver, CxxPlatformUtils.DEFAULT_DEBUG_PATH_SANITIZER, CxxPlatformUtils.DEFAULT_CONFIG.getHeaderVerification(), filesystem.getRootPath(), new DefaultPreprocessor(new HashedFileTool(Paths.get("compiler"))), preprocessorFlags, new RuleKeyAppendableFunction<FrameworkPath, Path>() { @Override public void appendToRuleKey(RuleKeyObjectSink sink) { // Do nothing. } @Override public Path apply(FrameworkPath input) { throw new UnsupportedOperationException("should not be called"); } }, ImmutableList.<CxxHeaders>of()), new CompilerDelegate( testSourcePathResolver, CxxPlatformUtils.DEFAULT_DEBUG_PATH_SANITIZER, new GccCompiler(new HashedFileTool(Paths.get("compiler"))), CxxToolFlags.of()), Paths.get("test.ii"), new FakeSourcePath(filesystem, "test.cpp"), CxxSource.Type.CXX, CxxPlatformUtils.DEFAULT_DEBUG_PATH_SANITIZER); rules.add(preprocessRule); compileBuildRuleParams = new FakeBuildRuleParamsBuilder(compileTarget) .setProjectFilesystem(filesystem) .setDeclaredDeps(ImmutableSortedSet.<BuildRule>of(preprocessRule)) .build(); rules.add( CxxPreprocessAndCompile.compile( compileBuildRuleParams, testSourcePathResolver, new CompilerDelegate( testSourcePathResolver, CxxPlatformUtils.DEFAULT_DEBUG_PATH_SANITIZER, new GccCompiler(new HashedFileTool(Paths.get("compiler"))), CxxToolFlags.of()), Paths.get("test.o"), new FakeSourcePath(filesystem, "test.ii"), CxxSource.Type.CXX_CPP_OUTPUT, CxxPlatformUtils.DEFAULT_DEBUG_PATH_SANITIZER)); break; case COMBINED: case PIPED: compileBuildRuleParams = new FakeBuildRuleParamsBuilder(compileTarget).setProjectFilesystem(filesystem).build(); rules.add( CxxPreprocessAndCompile.preprocessAndCompile( compileBuildRuleParams, testSourcePathResolver, new PreprocessorDelegate( testSourcePathResolver, CxxPlatformUtils.DEFAULT_DEBUG_PATH_SANITIZER, CxxPlatformUtils.DEFAULT_CONFIG.getHeaderVerification(), filesystem.getRootPath(), new DefaultPreprocessor(new HashedFileTool(Paths.get("preprocessor"))), preprocessorFlags, new RuleKeyAppendableFunction<FrameworkPath, Path>() { @Override public void appendToRuleKey(RuleKeyObjectSink sink) { // Do nothing. } @Override public Path apply(FrameworkPath input) { throw new UnsupportedOperationException("should not be called"); } }, ImmutableList.<CxxHeaders>of()), new CompilerDelegate( testSourcePathResolver, CxxPlatformUtils.DEFAULT_DEBUG_PATH_SANITIZER, new GccCompiler(new HashedFileTool(Paths.get("compiler"))), CxxToolFlags.of()), Paths.get("test.o"), new FakeSourcePath(filesystem, "test.cpp"), CxxSource.Type.CXX, Optional.<PrecompiledHeaderReference>absent(), CxxPlatformUtils.DEFAULT_DEBUG_PATH_SANITIZER, strategy)); break; default: throw new RuntimeException("Invalid strategy"); } HeaderSymlinkTree privateSymlinkTree = CxxDescriptionEnhancer.createHeaderSymlinkTree( testBuildRuleParams, testBuildRuleResolver, testSourcePathResolver, CxxPlatformUtils.DEFAULT_PLATFORM, ImmutableMap.<Path, SourcePath>of(), HeaderVisibility.PRIVATE); HeaderSymlinkTree exportedSymlinkTree = CxxDescriptionEnhancer.createHeaderSymlinkTree( testBuildRuleParams, testBuildRuleResolver, testSourcePathResolver, CxxPlatformUtils.DEFAULT_PLATFORM, ImmutableMap.<Path, SourcePath>of(), HeaderVisibility.PUBLIC); CxxCompilationDatabase compilationDatabase = CxxCompilationDatabase.createCompilationDatabase( testBuildRuleParams, testSourcePathResolver, strategy, rules.build(), ImmutableSortedSet.of(privateSymlinkTree, exportedSymlinkTree)); assertThat( compilationDatabase.getRuntimeDeps(), Matchers.<BuildRule>contains(exportedSymlinkTree, privateSymlinkTree)); assertEquals( "getPathToOutput() should be a function of the build target.", BuildTargets.getGenPath(filesystem, testBuildTarget, "__%s.json"), compilationDatabase.getPathToOutput()); BuildContext buildContext = FakeBuildContext.NOOP_CONTEXT; BuildableContext buildableContext = new FakeBuildableContext(); List<Step> buildSteps = compilationDatabase.getPostBuildSteps(buildContext, buildableContext); assertEquals(2, buildSteps.size()); assertTrue(buildSteps.get(0) instanceof MkdirStep); assertTrue(buildSteps.get(1) instanceof CxxCompilationDatabase.GenerateCompilationCommandsJson); CxxCompilationDatabase.GenerateCompilationCommandsJson step = (CxxCompilationDatabase.GenerateCompilationCommandsJson) buildSteps.get(1); Iterable<CxxCompilationDatabaseEntry> observedEntries = step.createEntries(); Iterable<CxxCompilationDatabaseEntry> expectedEntries = ImmutableList.of( CxxCompilationDatabaseEntry.of(root, root + "/test.cpp", expectedArguments)); MoreAsserts.assertIterablesEquals(expectedEntries, observedEntries); }