@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());
  }
  @Test
  public void testCreateBuildRule() throws Exception {
    // Set up a #halide-compiler rule, then set up a halide_library rule, and
    // check that the library rule depends on the compiler rule.
    BuildTarget compilerTarget =
        BuildTargetFactory.newInstance("//:rule")
            .withFlavors(HalideLibraryDescription.HALIDE_COMPILER_FLAVOR);
    BuildTarget libTarget = BuildTargetFactory.newInstance("//:rule");

    ProjectFilesystem filesystem = new FakeProjectFilesystem();
    HalideLibraryBuilder compilerBuilder = new HalideLibraryBuilder(compilerTarget);
    compilerBuilder.setSrcs(
        ImmutableSortedSet.of(SourceWithFlags.of(new FakeSourcePath("main.cpp"))));
    HalideLibraryBuilder libBuilder = new HalideLibraryBuilder(libTarget);
    TargetGraph targetGraph =
        TargetGraphFactory.newInstance(compilerBuilder.build(), libBuilder.build());
    BuildRuleResolver resolver =
        new BuildRuleResolver(targetGraph, new BuildTargetNodeToBuildRuleTransformer());
    CxxBinary compiler = (CxxBinary) compilerBuilder.build(resolver, filesystem, targetGraph);
    HalideLibrary lib = (HalideLibrary) libBuilder.build(resolver, filesystem, targetGraph);
    // Check that we picked up the implicit dependency on the #halide-compiler
    // version of the rule.
    assertEquals(lib.getDeps(), ImmutableSortedSet.<BuildRule>of(compiler));

    // Check that the library rule has the correct preprocessor input.
    CxxPlatform cxxPlatform = CxxLibraryBuilder.createDefaultPlatform();
    String headerName = "rule.h";
    Path headerPath = BuildTargets.getGenPath(libTarget, "%s/" + headerName);
    Path headerRoot =
        CxxDescriptionEnhancer.getHeaderSymlinkTreePath(
            libTarget, cxxPlatform.getFlavor(), HeaderVisibility.PUBLIC);
    assertEquals(
        CxxPreprocessorInput.builder()
            .addRules(
                CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
                    libTarget, cxxPlatform.getFlavor(), HeaderVisibility.PUBLIC))
            .setIncludes(
                CxxHeaders.builder()
                    .putNameToPathMap(
                        Paths.get(headerName), new BuildTargetSourcePath(libTarget, headerPath))
                    .putFullNameToPathMap(
                        headerRoot.resolve(headerName),
                        new BuildTargetSourcePath(libTarget, headerPath))
                    .build())
            .addSystemIncludeRoots(headerRoot)
            .build(),
        lib.getCxxPreprocessorInput(cxxPlatform, HeaderVisibility.PUBLIC));

    // Check that the library rule has the correct native linkable input.
    NativeLinkableInput input =
        lib.getNativeLinkableInput(cxxPlatform, Linker.LinkableDepType.STATIC);
    BuildRule buildRule =
        FluentIterable.from(input.getArgs())
            .transformAndConcat(Arg.getDepsFunction(new SourcePathResolver(resolver)))
            .get(0);
    assertTrue(buildRule instanceof HalideLibrary);
  }
 @Test
 public void buildTargetsWithDifferentFlavorsProduceDifferentDefaultSonames() {
   BuildTarget target1 = BuildTargetFactory.newInstance("//:rule#one");
   BuildTarget target2 = BuildTargetFactory.newInstance("//:rule#two");
   assertNotEquals(
       CxxDescriptionEnhancer.getDefaultSharedLibrarySoname(
           target1, CxxPlatformUtils.DEFAULT_PLATFORM),
       CxxDescriptionEnhancer.getDefaultSharedLibrarySoname(
           target2, CxxPlatformUtils.DEFAULT_PLATFORM));
 }
