@Test
  public void resolveHeadersBehindSymlinkTreesInPreprocessedOutput() throws IOException {
    BuckConfig buckConfig = new FakeBuckConfig();
    CxxPlatform cxxPlatform = DefaultCxxPlatforms.build(new CxxBuckConfig(buckConfig));

    ProjectWorkspace workspace =
        TestDataHelper.createProjectWorkspaceForScenario(this, "resolved", tmp);
    workspace.setUp();

    workspace.writeContentsToPath("", "lib2.h");

    BuildTarget target = BuildTargetFactory.newInstance("//:bin");
    CxxSourceRuleFactory cxxSourceRuleFactory = CxxSourceRuleFactoryHelper.of(target, cxxPlatform);
    workspace.runBuckCommand("build", target.toString()).assertSuccess();

    // Verify that the preprocessed source contains no references to the symlink tree used to
    // setup the headers.
    BuildTarget ppTarget =
        cxxSourceRuleFactory.createPreprocessBuildTarget(
            "bin.cpp", CxxSource.Type.CXX, CxxSourceRuleFactory.PicType.PDC);
    Path output =
        cxxSourceRuleFactory.getPreprocessOutputPath(ppTarget, CxxSource.Type.CXX, "bin.cpp");
    String contents = workspace.getFileContents(output.toString());
    assertThat(contents, Matchers.not(Matchers.containsString(BuckConstant.SCRATCH_DIR)));
    assertThat(contents, Matchers.not(Matchers.containsString(BuckConstant.GEN_DIR)));
    assertThat(contents, Matchers.containsString("# 1 \"bin.h"));
    assertThat(contents, Matchers.containsString("# 1 \"lib1.h"));
    assertThat(contents, Matchers.containsString("# 1 \"lib2.h"));
  }
  @Test
  public void testInferCxxBinaryDepsCaching() throws IOException {
    assumeTrue(Platform.detect() != Platform.WINDOWS);
    ProjectWorkspace workspace = InferHelper.setupCxxInferWorkspace(this, tmp);
    workspace.enableDirCache(); // enable the cache

    CxxPlatform cxxPlatform = DefaultCxxPlatforms.build(new CxxBuckConfig(new FakeBuckConfig()));
    BuildTarget inputBuildTarget = BuildTargetFactory.newInstance("//foo:binary_with_deps");
    String inputBuildTargetName =
        inputBuildTarget.withFlavors(CxxInferEnhancer.INFER).getFullyQualifiedName();

    /*
     * Build the given target and check that it succeeds.
     */
    workspace.runBuckCommand("build", inputBuildTargetName).assertSuccess();

    /*
     * Check that building after clean will use the cache
     */
    workspace.runBuckCommand("clean").assertSuccess();
    workspace.runBuckCommand("build", inputBuildTargetName).assertSuccess();
    BuckBuildLog buildLog = workspace.getBuildLog();
    for (BuildTarget buildTarget : buildLog.getAllTargets()) {
      buildLog.assertTargetWasFetchedFromCache(buildTarget.toString());
    }

    /*
     * Check that if the file in the binary target changes, then all the deps will be fetched
     * from the cache
     */
    String sourceName = "src_with_deps.c";
    workspace.replaceFileContents("foo/" + sourceName, "10", "30");
    workspace.runBuckCommand("clean").assertSuccess();
    workspace.runBuckCommand("build", inputBuildTargetName).assertSuccess();
    buildLog = workspace.getBuildLog();

    CxxSourceRuleFactory cxxSourceRuleFactory =
        CxxSourceRuleFactoryHelper.of(inputBuildTarget, cxxPlatform);

    BuildTarget captureBuildTarget = cxxSourceRuleFactory.createInferCaptureBuildTarget(sourceName);

    // this is flavored, and denotes the analysis step (generates a local report)
    BuildTarget inferAnalysisTarget = inputBuildTarget.withFlavors(CxxInferEnhancer.INFER_ANALYZE);

    // this is the flavored version of the top level target (the one give in input to buck)
    BuildTarget inferReportTarget = inputBuildTarget.withFlavors(CxxInferEnhancer.INFER);

    String bt;
    for (BuildTarget buildTarget : buildLog.getAllTargets()) {
      bt = buildTarget.toString();
      if (bt.equals(inferAnalysisTarget.toString())
          || bt.equals(captureBuildTarget.toString())
          || bt.equals(inferReportTarget.toString())) {
        buildLog.assertTargetBuiltLocally(bt);
      } else {
        buildLog.assertTargetWasFetchedFromCache(buildTarget.toString());
      }
    }
  }
  @Test
  public void testSimpleCxxBinaryWithHeader() throws IOException {
    ProjectWorkspace workspace =
        TestDataHelper.createProjectWorkspaceForScenario(this, "simple", tmp);
    workspace.setUp();

    CxxPlatform cxxPlatform = DefaultCxxPlatforms.build(new CxxBuckConfig(new FakeBuckConfig()));
    BuildTarget target = BuildTargetFactory.newInstance("//foo:simple_with_header");
    CxxSourceRuleFactory cxxSourceRuleFactory = CxxSourceRuleFactoryHelper.of(target, cxxPlatform);
    BuildTarget binaryTarget = CxxDescriptionEnhancer.createCxxLinkTarget(target);
    String sourceName = "simple_with_header.cpp";
    String headerName = "simple_with_header.h";
    String headerFull = "foo/" + headerName;
    BuildTarget preprocessTarget =
        cxxSourceRuleFactory.createPreprocessBuildTarget(
            sourceName, CxxSource.Type.CXX, CxxSourceRuleFactory.PicType.PDC);
    BuildTarget compileTarget =
        cxxSourceRuleFactory.createCompileBuildTarget(sourceName, CxxSourceRuleFactory.PicType.PDC);
    BuildTarget headerSymlinkTreeTarget =
        CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
            target, cxxPlatform.getFlavor(), HeaderVisibility.PRIVATE);

    // Do a clean build, verify that it succeeds, and check that all expected targets built
    // successfully.
    workspace.runBuckCommand("build", target.toString()).assertSuccess();
    BuckBuildLog buildLog = workspace.getBuildLog();
    assertEquals(
        ImmutableSet.of(
            headerSymlinkTreeTarget, preprocessTarget, compileTarget, binaryTarget, target),
        buildLog.getAllTargets());
    buildLog.assertTargetBuiltLocally(headerSymlinkTreeTarget.toString());
    buildLog.assertTargetBuiltLocally(preprocessTarget.toString());
    buildLog.assertTargetBuiltLocally(compileTarget.toString());
    buildLog.assertTargetBuiltLocally(binaryTarget.toString());
    buildLog.assertTargetBuiltLocally(target.toString());

    // Clear for new build.
    workspace.resetBuildLogFile();

    // Update the source file.
    workspace.replaceFileContents(headerFull, "blah = 5", "blah = 6");

    // Check that running a build again makes the source get recompiled and the binary
    // re-linked, but does not cause the header rules to re-run.
    workspace.runBuckCommand("build", target.toString()).assertSuccess();
    buildLog = workspace.getBuildLog();
    assertEquals(
        ImmutableSet.of(
            headerSymlinkTreeTarget, preprocessTarget, compileTarget, binaryTarget, target),
        buildLog.getAllTargets());
    buildLog.assertTargetHadMatchingRuleKey(headerSymlinkTreeTarget.toString());
    buildLog.assertTargetBuiltLocally(preprocessTarget.toString());
    buildLog.assertTargetBuiltLocally(compileTarget.toString());
    assertThat(
        buildLog.getLogEntry(binaryTarget).getSuccessType().get(),
        Matchers.not(Matchers.equalTo(BuildRuleSuccessType.MATCHING_RULE_KEY)));
    buildLog.assertTargetBuiltLocally(target.toString());
  }
 private BuildTarget getPreprocessTarget(
     CxxPlatform cxxPlatform, BuildTarget target, String source, CxxSource.Type type) {
   CxxSourceRuleFactory cxxSourceRuleFactory =
       CxxSourceRuleFactoryHelper.of(workspace.getDestPath(), target, cxxPlatform);
   if (mode == CxxPreprocessMode.SEPARATE) {
     return cxxSourceRuleFactory.createPreprocessBuildTarget(
         source, type, CxxSourceRuleFactory.PicType.PDC);
   } else {
     return cxxSourceRuleFactory.createCompileBuildTarget(
         source, CxxSourceRuleFactory.PicType.PDC);
   }
 }
  @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)));
  }
  @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())));
  }
 public static ImmutableMap<CxxPreprocessAndCompile, SourcePath> requirePreprocessAndCompileRules(
     BuildRuleParams params,
     BuildRuleResolver resolver,
     SourcePathResolver pathResolver,
     CxxPlatform cxxPlatform,
     ImmutableList<CxxPreprocessorInput> cxxPreprocessorInput,
     ImmutableList<String> compilerFlags,
     Optional<SourcePath> prefixHeader,
     CxxPreprocessMode strategy,
     ImmutableMap<String, CxxSource> sources,
     PicType pic) {
   CxxSourceRuleFactory factory =
       CxxSourceRuleFactory.of(
           params,
           resolver,
           pathResolver,
           cxxPlatform,
           cxxPreprocessorInput,
           compilerFlags,
           prefixHeader,
           pic);
   return factory.requirePreprocessAndCompileRules(strategy, sources);
 }
  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());
  }
  public void doTestSimpleCxxBinaryBuilds(String preprocessMode, boolean expectPreprocessorOutput)
      throws IOException {
    ProjectWorkspace workspace =
        TestDataHelper.createProjectWorkspaceForScenario(this, "simple", tmp);
    workspace.setUp();
    workspace.writeContentsToPath(
        String.format("[cxx]\npreprocess_mode = %s\n", preprocessMode), ".buckconfig");
    CxxPlatform cxxPlatform = DefaultCxxPlatforms.build(new CxxBuckConfig(new FakeBuckConfig()));
    BuildTarget target = BuildTargetFactory.newInstance("//foo:simple");
    CxxSourceRuleFactory cxxSourceRuleFactory = CxxSourceRuleFactoryHelper.of(target, cxxPlatform);
    BuildTarget binaryTarget = CxxDescriptionEnhancer.createCxxLinkTarget(target);
    String sourceName = "simple.cpp";
    String sourceFull = "foo/" + sourceName;
    BuildTarget preprocessTarget =
        cxxSourceRuleFactory.createPreprocessBuildTarget(
            sourceName, CxxSource.Type.CXX, CxxSourceRuleFactory.PicType.PDC);
    BuildTarget compileTarget =
        cxxSourceRuleFactory.createCompileBuildTarget(sourceName, CxxSourceRuleFactory.PicType.PDC);
    BuildTarget headerSymlinkTreeTarget =
        CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
            target, cxxPlatform.getFlavor(), HeaderVisibility.PRIVATE);

    // Do a clean build, verify that it succeeds, and check that all expected targets built
    // successfully.
    workspace.runBuckCommand("build", target.toString()).assertSuccess();
    BuckBuildLog buildLog = workspace.getBuildLog();
    ImmutableSet<BuildTarget> expectedTargets =
        ImmutableSet.<BuildTarget>builder()
            .addAll(ImmutableSet.of(headerSymlinkTreeTarget, compileTarget, binaryTarget, target))
            .addAll(
                (expectPreprocessorOutput
                    ? ImmutableSet.of(preprocessTarget)
                    : ImmutableSet.<BuildTarget>of()))
            .build();

    assertEquals(expectedTargets, buildLog.getAllTargets());
    buildLog.assertTargetBuiltLocally(headerSymlinkTreeTarget.toString());
    if (expectPreprocessorOutput) {
      buildLog.assertTargetBuiltLocally(preprocessTarget.toString());
    }
    buildLog.assertTargetBuiltLocally(compileTarget.toString());
    buildLog.assertTargetBuiltLocally(binaryTarget.toString());
    buildLog.assertTargetBuiltLocally(target.toString());

    // Clear for new build.
    workspace.resetBuildLogFile();

    // Check that running a build again results in no builds since everything is up to
    // date.
    workspace.runBuckCommand("build", target.toString()).assertSuccess();
    buildLog = workspace.getBuildLog();
    assertEquals(ImmutableSet.of(target, binaryTarget), buildLog.getAllTargets());
    buildLog.assertTargetHadMatchingRuleKey(binaryTarget.toString());
    buildLog.assertTargetHadMatchingRuleKey(target.toString());

    // Clear for new build.
    workspace.resetBuildLogFile();

    // Update the source file.
    workspace.replaceFileContents(sourceFull, "{}", "{ return 0; }");

    // Check that running a build again makes the source get recompiled and the binary
    // re-linked, but does not cause the header rules to re-run.
    workspace.runBuckCommand("build", target.toString()).assertSuccess();
    buildLog = workspace.getBuildLog();
    assertEquals(expectedTargets, buildLog.getAllTargets());
    buildLog.assertTargetHadMatchingRuleKey(headerSymlinkTreeTarget.toString());
    if (expectPreprocessorOutput) {
      buildLog.assertTargetBuiltLocally(preprocessTarget.toString());
    }
    buildLog.assertTargetBuiltLocally(compileTarget.toString());
    assertThat(
        buildLog.getLogEntry(binaryTarget).getSuccessType().get(),
        Matchers.not(Matchers.equalTo(BuildRuleSuccessType.MATCHING_RULE_KEY)));
    buildLog.assertTargetBuiltLocally(target.toString());

    // Clear for new build.
    workspace.resetBuildLogFile();

    // Update the source file.
    workspace.replaceFileContents(sourceFull, "{ return 0; }", "won't compile");

    // Check that running a build again makes the source get recompiled and the binary
    // re-linked, but does not cause the header rules to re-run.
    workspace.runBuckCommand("build", target.toString()).assertFailure();
    buildLog = workspace.getBuildLog();
    assertEquals(expectedTargets, buildLog.getAllTargets());
    buildLog.assertTargetHadMatchingRuleKey(headerSymlinkTreeTarget.toString());
    if (expectPreprocessorOutput) {
      buildLog.assertTargetBuiltLocally(preprocessTarget.toString());
    }
    assertThat(
        buildLog.getLogEntry(binaryTarget).getStatus(), Matchers.equalTo(BuildRuleStatus.CANCELED));
    assertThat(
        buildLog.getLogEntry(target).getStatus(), Matchers.equalTo(BuildRuleStatus.CANCELED));
  }
  @Test
  public void testInferCxxBinaryWithDeps() throws IOException {
    assumeTrue(Platform.detect() != Platform.WINDOWS);
    ProjectWorkspace workspace = InferHelper.setupCxxInferWorkspace(this, tmp);

    CxxPlatform cxxPlatform = DefaultCxxPlatforms.build(new CxxBuckConfig(new FakeBuckConfig()));
    BuildTarget inputBuildTarget = BuildTargetFactory.newInstance("//foo:binary_with_deps");
    String inputBuildTargetName =
        inputBuildTarget.withFlavors(CxxInferEnhancer.INFER).getFullyQualifiedName();

    /*
     * Build the given target and check that it succeeds.
     */
    workspace.runBuckCommand("build", inputBuildTargetName).assertSuccess();

    /*
     * Check that all the required build targets have been generated.
     */
    String sourceName = "src_with_deps.c";
    CxxSourceRuleFactory cxxSourceRuleFactory =
        CxxSourceRuleFactoryHelper.of(inputBuildTarget, cxxPlatform);
    // 1. create the targets of binary_with_deps
    // this is unflavored, but bounded to the InferCapture build rule
    BuildTarget topCaptureBuildTarget =
        cxxSourceRuleFactory.createInferCaptureBuildTarget(sourceName);

    // this is unflavored, but necessary to run the compiler successfully
    BuildTarget topHeaderSymlinkTreeTarget =
        CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
            inputBuildTarget, cxxPlatform.getFlavor(), HeaderVisibility.PRIVATE);

    // this is flavored, and denotes the analysis step (generates a local report)
    BuildTarget topInferAnalysisTarget =
        inputBuildTarget.withFlavors(CxxInferEnhancer.INFER_ANALYZE);

    // this is flavored and corresponds to the top level target (the one give in input to buck)
    BuildTarget topInferReportTarget = inputBuildTarget.withFlavors(CxxInferEnhancer.INFER);

    // 2. create the targets of dep_one
    BuildTarget depOneBuildTarget = BuildTargetFactory.newInstance("//foo:dep_one");
    String depOneSourceName = "dep_one.c";
    String depOneSourceFull = "foo/" + depOneSourceName;
    CxxSourceRuleFactory depOneSourceRuleFactory =
        CxxSourceRuleFactoryHelper.of(depOneBuildTarget, cxxPlatform);

    BuildTarget depOneCaptureBuildTarget =
        depOneSourceRuleFactory.createInferCaptureBuildTarget(depOneSourceName);

    BuildTarget depOneHeaderSymlinkTreeTarget =
        CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
            depOneBuildTarget, cxxPlatform.getFlavor(), HeaderVisibility.PRIVATE);

    BuildTarget depOneExportedHeaderSymlinkTreeTarget =
        CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
            depOneBuildTarget, cxxPlatform.getFlavor(), HeaderVisibility.PUBLIC);

    BuildTarget depOneInferAnalysisTarget =
        depOneCaptureBuildTarget.withFlavors(
            cxxPlatform.getFlavor(), CxxInferEnhancer.INFER_ANALYZE);

    // 3. create the targets of dep_two
    BuildTarget depTwoBuildTarget = BuildTargetFactory.newInstance("//foo:dep_two");
    CxxSourceRuleFactory depTwoSourceRuleFactory =
        CxxSourceRuleFactoryHelper.of(depTwoBuildTarget, cxxPlatform);

    BuildTarget depTwoCaptureBuildTarget =
        depTwoSourceRuleFactory.createInferCaptureBuildTarget("dep_two.c");

    BuildTarget depTwoHeaderSymlinkTreeTarget =
        CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
            depTwoBuildTarget, cxxPlatform.getFlavor(), HeaderVisibility.PRIVATE);

    BuildTarget depTwoExportedHeaderSymlinkTreeTarget =
        CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
            depTwoBuildTarget, cxxPlatform.getFlavor(), HeaderVisibility.PUBLIC);

    BuildTarget depTwoInferAnalysisTarget =
        depTwoCaptureBuildTarget.withFlavors(
            cxxPlatform.getFlavor(), CxxInferEnhancer.INFER_ANALYZE);

    // Check all the targets are in the buildLog
    assertEquals(
        ImmutableSet.of(
            topCaptureBuildTarget,
            topHeaderSymlinkTreeTarget,
            topInferAnalysisTarget,
            topInferReportTarget,
            depOneCaptureBuildTarget,
            depOneHeaderSymlinkTreeTarget,
            depOneExportedHeaderSymlinkTreeTarget,
            depOneInferAnalysisTarget,
            depTwoCaptureBuildTarget,
            depTwoHeaderSymlinkTreeTarget,
            depTwoExportedHeaderSymlinkTreeTarget,
            depTwoInferAnalysisTarget),
        workspace.getBuildLog().getAllTargets());

    /*
     * Check that running a build again results in no builds since nothing has changed.
     */
    workspace.resetBuildLogFile(); // clear for new build
    workspace.runBuckCommand("build", inputBuildTargetName).assertSuccess();
    BuckBuildLog buildLog = workspace.getBuildLog();
    assertEquals(ImmutableSet.of(topInferReportTarget), buildLog.getAllTargets());
    buildLog.assertTargetHadMatchingRuleKey(topInferReportTarget.toString());

    /*
     * Check that if a library source file changes then the capture/analysis rules run again on
     * the main target and on dep_one only.
     */
    workspace.resetBuildLogFile();
    workspace.replaceFileContents(depOneSourceFull, "flag > 0", "flag < 0");
    workspace.runBuckCommand("build", inputBuildTargetName).assertSuccess();
    buildLog = workspace.getBuildLog();
    assertEquals(
        ImmutableSet.of(
            topInferAnalysisTarget, // analysis runs again
            topInferReportTarget, // report runs again
            topCaptureBuildTarget, // cached
            depTwoInferAnalysisTarget, // cached
            depOneCaptureBuildTarget, // capture of the changed file runs again
            depOneExportedHeaderSymlinkTreeTarget, // cached
            depOneHeaderSymlinkTreeTarget, // cached
            depOneInferAnalysisTarget), // analysis of the library runs again
        buildLog.getAllTargets());
    buildLog.assertTargetBuiltLocally(topInferAnalysisTarget.toString());
    buildLog.assertTargetBuiltLocally(topInferReportTarget.toString());
    buildLog.assertTargetHadMatchingRuleKey(topCaptureBuildTarget.toString());
    buildLog.assertTargetHadMatchingRuleKey(depTwoInferAnalysisTarget.toString());
    buildLog.assertTargetBuiltLocally(depOneCaptureBuildTarget.toString());
    buildLog.assertTargetHadMatchingRuleKey(depOneExportedHeaderSymlinkTreeTarget.toString());
    buildLog.assertTargetHadMatchingRuleKey(depOneHeaderSymlinkTreeTarget.toString());
    buildLog.assertTargetBuiltLocally(depOneInferAnalysisTarget.toString());
  }
  @Test
  public void testInferCxxBinaryWithoutDeps() throws IOException {
    assumeTrue(Platform.detect() != Platform.WINDOWS);
    ProjectWorkspace workspace = InferHelper.setupCxxInferWorkspace(this, tmp);

    CxxPlatform cxxPlatform = DefaultCxxPlatforms.build(new CxxBuckConfig(new FakeBuckConfig()));
    BuildTarget inputBuildTarget = BuildTargetFactory.newInstance("//foo:simple");
    String inputBuildTargetName =
        inputBuildTarget.withFlavors(CxxInferEnhancer.INFER).getFullyQualifiedName();

    /*
     * Build the given target and check that it succeeds.
     */
    workspace.runBuckCommand("build", inputBuildTargetName).assertSuccess();

    /*
     * Check that all the required build targets have been generated.
     */
    String sourceName = "simple.cpp";
    String sourceFull = "foo/" + sourceName;

    CxxSourceRuleFactory cxxSourceRuleFactory =
        CxxSourceRuleFactoryHelper.of(inputBuildTarget, cxxPlatform);
    // this is unflavored, but bounded to the InferCapture build rule
    BuildTarget captureBuildTarget = cxxSourceRuleFactory.createInferCaptureBuildTarget(sourceName);
    // this is unflavored, but necessary to run the compiler successfully
    BuildTarget headerSymlinkTreeTarget =
        CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
            inputBuildTarget, cxxPlatform.getFlavor(), HeaderVisibility.PRIVATE);
    // this is flavored, and denotes the analysis step (generates a local report)
    BuildTarget inferAnalysisTarget = inputBuildTarget.withFlavors(CxxInferEnhancer.INFER_ANALYZE);

    // this is flavored and corresponds to the top level target (the one give in input to buck)
    BuildTarget inferReportTarget = inputBuildTarget.withFlavors(CxxInferEnhancer.INFER);

    ImmutableSet<BuildTarget> expectedTargets =
        ImmutableSet.<BuildTarget>builder()
            .addAll(
                ImmutableSet.of(
                    headerSymlinkTreeTarget,
                    captureBuildTarget,
                    inferAnalysisTarget,
                    inferReportTarget))
            .build();

    BuckBuildLog buildLog = workspace.getBuildLog();
    assertEquals(expectedTargets, buildLog.getAllTargets());
    buildLog.assertTargetBuiltLocally(headerSymlinkTreeTarget.toString());
    buildLog.assertTargetBuiltLocally(captureBuildTarget.toString());
    buildLog.assertTargetBuiltLocally(inferAnalysisTarget.toString());
    buildLog.assertTargetBuiltLocally(inferReportTarget.toString());

    /*
     * Check that running a build again results in no builds since nothing has changed.
     */
    workspace.resetBuildLogFile(); // clear for new build
    workspace.runBuckCommand("build", inputBuildTargetName).assertSuccess();
    buildLog = workspace.getBuildLog();
    assertEquals(ImmutableSet.of(inferReportTarget), buildLog.getAllTargets());
    buildLog.assertTargetHadMatchingRuleKey(inferReportTarget.toString());

    /*
     * Check that changing the source file results in running the capture/analysis rules again.
     */
    workspace.resetBuildLogFile();
    workspace.replaceFileContents(sourceFull, "*s = 42;", "");
    workspace.runBuckCommand("build", inputBuildTargetName).assertSuccess();
    buildLog = workspace.getBuildLog();
    assertEquals(expectedTargets, buildLog.getAllTargets());
    buildLog.assertTargetBuiltLocally(captureBuildTarget.toString());
    buildLog.assertTargetBuiltLocally(inferAnalysisTarget.toString());
    buildLog.assertTargetHadMatchingRuleKey(headerSymlinkTreeTarget.toString());
  }
  public static CxxLinkAndCompileRules createBuildRulesForCxxBinary(
      TargetGraph targetGraph,
      BuildRuleParams params,
      BuildRuleResolver resolver,
      CxxPlatform cxxPlatform,
      ImmutableMap<String, CxxSource> srcs,
      ImmutableMap<Path, SourcePath> headers,
      ImmutableMap<String, SourcePath> lexSrcs,
      ImmutableMap<String, SourcePath> yaccSrcs,
      CxxPreprocessMode preprocessMode,
      Linker.LinkableDepType linkStyle,
      Optional<ImmutableList<String>> preprocessorFlags,
      Optional<PatternMatchedCollection<ImmutableList<String>>> platformPreprocessorFlags,
      Optional<ImmutableMap<CxxSource.Type, ImmutableList<String>>> langPreprocessorFlags,
      Optional<ImmutableSortedSet<FrameworkPath>> frameworks,
      Optional<ImmutableList<String>> compilerFlags,
      Optional<PatternMatchedCollection<ImmutableList<String>>> platformCompilerFlags,
      Optional<SourcePath> prefixHeader,
      Optional<ImmutableList<String>> linkerFlags,
      Optional<PatternMatchedCollection<ImmutableList<String>>> platformLinkerFlags,
      Optional<Linker.CxxRuntimeType> cxxRuntimeType) {
    SourcePathResolver sourcePathResolver = new SourcePathResolver(resolver);
    Path linkOutput = getLinkOutputPath(params.getBuildTarget());
    ImmutableList.Builder<Arg> argsBuilder = ImmutableList.builder();
    CommandTool.Builder executableBuilder = new CommandTool.Builder();

    // Setup the rules to run lex/yacc.
    CxxHeaderSourceSpec lexYaccSources =
        requireLexYaccSources(params, resolver, sourcePathResolver, cxxPlatform, lexSrcs, yaccSrcs);

    // Setup the header symlink tree and combine all the preprocessor input from this rule
    // and all dependencies.
    HeaderSymlinkTree headerSymlinkTree =
        requireHeaderSymlinkTree(
            params,
            resolver,
            sourcePathResolver,
            cxxPlatform,
            /* includeLexYaccHeaders */ true,
            lexSrcs,
            yaccSrcs,
            headers,
            HeaderVisibility.PRIVATE);
    ImmutableList<CxxPreprocessorInput> cxxPreprocessorInput =
        collectCxxPreprocessorInput(
            targetGraph,
            params,
            cxxPlatform,
            CxxFlags.getLanguageFlags(
                preprocessorFlags, platformPreprocessorFlags, langPreprocessorFlags, cxxPlatform),
            ImmutableList.of(headerSymlinkTree),
            getFrameworkSearchPaths(frameworks, cxxPlatform, new SourcePathResolver(resolver)),
            CxxPreprocessables.getTransitiveCxxPreprocessorInput(
                targetGraph,
                cxxPlatform,
                FluentIterable.from(params.getDeps())
                    .filter(Predicates.instanceOf(CxxPreprocessorDep.class))));

    // The complete list of input sources.
    ImmutableMap<String, CxxSource> sources =
        ImmutableMap.<String, CxxSource>builder()
            .putAll(srcs)
            .putAll(lexYaccSources.getCxxSources())
            .build();

    // Generate and add all the build rules to preprocess and compile the source to the
    // resolver and get the `SourcePath`s representing the generated object files.
    ImmutableMap<CxxPreprocessAndCompile, SourcePath> objects =
        CxxSourceRuleFactory.requirePreprocessAndCompileRules(
            params,
            resolver,
            sourcePathResolver,
            cxxPlatform,
            cxxPreprocessorInput,
            CxxFlags.getFlags(compilerFlags, platformCompilerFlags, cxxPlatform),
            prefixHeader,
            preprocessMode,
            sources,
            linkStyle == Linker.LinkableDepType.STATIC
                ? CxxSourceRuleFactory.PicType.PDC
                : CxxSourceRuleFactory.PicType.PIC);

    // Build up the linker flags, which support macro expansion.
    ImmutableList<String> resolvedLinkerFlags =
        CxxFlags.getFlags(linkerFlags, platformLinkerFlags, cxxPlatform);
    argsBuilder.addAll(
        FluentIterable.from(resolvedLinkerFlags)
            .transform(
                MacroArg.toMacroArgFunction(
                    MACRO_HANDLER,
                    params.getBuildTarget(),
                    params.getCellRoots(),
                    resolver,
                    params.getProjectFilesystem())));

    // Special handling for dynamically linked binaries.
    if (linkStyle == Linker.LinkableDepType.SHARED) {

      // Create a symlink tree with for all shared libraries needed by this binary.
      SymlinkTree sharedLibraries =
          resolver.addToIndex(
              createSharedLibrarySymlinkTree(
                  targetGraph,
                  params,
                  sourcePathResolver,
                  cxxPlatform,
                  Predicates.instanceOf(NativeLinkable.class)));

      // 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/%s",
                      cxxPlatform.getLd().origin(),
                      linkOutput.getParent().relativize(sharedLibraries.getRoot()).toString()))));

      // Add all the shared libraries and the symlink tree as inputs to the tool that represents
      // this binary, so that users can attach the proper deps.
      executableBuilder.addDep(sharedLibraries);
      executableBuilder.addInputs(sharedLibraries.getLinks().values());
    }

    // Add object files into the args.
    argsBuilder.addAll(SourcePathArg.from(sourcePathResolver, objects.values()));

    // Generate the final link rule.  We use the top-level target as the link rule's
    // target, so that it corresponds to the actual binary we build.
    CxxLink cxxLink =
        CxxLinkableEnhancer.createCxxLinkableBuildRule(
            targetGraph,
            cxxPlatform,
            params,
            sourcePathResolver,
            createCxxLinkTarget(params.getBuildTarget()),
            Linker.LinkType.EXECUTABLE,
            Optional.<String>absent(),
            linkOutput,
            argsBuilder.build(),
            linkStyle,
            params.getDeps(),
            cxxRuntimeType,
            Optional.<SourcePath>absent(),
            ImmutableSet.<BuildTarget>of());
    resolver.addToIndex(cxxLink);

    // Add the output of the link as the lone argument needed to invoke this binary as a tool.
    executableBuilder.addArg(new BuildTargetSourcePath(cxxLink.getBuildTarget()));

    return new CxxLinkAndCompileRules(
        cxxLink, ImmutableSortedSet.copyOf(objects.keySet()), executableBuilder.build());
  }