/* * Copyright 2015-present Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package com.facebook.buck.cxx; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assume.assumeThat; import com.facebook.buck.cli.FakeBuckConfig; import com.facebook.buck.io.ProjectFilesystem; import com.facebook.buck.model.BuildTarget; import com.facebook.buck.model.BuildTargetFactory; import com.facebook.buck.model.BuildTargets; import com.facebook.buck.rules.BuildRuleSuccessType; import com.facebook.buck.testutil.FakeProjectFilesystem; import com.facebook.buck.testutil.integration.BuckBuildLog; import com.facebook.buck.testutil.integration.ProjectWorkspace; import com.facebook.buck.testutil.integration.TemporaryPaths; import com.facebook.buck.testutil.integration.TestDataHelper; import com.facebook.buck.util.HumanReadableException; import com.facebook.buck.util.environment.Platform; import com.google.common.base.Charsets; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.io.Files; import java.io.IOException; import java.nio.file.Path; import java.util.Collection; import java.util.Optional; import org.hamcrest.Matchers; import org.junit.Assume; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.runners.Parameterized; public class CxxPreprocessAndCompileIntegrationTest { @Parameterized.Parameters(name = "sandbox_sources={0}") public static Collection<Object[]> data() { return ImmutableList.of(new Object[] {true}, new Object[] {false}); } @Parameterized.Parameter(0) public boolean sandboxSource; @Rule public TemporaryPaths tmp = new TemporaryPaths(); private ProjectWorkspace workspace; @Before public void setUp() throws IOException { workspace = TestDataHelper.createProjectWorkspaceForScenario(this, "step_test", tmp); workspace.setUp(); workspace.writeContentsToPath( "[cxx]\n" + " sandbox_sources = " + sandboxSource + "\n" + " asflags = -g\n" + " cppflags = -g\n" + " cflags = -g\n" + " cxxppflags = -g\n" + " cxxflags = -g\n" + "[build]\n" + " depfiles = enabled\n", ".buckconfig"); } @Test public void sanitizeWorkingDirectory() throws IOException { BuildTarget target = BuildTargetFactory.newInstance("//:simple#default,static"); ProjectFilesystem filesystem = new FakeProjectFilesystem(); workspace.runBuckBuild(target.getFullyQualifiedName()).assertSuccess(); Path lib = workspace.getPath(BuildTargets.getGenPath(filesystem, target, "%s/libsimple.a")); String contents = Files.asByteSource(lib.toFile()).asCharSource(Charsets.ISO_8859_1).read(); assertFalse(lib.toString(), contents.contains(tmp.getRoot().toString())); } @Test public void sanitizeWorkingDirectoryWhenBuildingAssembly() throws IOException { BuildTarget target = BuildTargetFactory.newInstance("//:simple_assembly#default,static"); ProjectFilesystem filesystem = new FakeProjectFilesystem(); ProjectWorkspace.ProcessResult processResult = workspace.runBuckBuild(target.getFullyQualifiedName()); processResult.assertSuccess(); Path lib = workspace.getPath(BuildTargets.getGenPath(filesystem, target, "%s/libsimple_assembly.a")); String contents = Files.asByteSource(lib.toFile()).asCharSource(Charsets.ISO_8859_1).read(); assertFalse(lib.toString(), contents.contains(tmp.getRoot().toString())); } @Test public void sanitizeSymlinkedWorkingDirectory() throws InterruptedException, IOException { TemporaryFolder folder = new TemporaryFolder(); folder.create(); ProjectFilesystem filesystem = new ProjectFilesystem(folder.getRoot().toPath()); // Setup up a symlink to our working directory. Path symlinkedRoot = folder.getRoot().toPath().resolve("symlinked-root"); java.nio.file.Files.createSymbolicLink(symlinkedRoot, tmp.getRoot()); // Run the build, setting PWD to the above symlink. Typically, this causes compilers to use // the symlinked directory, even though it's not the right project root. BuildTarget target = BuildTargetFactory.newInstance("//:simple#default,static"); workspace .runBuckCommandWithEnvironmentOverridesAndContext( tmp.getRoot(), Optional.empty(), ImmutableMap.of("PWD", symlinkedRoot.toString()), "build", target.getFullyQualifiedName()) .assertSuccess(); // Verify that we still sanitized this path correctly. Path lib = workspace.getPath(BuildTargets.getGenPath(filesystem, target, "%s/libsimple.a")); String contents = Files.asByteSource(lib.toFile()).asCharSource(Charsets.ISO_8859_1).read(); assertFalse(lib.toString(), contents.contains(tmp.getRoot().toString())); assertFalse(lib.toString(), contents.contains(symlinkedRoot.toString())); folder.delete(); } @Test public void inputBasedRuleKeyAvoidsRerunningIfGeneratedSourceDoesNotChange() throws Exception { CxxPlatform cxxPlatform = CxxPlatformUtils.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 compileTarget = cxxSourceRuleFactory.createCompileBuildTarget(sourceName); // Run the build and verify that the C++ source was compiled. workspace.runBuckBuild(target.toString()).assertSuccess(); 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 compile rules aren't re-run. assertThat( workspace.getBuildLog().getLogEntry(compileTarget).getSuccessType(), equalTo(Optional.of(BuildRuleSuccessType.MATCHING_INPUT_BASED_RULE_KEY))); } @Test public void inputBasedRuleKeyAvoidsRerunningIfGeneratedHeaderDoesNotChange() throws Exception { CxxPlatform cxxPlatform = CxxPlatformUtils.build(new CxxBuckConfig(FakeBuckConfig.builder().build())); BuildTarget target = BuildTargetFactory.newInstance("//:binary_using_generated_header"); String unusedGenruleInput = "unused.dat"; BuildTarget genrule = BuildTargetFactory.newInstance("//:genheader"); String sourceName = "foo.cpp"; CxxSourceRuleFactory cxxSourceRuleFactory = CxxSourceRuleFactoryHelper.of(workspace.getDestPath(), target, cxxPlatform); BuildTarget compileTarget = cxxSourceRuleFactory.createCompileBuildTarget(sourceName); // Run the build and verify that the C++ source was compiled. workspace.runBuckBuild(target.toString()).assertSuccess(); 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 compile rules aren't re-run. assertThat( workspace.getBuildLog().getLogEntry(compileTarget).getSuccessType(), equalTo(Optional.of(BuildRuleSuccessType.MATCHING_INPUT_BASED_RULE_KEY))); } private void depfileBasedRuleKeyRebuildsAfterChangeToUsedHeader(boolean disableHeaderSymlinks) throws Exception { CxxPlatform cxxPlatform = CxxPlatformUtils.build(new CxxBuckConfig(FakeBuckConfig.builder().build())); String targetName = "//:binary_with_used_full_header" + (disableHeaderSymlinks ? "_nosymlinks" : ""); BuildTarget target = BuildTargetFactory.newInstance(targetName); String usedHeaderName = "source_full_header.h"; String sourceName = "source_full_header.cpp"; BuildTarget preprocessTarget = getPreprocessTarget(cxxPlatform, target, sourceName); // Run the build and verify that the C++ source was preprocessed. workspace.runBuckBuild("--config", "build.depfiles=enabled", 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=enabled", 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()))); } @Test public void depfileBasedRuleKeyRebuildsAfterChangeToUsedHeader() throws Exception { depfileBasedRuleKeyRebuildsAfterChangeToUsedHeader(false); } @Test public void depfileBasedRuleKeyRebuildsAfterChangeToUsedHeaderNoSymlinks() throws Exception { depfileBasedRuleKeyRebuildsAfterChangeToUsedHeader(true); } private void depfileBasedRuleKeyRebuildsAfterChangeToUsedHeaderUsingFileRelativeInclusion( boolean disableHeaderSymlinks) throws Exception { CxxPlatform cxxPlatform = CxxPlatformUtils.build(new CxxBuckConfig(FakeBuckConfig.builder().build())); String targetName = "//:binary_with_used_relative_header" + (disableHeaderSymlinks ? "_nosymlinks" : ""); BuildTarget target = BuildTargetFactory.newInstance(targetName); String usedHeaderName = "source_relative_header.h"; String sourceName = "source_relative_header.cpp"; BuildTarget preprocessTarget = getPreprocessTarget(cxxPlatform, target, sourceName); // Run the build and verify that the C++ source was preprocessed. workspace.runBuckBuild("--config", "build.depfiles=enabled", 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=enabled", 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()))); } @Test public void depfileBasedRuleKeyRebuildsAfterChangeToUsedHeaderUsingFileRelativeInclusion() throws Exception { depfileBasedRuleKeyRebuildsAfterChangeToUsedHeaderUsingFileRelativeInclusion(false); } @Test public void depfileBasedRuleKeyRebuildsAfterChangeToUsedHeaderUsingFileRelativeInclusionNosymlinks() throws Exception { depfileBasedRuleKeyRebuildsAfterChangeToUsedHeaderUsingFileRelativeInclusion(true); } private void depfileBasedRuleKeyRebuildsAfterChangeToUsedParentHeaderUsingFileRelativeInclusion( boolean disableHeaderSymlinks) throws Exception { CxxPlatform cxxPlatform = CxxPlatformUtils.build(new CxxBuckConfig(FakeBuckConfig.builder().build())); String targetName = "//:binary_with_used_relative_parent_header" + (disableHeaderSymlinks ? "_nosymlinks" : ""); BuildTarget target = BuildTargetFactory.newInstance(targetName); String usedHeaderName = "source_relative_parent_header.h"; String sourceName = "source_relative_parent_header/source.cpp"; BuildTarget preprocessTarget = getPreprocessTarget(cxxPlatform, target, sourceName); // Run the build and verify that the C++ source was preprocessed. workspace.runBuckBuild("--config", "build.depfiles=enabled", 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=enabled", 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()))); } @Test public void depfileBasedRuleKeyRebuildsAfterChangeToUsedParentHeaderUsingFileRelativeInclusion() throws Exception { depfileBasedRuleKeyRebuildsAfterChangeToUsedParentHeaderUsingFileRelativeInclusion(false); } @Test public void depfileBasedRuleKeyRebuildsAfterChangeToUsedParentHeaderUsingFileRelativeInclusionNoSymlinks() throws Exception { depfileBasedRuleKeyRebuildsAfterChangeToUsedParentHeaderUsingFileRelativeInclusion(true); } private void depfileBasedRuleKeyAvoidsRecompilingAfterChangeToUnusedHeader( boolean disableHeaderSymlinks) throws Exception { CxxPlatform cxxPlatform = CxxPlatformUtils.build(new CxxBuckConfig(FakeBuckConfig.builder().build())); String targetName = "//:binary_with_unused_header" + (disableHeaderSymlinks ? "_nosymlinks" : ""); BuildTarget target = BuildTargetFactory.newInstance(targetName); String unusedHeaderName = "unused_header.h"; String sourceName = "source.cpp"; BuildTarget preprocessTarget = getPreprocessTarget(cxxPlatform, target, sourceName); // Run the build and verify that the C++ source was preprocessed. workspace.runBuckBuild("--config", "build.depfiles=enabled", 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=enabled", 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 depfileBasedRuleKeyAvoidsRecompilingAfterChangeToUnusedHeader() throws Exception { depfileBasedRuleKeyAvoidsRecompilingAfterChangeToUnusedHeader(false); } @Test public void depfileBasedRuleKeyAvoidsRecompilingAfterChangeToUnusedHeaderNoSymlinks() throws Exception { depfileBasedRuleKeyAvoidsRecompilingAfterChangeToUnusedHeader(true); } @Test public void manifestCachingRebuildsAfterChangeToUsedHeader() throws Exception { CxxPlatform cxxPlatform = CxxPlatformUtils.build(new CxxBuckConfig(FakeBuckConfig.builder().build())); BuildTarget target = BuildTargetFactory.newInstance("//:binary_with_used_full_header"); String usedHeaderName = "source_full_header.h"; String sourceName = "source_full_header.cpp"; BuildTarget preprocessTarget = getPreprocessTarget(cxxPlatform, target, sourceName); // Enable caching for manifest-based caching. workspace.enableDirCache(); // Run the build and verify that the C++ source was preprocessed. workspace.runBuckBuild("--config", "build.depfiles=cache", 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); // Clean the build directory, so that we need to go to cache. workspace.runBuckCommand("clean"); // Run the build again and verify that we recompiled as the header caused the depfile rule key // to change. workspace.runBuckBuild("--config", "build.depfiles=cache", 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()))); } @Test public void manifestCachingRebuildsAfterChangeToUsedHeaderUsingFileRelativeInclusion() throws Exception { CxxPlatform cxxPlatform = CxxPlatformUtils.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); // Enable caching for manifest-based caching. workspace.enableDirCache(); // Run the build and verify that the C++ source was preprocessed. workspace.runBuckBuild("--config", "build.depfiles=cache", 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); // Clean the build directory, so that we need to go to cache. workspace.runBuckCommand("clean"); // Run the build again and verify that we recompiled as the header caused the depfile rule key // to change. workspace.runBuckBuild("--config", "build.depfiles=cache", 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()))); } @Test public void manifestCachingGetsHitAfterChangeToUnusedHeader() throws Exception { CxxPlatform cxxPlatform = CxxPlatformUtils.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); // Enable caching for manifest-based caching. workspace.enableDirCache(); // Run the build and verify that the C++ source was preprocessed. workspace.runBuckBuild("--config", "build.depfiles=cache", target.toString()).assertSuccess(); BuckBuildLog.BuildLogEntry firstRunEntry = workspace.getBuildLog().getLogEntry(preprocessTarget); assertThat( firstRunEntry.getSuccessType(), equalTo(Optional.of(BuildRuleSuccessType.BUILT_LOCALLY))); // Clean the build directory, so that we need to go to cache. workspace.runBuckCommand("clean"); // 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=cache", target.toString()).assertSuccess(); BuckBuildLog.BuildLogEntry secondRunEntry = workspace.getBuildLog().getLogEntry(preprocessTarget); assertThat( secondRunEntry.getSuccessType(), equalTo(Optional.of(BuildRuleSuccessType.FETCHED_FROM_CACHE_MANIFEST_BASED))); // Also, make sure the original rule keys are actually different. assertThat(secondRunEntry.getRuleKey(), Matchers.not(equalTo(firstRunEntry.getRuleKey()))); } @Test public void parentDirectoryReferenceInSource() throws IOException { Assume.assumeFalse("parent directories do not work in sandbox mode", sandboxSource); workspace.writeContentsToPath( "[cxx]\n" + " sandbox_sources = " + sandboxSource + "\n" + "[project]\n check_package_boundary = false\n", ".buckconfig"); workspace.runBuckBuild("//parent_dir_ref:simple#default,static").assertSuccess(); } @Test public void langCompilerFlags() throws IOException { workspace.runBuckBuild("//:lang_compiler_flags#default,static").assertSuccess(); } @Test public void binaryBuildRuleTools() throws IOException { workspace .runBuckBuild( "-c", "cxx.cc=//:cc", "-c", "cxx.cc_type=gcc", "-c", "cxx.cpp=//:cc", "-c", "cxx.cpp_type=gcc", "-c", "cxx.cxx=//:cxx", "-c", "cxx.cxx_type=gcc", "-c", "cxx.cxxpp=//:cxx", "-c", "cxx.cxxpp_type=gcc", "//:simple#default,static") .assertSuccess(); } @Test public void ignoreVerifyHeaders() throws IOException { ProjectWorkspace.ProcessResult result = workspace.runBuckBuild("-c", "cxx.untracked_headers=ignore", "//:untracked_header"); if (sandboxSource) { result.assertFailure(); } else { result.assertSuccess(); } } @Test public void errorVerifyHeaders() throws IOException { ProjectWorkspace.ProcessResult result = null; HumanReadableException caughtException = null; try { result = workspace.runBuckBuild( "-c", "cxx.untracked_headers=error", "-c", "cxx.untracked_headers_whitelist=/usr/include/stdc-predef\\.h", "//:untracked_header"); result.assertFailure(); } catch (HumanReadableException e) { caughtException = e; } if (sandboxSource) { assertThat( result.getStderr(), Matchers.anyOf( // clang containsString("'untracked_header.h' file not found"), // gcc containsString("untracked_header.h: No such file or directory"))); } else { assertThat( caughtException.getHumanReadableErrorMessage(), containsString( "untracked_header.cpp: included an untracked header \"untracked_header.h\"")); } } @Test public void whitelistVerifyHeaders() throws IOException { ProjectWorkspace.ProcessResult result = workspace.runBuckBuild( "-c", "cxx.untracked_headers=error", "-c", "cxx.untracked_headers_whitelist=" + "/usr/include/stdc-predef\\.h, /usr/local/.*, untracked_.*.h", "//:untracked_header"); if (sandboxSource) { result.assertFailure(); } else { result.assertSuccess(); } } private BuildTarget getPreprocessTarget( CxxPlatform cxxPlatform, BuildTarget target, String source) { CxxSourceRuleFactory cxxSourceRuleFactory = CxxSourceRuleFactoryHelper.of(workspace.getDestPath(), target, cxxPlatform); return cxxSourceRuleFactory.createCompileBuildTarget(source); } @Test public void errorVerifyHeadersWithPrefixHeader() throws Exception { ProjectWorkspace.ProcessResult result = workspace.runBuckBuild( "-c", "cxx.untracked_headers=error", "-c", "cxx.untracked_headers_whitelist=" + "/usr/include/stdc-predef\\.h, untracked_header\\.h", "//:untracked_header_with_prefix_header"); result.assertSuccess(); } @Test public void verifyAppleFrameworksHeaders() throws IOException { assumeThat(Platform.detect(), is(Platform.MACOS)); ProjectWorkspace.ProcessResult result = workspace.runBuckBuild( "-c", "cxx.untracked_headers=error", "-c", "cxx.default_platform=iphonesimulator-x86_64", "//:with_xctest"); if (sandboxSource) { result.assertFailure(); } else { result.assertSuccess(); } } @Test public void canCompileSourcesFromOtherCells() throws IOException { workspace = TestDataHelper.createProjectWorkspaceForScenario( this, "cxx_preprocess_and_compile_can_compile_sources_from_other_cells", tmp); workspace.setUp(); workspace .runBuckBuild( "//:binary", "-c", "cxx.untracked_headers=error", "-c", "cxx.untracked_headers_whitelist=" + "/usr/.*") .assertSuccess(); } }