示例#4
0
 @Override
 public PythonPackageComponents getPythonPackageComponents(
     TargetGraph targetGraph, PythonPlatform pythonPlatform, CxxPlatform cxxPlatform) {
   if (headerOnly.apply(cxxPlatform)) {
     return PythonPackageComponents.of();
   }
   if (linkage == Linkage.STATIC) {
     return PythonPackageComponents.of();
   }
   if (!isPlatformSupported(cxxPlatform)) {
     return PythonPackageComponents.of();
   }
   ImmutableMap.Builder<Path, SourcePath> libs = ImmutableMap.builder();
   String sharedLibrarySoname =
       soname.or(
           CxxDescriptionEnhancer.getDefaultSharedLibrarySoname(getBuildTarget(), cxxPlatform));
   BuildRule sharedLibraryBuildRule =
       requireBuildRule(
           targetGraph, cxxPlatform.getFlavor(), CxxDescriptionEnhancer.SHARED_FLAVOR);
   libs.put(
       Paths.get(sharedLibrarySoname),
       new BuildTargetSourcePath(sharedLibraryBuildRule.getBuildTarget()));
   return PythonPackageComponents.of(
       /* modules */ ImmutableMap.<Path, SourcePath>of(),
       /* resources */ ImmutableMap.<Path, SourcePath>of(),
       /* nativeLibraries */ libs.build(),
       /* prebuiltLibraries */ ImmutableSet.<SourcePath>of(),
       /* zipSafe */ Optional.<Boolean>absent());
 }
  public CxxInferCapture createInferCaptureBuildRule(
      BuildTarget target, String name, CxxSource source, CxxInferTools inferTools) {
    Preconditions.checkArgument(CxxSourceTypes.isPreprocessableType(source.getType()));

    LOG.verbose("Creating preprocessed InferCapture build rule %s for %s", target, source);

    CxxInferCapture result =
        new CxxInferCapture(
            getParams()
                .copyWithChanges(
                    target,
                    new DepsBuilder().addPreprocessDeps().add(source),
                    Suppliers.ofInstance(ImmutableSortedSet.<BuildRule>of())),
            getPathResolver(),
            CxxToolFlags.copyOf(
                CxxSourceTypes.getPlatformPreprocessFlags(getCxxPlatform(), source.getType()),
                preprocessorFlags.getUnchecked(source.getType())),
            computeCompilerFlags(source.getType(), source.getFlags()),
            source.getPath(),
            source.getType(),
            getCompileOutputPath(target, name),
            getIncludeRoots(),
            getSystemIncludeRoots(),
            getHeaderMaps(),
            getFrameworks(),
            CxxDescriptionEnhancer.frameworkPathToSearchPath(getCxxPlatform(), getPathResolver()),
            getPrefixHeader(),
            inferTools,
            getCxxPlatform().getDebugPathSanitizer());
    getResolver().addToIndex(result);
    return result;
  }
  public static CxxHeaderSourceSpec requireLexYaccSources(
      BuildRuleParams params,
      BuildRuleResolver ruleResolver,
      SourcePathResolver pathResolver,
      CxxPlatform cxxPlatform,
      ImmutableMap<String, SourcePath> lexSources,
      ImmutableMap<String, SourcePath> yaccSources) {
    BuildTarget lexYaccTarget = createLexYaccSourcesBuildTarget(params.getBuildTarget());

    // Check the cache...
    Optional<BuildRule> rule = ruleResolver.getRuleOptional(lexYaccTarget);
    if (rule.isPresent()) {
      @SuppressWarnings("unchecked")
      ContainerBuildRule<CxxHeaderSourceSpec> containerRule =
          (ContainerBuildRule<CxxHeaderSourceSpec>) rule.get();
      return containerRule.get();
    }

    // Setup the rules to run lex/yacc.
    CxxHeaderSourceSpec lexYaccSources =
        CxxDescriptionEnhancer.createLexYaccBuildRules(
            params,
            ruleResolver,
            cxxPlatform,
            ImmutableList.<String>of(),
            lexSources,
            ImmutableList.<String>of(),
            yaccSources);

    ruleResolver.addToIndex(
        ContainerBuildRule.of(params, pathResolver, lexYaccTarget, lexYaccSources));

    return lexYaccSources;
  }
  public static HeaderSymlinkTree createHeaderSymlinkTree(
      BuildRuleParams params,
      BuildRuleResolver ruleResolver,
      SourcePathResolver pathResolver,
      CxxPlatform cxxPlatform,
      boolean includeLexYaccHeaders,
      ImmutableMap<String, SourcePath> lexSources,
      ImmutableMap<String, SourcePath> yaccSources,
      ImmutableMap<Path, SourcePath> headers,
      HeaderVisibility headerVisibility) {

    BuildTarget headerSymlinkTreeTarget =
        CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
            params.getBuildTarget(), cxxPlatform.getFlavor(), headerVisibility);
    Path headerSymlinkTreeRoot =
        CxxDescriptionEnhancer.getHeaderSymlinkTreePath(
            params.getBuildTarget(), cxxPlatform.getFlavor(), headerVisibility);
    Optional<Path> headerMapLocation = Optional.absent();
    if (cxxPlatform.getCpp().supportsHeaderMaps() && cxxPlatform.getCxxpp().supportsHeaderMaps()) {
      headerMapLocation =
          Optional.of(
              getHeaderMapPath(params.getBuildTarget(), cxxPlatform.getFlavor(), headerVisibility));
    }

    CxxHeaderSourceSpec lexYaccSources;
    if (includeLexYaccHeaders) {
      lexYaccSources =
          requireLexYaccSources(
              params, ruleResolver, pathResolver, cxxPlatform, lexSources, yaccSources);
    } else {
      lexYaccSources = CxxHeaderSourceSpec.builder().build();
    }

    return CxxPreprocessables.createHeaderSymlinkTreeBuildRule(
        pathResolver,
        headerSymlinkTreeTarget,
        params,
        headerSymlinkTreeRoot,
        headerMapLocation,
        ImmutableMap.<Path, SourcePath>builder()
            .putAll(headers)
            .putAll(lexYaccSources.getCxxHeaders())
            .build());
  }
 public static SymlinkTree buildNativeLibsSymlinkTreeRule(
     BuildRuleParams buildRuleParams, SourcePathResolver pathResolver, CxxPlatform cxxPlatform)
     throws NoSuchBuildTargetException {
   return CxxDescriptionEnhancer.createSharedLibrarySymlinkTree(
       buildRuleParams,
       pathResolver,
       cxxPlatform,
       buildRuleParams.getDeps(),
       Predicates.or(
           Predicates.instanceOf(NativeLinkable.class),
           Predicates.instanceOf(JavaLibrary.class)));
 }
  @Test
  public void nonTestLibraryDepDoesNotIncludePrivateHeadersOfLibrary() throws Exception {
    SourcePathResolver pathResolver = new SourcePathResolver(new BuildRuleResolver());

    BuildTarget libTarget = BuildTargetFactory.newInstance("//:lib");

    BuildRuleParams libParams = new FakeBuildRuleParamsBuilder(libTarget).build();
    FakeCxxLibrary libRule =
        new FakeCxxLibrary(
            libParams,
            pathResolver,
            BuildTargetFactory.newInstance("//:header"),
            BuildTargetFactory.newInstance("//:symlink"),
            Paths.get("symlink/tree/lib"),
            BuildTargetFactory.newInstance("//:privateheader"),
            BuildTargetFactory.newInstance("//:privatesymlink"),
            Paths.get("private/symlink/tree/lib"),
            new FakeBuildRule("//:archive", pathResolver),
            new FakeBuildRule("//:shared", pathResolver),
            Paths.get("output/path/lib.so"),
            "lib.so",
            // This library has no tests.
            ImmutableSortedSet.<BuildTarget>of());

    BuildTarget otherLibDepTarget = BuildTargetFactory.newInstance("//:other");
    BuildRuleParams otherLibDepParams =
        new FakeBuildRuleParamsBuilder(otherLibDepTarget)
            .setDeclaredDeps(ImmutableSortedSet.<BuildRule>of(libRule))
            .build();

    ImmutableList<CxxPreprocessorInput> otherInput =
        CxxDescriptionEnhancer.collectCxxPreprocessorInput(
            TargetGraph.EMPTY,
            otherLibDepParams,
            CxxPlatformUtils.DEFAULT_PLATFORM,
            ImmutableMultimap.<CxxSource.Type, String>of(),
            ImmutableList.<HeaderSymlinkTree>of(),
            ImmutableSet.<FrameworkPath>of(),
            CxxPreprocessables.getTransitiveCxxPreprocessorInput(
                TargetGraph.EMPTY,
                CxxPlatformUtils.DEFAULT_PLATFORM,
                FluentIterable.from(otherLibDepParams.getDeps())
                    .filter(Predicates.instanceOf(CxxPreprocessorDep.class))));

    assertThat(
        "Non-test rule with library dep should include public and not private headers",
        CxxPreprocessorInput.concat(otherInput).getIncludeRoots(),
        allOf(
            hasItem(Paths.get("symlink/tree/lib")),
            not(hasItem(Paths.get("private/symlink/tree/lib")))));
  }
