@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)); }
@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"))))); }
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()); }
/** * @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; }
@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); }
@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(); }
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); }