示例#10
0
  private <A extends Arg> BuildRule getFlavoredBinaryRule(
      TargetGraph targetGraph,
      final BuildRuleParams params,
      final BuildRuleResolver resolver,
      A args) {
    // Cxx targets must have one Platform Flavor set otherwise nothing gets compiled.
    ImmutableSet<Flavor> flavors =
        params
            .getBuildTarget()
            .withoutFlavors(ImmutableSet.of(ReactNativeFlavors.DO_NOT_BUNDLE))
            .getFlavors();
    if (!cxxPlatformFlavorDomain.containsAnyOf(flavors)) {
      flavors =
          new ImmutableSet.Builder<Flavor>()
              .addAll(flavors)
              .add(defaultCxxPlatform.getFlavor())
              .build();
    }

    final TargetNode<?> binaryTargetNode = Preconditions.checkNotNull(targetGraph.get(args.binary));
    // If the binary target of the AppleBundle is an AppleLibrary then the build flavor
    // must be specified.
    if (binaryTargetNode.getDescription() instanceof AppleLibraryDescription
        && (Sets.intersection(
                    SUPPORTED_LIBRARY_FLAVORS, binaryTargetNode.getBuildTarget().getFlavors())
                .size()
            != 1)) {
      throw new HumanReadableException(
          "AppleExtension bundle [%s] must have exactly one of these flavors: [%s].",
          binaryTargetNode.getBuildTarget().toString(),
          Joiner.on(", ").join(SUPPORTED_LIBRARY_FLAVORS));
    }

    BuildRuleParams binaryRuleParams =
        new BuildRuleParams(
            args.binary,
            Suppliers.ofInstance(
                BuildRules.toBuildRulesFor(
                    params.getBuildTarget(), resolver, binaryTargetNode.getDeclaredDeps())),
            Suppliers.ofInstance(
                BuildRules.toBuildRulesFor(
                    params.getBuildTarget(), resolver, binaryTargetNode.getExtraDeps())),
            params.getProjectFilesystem(),
            params.getCellRoots(),
            params.getRuleKeyBuilderFactory());

    return CxxDescriptionEnhancer.requireBuildRule(
        targetGraph, binaryRuleParams, resolver, flavors.toArray(new Flavor[0]));
  }
  @Test
  public void libraryTestIncludesPrivateHeadersOfLibraryUnderTest() throws Exception {
    SourcePathResolver pathResolver = new SourcePathResolver(new BuildRuleResolver());

    BuildTarget libTarget = BuildTargetFactory.newInstance("//:lib");
    BuildTarget testTarget = BuildTargetFactory.newInstance("//:test");

    BuildRuleParams libParams = new FakeBuildRuleParamsBuilder(libTarget).build();
    FakeCxxLibrary libRule =
        new FakeCxxLibrary(
            libParams,
            pathResolver,
            BuildTargetFactory.newInstance("//:header"),
            BuildTargetFactory.newInstance("//:symlink"),
            Paths.get("symlink/tree/lib"),
            BuildTargetFactory.newInstance("//:privateheader"),
            BuildTargetFactory.newInstance("//:privatesymlink"),
            Paths.get("private/symlink/tree/lib"),
            new FakeBuildRule("//:archive", pathResolver),
            new FakeBuildRule("//:shared", pathResolver),
            Paths.get("output/path/lib.so"),
            "lib.so",
            // Ensure the test is listed as a dep of the lib.
            ImmutableSortedSet.of(testTarget));

    BuildRuleParams testParams =
        new FakeBuildRuleParamsBuilder(testTarget)
            .setDeclaredDeps(ImmutableSortedSet.<BuildRule>of(libRule))
            .build();

    ImmutableList<CxxPreprocessorInput> combinedInput =
        CxxDescriptionEnhancer.collectCxxPreprocessorInput(
            TargetGraph.EMPTY,
            testParams,
            CxxPlatformUtils.DEFAULT_PLATFORM,
            ImmutableMultimap.<CxxSource.Type, String>of(),
            ImmutableList.<HeaderSymlinkTree>of(),
            ImmutableSet.<FrameworkPath>of(),
            CxxPreprocessables.getTransitiveCxxPreprocessorInput(
                TargetGraph.EMPTY,
                CxxPlatformUtils.DEFAULT_PLATFORM,
                FluentIterable.from(testParams.getDeps())
                    .filter(Predicates.instanceOf(CxxPreprocessorDep.class))));

    assertThat(
        "Test of library should include both public and private headers",
        CxxPreprocessorInput.concat(combinedInput).getIncludeRoots(),
        hasItems(Paths.get("symlink/tree/lib"), Paths.get("private/symlink/tree/lib")));
  }
 @Override
 public PreprocessorDelegate load(@Nonnull PreprocessAndCompilePreprocessorDelegateKey key)
     throws Exception {
   return new PreprocessorDelegate(
       getPathResolver(),
       getCxxPlatform().getDebugPathSanitizer(),
       getParams().getProjectFilesystem().getRootPath(),
       CxxSourceTypes.getPreprocessor(getCxxPlatform(), key.getSourceType()),
       computePreprocessorFlags(key.getSourceType(), key.getSourceFlags()),
       getIncludeRoots(),
       getSystemIncludeRoots(),
       getHeaderMaps(),
       getFrameworks(),
       CxxDescriptionEnhancer.frameworkPathToSearchPath(getCxxPlatform(), getPathResolver()),
       getPrefixHeader(),
       getIncludes());
 }
示例#13
0
 /**
  * @return adds a the header {@link SymlinkTree} for the given rule to the {@link
  *     CxxPreprocessorInput}.
  */
 public static CxxPreprocessorInput.Builder addHeaderSymlinkTree(
     CxxPreprocessorInput.Builder builder,
     BuildTarget target,
     BuildRuleResolver ruleResolver,
     Flavor flavor,
     HeaderVisibility headerVisibility,
     IncludeType includeType)
     throws NoSuchBuildTargetException {
   BuildRule rule =
       ruleResolver.requireRule(
           BuildTarget.builder(target)
               .addFlavors(
                   flavor, CxxDescriptionEnhancer.getHeaderSymlinkTreeFlavor(headerVisibility))
               .build());
   Preconditions.checkState(
       rule instanceof HeaderSymlinkTree,
       "Attempt to add %s of type %s and class %s to %s",
       rule.getFullyQualifiedName(),
       rule.getType(),
       rule.getClass(),
       target);
   HeaderSymlinkTree symlinkTree = (HeaderSymlinkTree) rule;
   builder
       .addRules(symlinkTree.getBuildTarget())
       .setIncludes(
           CxxHeaders.builder()
               .setNameToPathMap(ImmutableSortedMap.copyOf(symlinkTree.getLinks()))
               .setFullNameToPathMap(ImmutableSortedMap.copyOf(symlinkTree.getFullLinks()))
               .build());
   switch (includeType) {
     case LOCAL:
       builder.addIncludeRoots(symlinkTree.getIncludePath());
       builder.addAllHeaderMaps(symlinkTree.getHeaderMap().asSet());
       break;
     case SYSTEM:
       builder.addSystemIncludeRoots(symlinkTree.getSystemIncludePath());
       break;
   }
   return builder;
 }
示例#14
0
 @Override
 public ImmutableMap<String, SourcePath> getSharedLibraries(
     TargetGraph targetGraph, CxxPlatform cxxPlatform) {
   if (headerOnly.apply(cxxPlatform)) {
     return ImmutableMap.of();
   }
   if (linkage == Linkage.STATIC) {
     return ImmutableMap.of();
   }
   if (!isPlatformSupported(cxxPlatform)) {
     return ImmutableMap.of();
   }
   ImmutableMap.Builder<String, SourcePath> libs = ImmutableMap.builder();
   String sharedLibrarySoname =
       soname.or(
           CxxDescriptionEnhancer.getDefaultSharedLibrarySoname(getBuildTarget(), cxxPlatform));
   BuildRule sharedLibraryBuildRule =
       requireBuildRule(
           targetGraph, cxxPlatform.getFlavor(), CxxDescriptionEnhancer.SHARED_FLAVOR);
   libs.put(
       sharedLibrarySoname, new BuildTargetSourcePath(sharedLibraryBuildRule.getBuildTarget()));
   return libs.build();
 }
  public static HeaderSymlinkTree requireHeaderSymlinkTree(
      BuildRuleParams params,
      BuildRuleResolver ruleResolver,
      SourcePathResolver pathResolver,
      CxxPlatform cxxPlatform,
      boolean includeLexYaccHeaders,
      ImmutableMap<String, SourcePath> lexSources,
      ImmutableMap<String, SourcePath> yaccSources,
      ImmutableMap<Path, SourcePath> headers,
      HeaderVisibility headerVisibility) {
    BuildTarget headerSymlinkTreeTarget =
        CxxDescriptionEnhancer.createHeaderSymlinkTreeTarget(
            params.getBuildTarget(), cxxPlatform.getFlavor(), headerVisibility);

    // Check the cache...
    Optional<BuildRule> rule = ruleResolver.getRuleOptional(headerSymlinkTreeTarget);
    if (rule.isPresent()) {
      Preconditions.checkState(rule.get() instanceof HeaderSymlinkTree);
      return (HeaderSymlinkTree) rule.get();
    }

    HeaderSymlinkTree symlinkTree =
        createHeaderSymlinkTree(
            params,
            ruleResolver,
            pathResolver,
            cxxPlatform,
            includeLexYaccHeaders,
            lexSources,
            yaccSources,
            headers,
            headerVisibility);

    ruleResolver.addToIndex(symlinkTree);

    return symlinkTree;
  }
  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());
  }
 @VisibleForTesting
 protected BuildTarget getExtensionTarget(BuildTarget target, Flavor platform) {
   return CxxDescriptionEnhancer.createSharedLibraryBuildTarget(target, platform);
 }
示例#18
0
  @Override
  public NativeLinkableInput getNativeLinkableInput(
      TargetGraph targetGraph, CxxPlatform cxxPlatform, Linker.LinkableDepType type) {

    if (!isPlatformSupported(cxxPlatform)) {
      return NativeLinkableInput.of();
    }

    if (headerOnly.apply(cxxPlatform)) {
      return NativeLinkableInput.of(
          ImmutableList.<SourcePath>of(),
          ImmutableList.<String>of(),
          Preconditions.checkNotNull(frameworks),
          ImmutableSet.<FrameworkPath>of());
    }

    // Build up the arguments used to link this library.  If we're linking the
    // whole archive, wrap the library argument in the necessary "ld" flags.
    final Pair<ImmutableList<String>, ImmutableSet<SourcePath>> flagsAndBuildInputs =
        exportedLinkerFlags.apply(cxxPlatform);
    ImmutableList.Builder<String> linkerArgsBuilder = ImmutableList.builder();
    linkerArgsBuilder.addAll(flagsAndBuildInputs.getFirst());

    final BuildRule libraryRule;
    if (type != Linker.LinkableDepType.SHARED || linkage == Linkage.STATIC) {
      libraryRule =
          requireBuildRule(
              targetGraph,
              cxxPlatform.getFlavor(),
              type == Linker.LinkableDepType.STATIC
                  ? CxxDescriptionEnhancer.STATIC_FLAVOR
                  : CxxDescriptionEnhancer.STATIC_PIC_FLAVOR);
      Path staticLibraryPath =
          CxxDescriptionEnhancer.getStaticLibraryPath(
              getBuildTarget(),
              cxxPlatform.getFlavor(),
              type == Linker.LinkableDepType.STATIC
                  ? CxxSourceRuleFactory.PicType.PDC
                  : CxxSourceRuleFactory.PicType.PIC);
      if (linkWhole) {
        Linker linker = cxxPlatform.getLd();
        linkerArgsBuilder.addAll(linker.linkWhole(staticLibraryPath.toString()));
      } else {
        linkerArgsBuilder.add(staticLibraryPath.toString());
      }
    } else {
      String sharedLibrarySoname =
          soname.or(
              CxxDescriptionEnhancer.getDefaultSharedLibrarySoname(
                  params.getBuildTarget(), cxxPlatform));
      Path sharedLibraryPath =
          CxxDescriptionEnhancer.getSharedLibraryPath(
              getBuildTarget(), sharedLibrarySoname, cxxPlatform);
      libraryRule =
          requireBuildRule(
              targetGraph, cxxPlatform.getFlavor(), CxxDescriptionEnhancer.SHARED_FLAVOR);
      linkerArgsBuilder.add(sharedLibraryPath.toString());
    }
    final ImmutableList<String> linkerArgs = linkerArgsBuilder.build();

    return NativeLinkableInput.of(
        ImmutableList.<SourcePath>builder()
            .add(new BuildTargetSourcePath(libraryRule.getBuildTarget()))
            .addAll(flagsAndBuildInputs.getSecond())
            .build(),
        linkerArgs,
        Preconditions.checkNotNull(frameworks),
        Preconditions.checkNotNull(libraries));
  }
  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));
  }
 @VisibleForTesting
 protected static BuildTarget getExtensionTarget(
     BuildTarget target, Flavor pythonPlatform, Flavor platform) {
   return CxxDescriptionEnhancer.createSharedLibraryBuildTarget(
       BuildTarget.builder(target).addFlavors(pythonPlatform).build(), platform);
 }
  private ImmutableList<com.facebook.buck.rules.args.Arg> getExtensionArgs(
      BuildRuleParams params,
      BuildRuleResolver ruleResolver,
      SourcePathResolver pathResolver,
      CxxPlatform cxxPlatform,
      Arg args)
      throws NoSuchBuildTargetException {

    // Extract all C/C++ sources from the constructor arg.
    ImmutableMap<String, CxxSource> srcs =
        CxxDescriptionEnhancer.parseCxxSources(
            params.getBuildTarget(), pathResolver, cxxPlatform, args);
    ImmutableMap<Path, SourcePath> headers =
        CxxDescriptionEnhancer.parseHeaders(
            params.getBuildTarget(), pathResolver, Optional.of(cxxPlatform), args);

    // Setup the header symlink tree and combine all the preprocessor input from this rule
    // and all dependencies.
    HeaderSymlinkTree headerSymlinkTree =
        CxxDescriptionEnhancer.requireHeaderSymlinkTree(
            params,
            ruleResolver,
            new SourcePathResolver(ruleResolver),
            cxxPlatform,
            headers,
            HeaderVisibility.PRIVATE);
    ImmutableList<CxxPreprocessorInput> cxxPreprocessorInput =
        CxxDescriptionEnhancer.collectCxxPreprocessorInput(
            params,
            cxxPlatform,
            CxxFlags.getLanguageFlags(
                args.preprocessorFlags,
                args.platformPreprocessorFlags,
                args.langPreprocessorFlags,
                cxxPlatform),
            ImmutableList.of(headerSymlinkTree),
            ImmutableSet.<FrameworkPath>of(),
            CxxPreprocessables.getTransitiveCxxPreprocessorInput(cxxPlatform, params.getDeps()));

    // Generate rule to build the object files.
    ImmutableMap<CxxPreprocessAndCompile, SourcePath> picObjects =
        CxxSourceRuleFactory.requirePreprocessAndCompileRules(
            params,
            ruleResolver,
            pathResolver,
            cxxBuckConfig,
            cxxPlatform,
            cxxPreprocessorInput,
            CxxFlags.getLanguageFlags(
                args.compilerFlags,
                args.platformCompilerFlags,
                args.langCompilerFlags,
                cxxPlatform),
            args.prefixHeader,
            cxxBuckConfig.getPreprocessMode(),
            srcs,
            CxxSourceRuleFactory.PicType.PIC);

    ImmutableList.Builder<com.facebook.buck.rules.args.Arg> argsBuilder = ImmutableList.builder();
    argsBuilder.addAll(
        StringArg.from(CxxFlags.getFlags(args.linkerFlags, args.platformLinkerFlags, cxxPlatform)));

    // Embed a origin-relative library path into the binary so it can find the shared libraries.
    argsBuilder.addAll(
        StringArg.from(
            Linkers.iXlinker(
                "-rpath",
                String.format("%s/", cxxPlatform.getLd().resolve(ruleResolver).libOrigin()))));

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

    return argsBuilder.build();
  }
示例#22
0
 public BuildRule requireBuildRule(TargetGraph targetGraph, Flavor... flavors) {
   return CxxDescriptionEnhancer.requireBuildRule(targetGraph, params, ruleResolver, flavors);
 }
  @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 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());
  }
  @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());
  }
  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);
  }