Java tutorial
/* * Copyright 2017-present Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. You may obtain * a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package com.facebook.buck.cxx; import com.facebook.buck.core.cell.CellPathResolver; import com.facebook.buck.core.exceptions.HumanReadableException; import com.facebook.buck.core.model.BuildTarget; import com.facebook.buck.core.model.Flavor; import com.facebook.buck.core.model.FlavorDomain; import com.facebook.buck.core.rules.ActionGraphBuilder; import com.facebook.buck.core.rules.BuildRule; import com.facebook.buck.core.rules.BuildRuleParams; import com.facebook.buck.core.rules.SourcePathRuleFinder; import com.facebook.buck.core.rules.impl.NoopBuildRule; import com.facebook.buck.core.sourcepath.SourcePath; import com.facebook.buck.core.sourcepath.resolver.SourcePathResolver; import com.facebook.buck.core.sourcepath.resolver.impl.DefaultSourcePathResolver; import com.facebook.buck.core.toolchain.ToolchainProvider; import com.facebook.buck.cxx.toolchain.CxxBuckConfig; import com.facebook.buck.cxx.toolchain.CxxPlatform; import com.facebook.buck.cxx.toolchain.CxxPlatformsProvider; import com.facebook.buck.cxx.toolchain.HeaderMode; import com.facebook.buck.cxx.toolchain.HeaderSymlinkTree; import com.facebook.buck.cxx.toolchain.HeaderVisibility; import com.facebook.buck.cxx.toolchain.InferBuckConfig; import com.facebook.buck.cxx.toolchain.LinkerMapMode; import com.facebook.buck.cxx.toolchain.PicType; import com.facebook.buck.cxx.toolchain.SharedLibraryInterfaceParams; import com.facebook.buck.cxx.toolchain.linker.Linker; import com.facebook.buck.cxx.toolchain.linker.Linker.LinkType; import com.facebook.buck.cxx.toolchain.nativelink.NativeLinkTarget; import com.facebook.buck.cxx.toolchain.nativelink.NativeLinkTargetMode; import com.facebook.buck.cxx.toolchain.nativelink.NativeLinkable; import com.facebook.buck.cxx.toolchain.nativelink.NativeLinkableInput; import com.facebook.buck.io.filesystem.ProjectFilesystem; import com.facebook.buck.rules.args.Arg; import com.facebook.buck.rules.args.SanitizedArg; import com.facebook.buck.rules.args.SourcePathArg; import com.facebook.buck.rules.args.StringArg; import com.facebook.buck.rules.coercer.FrameworkPath; import com.facebook.buck.rules.macros.StringWithMacros; import com.facebook.buck.util.RichStream; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Multimaps; import java.nio.file.Path; import java.util.Map; import java.util.Optional; import java.util.function.Predicate; import java.util.function.Supplier; public class CxxLibraryFactory { private final ToolchainProvider toolchainProvider; private final CxxBuckConfig cxxBuckConfig; private final InferBuckConfig inferBuckConfig; public CxxLibraryFactory(ToolchainProvider toolchainProvider, CxxBuckConfig cxxBuckConfig, InferBuckConfig inferBuckConfig) { this.toolchainProvider = toolchainProvider; this.cxxBuckConfig = cxxBuckConfig; this.inferBuckConfig = inferBuckConfig; } public BuildRule createBuildRule(BuildTarget buildTarget, ProjectFilesystem projectFilesystem, BuildRuleParams metadataRuleParams, ActionGraphBuilder graphBuilder, CellPathResolver cellRoots, CxxLibraryDescriptionArg args, Optional<Linker.LinkableDepType> linkableDepType, Optional<SourcePath> bundleLoader, ImmutableSet<BuildTarget> blacklist, ImmutableSortedSet<BuildTarget> extraDeps, CxxLibraryDescription.TransitiveCxxPreprocessorInputFunction transitiveCxxPreprocessorInputFunction, Optional<CxxLibraryDescriptionDelegate> delegate) { CxxPlatformsProvider cxxPlatformsProvider = getCxxPlatformsProvider(); FlavorDomain<CxxPlatform> cxxPlatforms = cxxPlatformsProvider.getResolvedCxxPlatforms(graphBuilder); Flavor defaultCxxFlavor = cxxPlatformsProvider.getDefaultUnresolvedCxxPlatform().getFlavor(); // See if we're building a particular "type" and "platform" of this library, and if so, extract // them from the flavors attached to the build target. Optional<Map.Entry<Flavor, CxxLibraryDescription.Type>> type = CxxLibraryDescription .getLibType(buildTarget); Optional<CxxPlatform> platform = cxxPlatforms.getValue(buildTarget); CxxDeps cxxDeps = CxxDeps.builder().addDeps(args.getCxxDeps()).addDeps(extraDeps).build(); Supplier<CxxPlatform> cxxPlatformOrDefaultSupplier = () -> platform .orElse(cxxPlatforms.getValue(args.getDefaultPlatform().orElse(defaultCxxFlavor))); if (buildTarget.getFlavors().contains(CxxCompilationDatabase.COMPILATION_DATABASE)) { CxxPlatform cxxPlatformOrDefault = cxxPlatformOrDefaultSupplier.get(); // XXX: This needs bundleLoader for tests.. SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(graphBuilder); SourcePathResolver sourcePathResolver = DefaultSourcePathResolver.from(ruleFinder); // TODO(T21900763): We should be using `requireObjects` instead but those would not // necessarily be `CxxPreprocessAndCompile` rules (e.g., Swift in `apple_library`). ImmutableMap<CxxPreprocessAndCompile, SourcePath> objects = requireCxxObjects( buildTarget.withoutFlavors(CxxCompilationDatabase.COMPILATION_DATABASE), projectFilesystem, graphBuilder, sourcePathResolver, ruleFinder, cellRoots, cxxBuckConfig, cxxPlatformOrDefault, cxxPlatformOrDefault.getPicTypeForSharedLinking(), args, cxxDeps.get(graphBuilder, cxxPlatformOrDefault), transitiveCxxPreprocessorInputFunction, delegate); return CxxCompilationDatabase.createCompilationDatabase(buildTarget, projectFilesystem, objects.keySet()); } else if (buildTarget.getFlavors().contains(CxxCompilationDatabase.UBER_COMPILATION_DATABASE)) { return CxxDescriptionEnhancer .createUberCompilationDatabase( platform.isPresent() ? buildTarget : buildTarget.withAppendedFlavors( args.getDefaultPlatform().orElse(defaultCxxFlavor)), projectFilesystem, graphBuilder); } else if (CxxInferEnhancer.INFER_FLAVOR_DOMAIN.containsAnyOf(buildTarget.getFlavors())) { return CxxInferEnhancer.requireInferRule(buildTarget, projectFilesystem, graphBuilder, cellRoots, cxxBuckConfig, cxxPlatformOrDefaultSupplier.get(), args, inferBuckConfig); } else if (type.isPresent() && !platform.isPresent()) { BuildTarget untypedBuildTarget = CxxLibraryDescription.getUntypedBuildTarget(buildTarget); switch (type.get().getValue()) { case EXPORTED_HEADERS: Optional<HeaderMode> mode = CxxLibraryDescription.HEADER_MODE.getValue(buildTarget); if (mode.isPresent()) { return createExportedHeaderSymlinkTreeBuildRule(untypedBuildTarget, projectFilesystem, graphBuilder, mode.get(), args); } break; // $CASES-OMITTED$ default: } } else if (type.isPresent() && platform.isPresent()) { // If we *are* building a specific type of this lib, call into the type specific // rule builder methods. BuildTarget untypedBuildTarget = CxxLibraryDescription.getUntypedBuildTarget(buildTarget); switch (type.get().getValue()) { case HEADERS: return createHeaderSymlinkTreeBuildRule(untypedBuildTarget, projectFilesystem, graphBuilder, platform.get(), args); case EXPORTED_HEADERS: return createExportedPlatformHeaderSymlinkTreeBuildRule(untypedBuildTarget, projectFilesystem, graphBuilder, platform.get(), args); case SHARED: return createSharedLibraryBuildRule(untypedBuildTarget, projectFilesystem, graphBuilder, cellRoots, cxxBuckConfig, platform.get(), args, cxxDeps.get(graphBuilder, platform.get()), Linker.LinkType.SHARED, linkableDepType.orElse(Linker.LinkableDepType.SHARED), Optional.empty(), blacklist, transitiveCxxPreprocessorInputFunction, delegate); case SHARED_INTERFACE: return createSharedLibraryInterface(untypedBuildTarget, projectFilesystem, graphBuilder, platform.get(), cxxBuckConfig.isIndependentSharedLibraryInterfaces()); case MACH_O_BUNDLE: return createSharedLibraryBuildRule(untypedBuildTarget, projectFilesystem, graphBuilder, cellRoots, cxxBuckConfig, platform.get(), args, cxxDeps.get(graphBuilder, platform.get()), Linker.LinkType.MACH_O_BUNDLE, linkableDepType.orElse(Linker.LinkableDepType.SHARED), bundleLoader, blacklist, transitiveCxxPreprocessorInputFunction, delegate); case STATIC: return createStaticLibraryBuildRule(untypedBuildTarget, projectFilesystem, graphBuilder, cellRoots, cxxBuckConfig, platform.get(), args, cxxDeps.get(graphBuilder, platform.get()), PicType.PDC, transitiveCxxPreprocessorInputFunction, delegate); case STATIC_PIC: return createStaticLibraryBuildRule(untypedBuildTarget, projectFilesystem, graphBuilder, cellRoots, cxxBuckConfig, platform.get(), args, cxxDeps.get(graphBuilder, platform.get()), PicType.PIC, transitiveCxxPreprocessorInputFunction, delegate); } throw new RuntimeException("unhandled library build type"); } boolean hasObjectsForAnyPlatform = !args.getSrcs().isEmpty(); Predicate<CxxPlatform> hasObjects; if (hasObjectsForAnyPlatform) { hasObjects = x -> true; } else { hasObjects = input -> !args.getPlatformSrcs().getMatchingValues(input.getFlavor().toString()).isEmpty(); } // Otherwise, we return the generic placeholder of this library, that dependents can use // get the real build rules via querying the action graph. return new CxxLibrary(buildTarget, projectFilesystem, metadataRuleParams, args.getPrivateCxxDeps(), args.getExportedCxxDeps(), hasObjects.negate(), (input, graphBuilderInner) -> { ImmutableList<Arg> delegateExportedLinkerFlags = delegate .map(d -> d.getAdditionalExportedLinkerFlags(buildTarget, graphBuilderInner, input)) .orElse(ImmutableList.of()); ImmutableList<StringWithMacros> flags = CxxFlags.getFlagsWithMacrosWithPlatformMacroExpansion( args.getExportedLinkerFlags(), args.getExportedPlatformLinkerFlags(), input); return RichStream.from(flags) .map(f -> CxxDescriptionEnhancer.toStringWithMacrosArgs(buildTarget, cellRoots, graphBuilderInner, input, f)) .concat(RichStream.from(delegateExportedLinkerFlags)).toImmutableList(); }, (input, graphBuilderInner) -> { ImmutableList<Arg> delegatePostExportedLinkerFlags = delegate .map(d -> d.getAdditionalPostExportedLinkerFlags(buildTarget, graphBuilderInner, input)) .orElse(ImmutableList.of()); ImmutableList<StringWithMacros> flags = CxxFlags.getFlagsWithMacrosWithPlatformMacroExpansion( args.getExportedPostLinkerFlags(), args.getExportedPostPlatformLinkerFlags(), input); return RichStream.from(flags) .map(f -> CxxDescriptionEnhancer.toStringWithMacrosArgs(buildTarget, cellRoots, graphBuilderInner, input, f)) .concat(RichStream.from(delegatePostExportedLinkerFlags)).toImmutableList(); }, (cxxPlatform, ruleResolverInner, pathResolverInner, ruleFinderInner) -> { return getSharedLibraryNativeLinkTargetInput(buildTarget, projectFilesystem, ruleResolverInner, pathResolverInner, ruleFinderInner, cellRoots, cxxBuckConfig, cxxPlatform, args, cxxDeps.get(ruleResolverInner, cxxPlatform), CxxFlags.getFlagsWithMacrosWithPlatformMacroExpansion(args.getLinkerFlags(), args.getPlatformLinkerFlags(), cxxPlatform), CxxFlags.getFlagsWithMacrosWithPlatformMacroExpansion(args.getExportedLinkerFlags(), args.getExportedPlatformLinkerFlags(), cxxPlatform), CxxFlags.getFlagsWithMacrosWithPlatformMacroExpansion(args.getPostLinkerFlags(), args.getPostPlatformLinkerFlags(), cxxPlatform), CxxFlags.getFlagsWithMacrosWithPlatformMacroExpansion(args.getExportedPostLinkerFlags(), args.getExportedPostPlatformLinkerFlags(), cxxPlatform), args.getFrameworks(), args.getLibraries(), transitiveCxxPreprocessorInputFunction, delegate); }, args.getSupportedPlatformsRegex(), args.getFrameworks(), args.getLibraries(), args.getForceStatic().orElse(false) ? NativeLinkable.Linkage.STATIC : args.getPreferredLinkage().orElse(NativeLinkable.Linkage.ANY), args.getLinkWhole().orElse(false), args.getSoname(), args.getTests(), args.getCanBeAsset().orElse(false), !buildTarget.getFlavors().contains(CxxDescriptionEnhancer.EXPORTED_HEADER_SYMLINK_TREE_FLAVOR), args.isReexportAllHeaderDependencies() .orElse(cxxBuckConfig.getDefaultReexportAllHeaderDependencies()), args.getSupportsMergedLinking().orElse(true), delegate); } /** * @return an {@link Iterable} with platform dependencies that need to be resolved at parse time. */ public Iterable<BuildTarget> getPlatformParseTimeDeps() { // Since we don't have context on the top-level rules using this C/C++ library (e.g. it may be // a `python_binary`), we eagerly add the deps for all possible platforms to guarantee that the // correct ones are included. return getCxxPlatformsProvider().getUnresolvedCxxPlatforms().getValues().stream() .flatMap(p -> RichStream.from(p.getParseTimeDeps())).collect(ImmutableList.toImmutableList()); } private static ImmutableList<SourcePath> requireObjects(BuildTarget buildTarget, ProjectFilesystem projectFilesystem, ActionGraphBuilder graphBuilder, SourcePathResolver sourcePathResolver, SourcePathRuleFinder ruleFinder, CellPathResolver cellRoots, CxxBuckConfig cxxBuckConfig, CxxPlatform cxxPlatform, PicType pic, CxxLibraryDescriptionArg args, ImmutableSet<BuildRule> deps, CxxLibraryDescription.TransitiveCxxPreprocessorInputFunction transitivePreprocessorInputs, Optional<CxxLibraryDescriptionDelegate> delegate) { // TODO(T21900747): Fix dependence on order of object paths ImmutableList.Builder<SourcePath> builder = ImmutableList.builder(); ImmutableMap<CxxPreprocessAndCompile, SourcePath> cxxObjects = requireCxxObjects(buildTarget, projectFilesystem, graphBuilder, sourcePathResolver, ruleFinder, cellRoots, cxxBuckConfig, cxxPlatform, pic, args, deps, transitivePreprocessorInputs, delegate); builder.addAll(cxxObjects.values()); Optional<ImmutableList<SourcePath>> pluginObjectPaths = delegate .flatMap(p -> p.getObjectFilePaths(buildTarget, graphBuilder, cxxPlatform)); pluginObjectPaths.ifPresent(paths -> builder.addAll(paths)); return builder.build(); } private static ImmutableMap<CxxPreprocessAndCompile, SourcePath> requireCxxObjects(BuildTarget buildTarget, ProjectFilesystem projectFilesystem, ActionGraphBuilder graphBuilder, SourcePathResolver sourcePathResolver, SourcePathRuleFinder ruleFinder, CellPathResolver cellRoots, CxxBuckConfig cxxBuckConfig, CxxPlatform cxxPlatform, PicType pic, CxxLibraryDescriptionArg args, ImmutableSet<BuildRule> deps, CxxLibraryDescription.TransitiveCxxPreprocessorInputFunction transitivePreprocessorInputs, Optional<CxxLibraryDescriptionDelegate> delegate) { boolean shouldCreatePrivateHeadersSymlinks = args.getXcodePrivateHeadersSymlinks() .orElse(cxxPlatform.getPrivateHeadersSymlinksEnabled()); HeaderSymlinkTree headerSymlinkTree = CxxDescriptionEnhancer.requireHeaderSymlinkTree(buildTarget, projectFilesystem, ruleFinder, graphBuilder, cxxPlatform, CxxDescriptionEnhancer.parseHeaders(buildTarget, graphBuilder, ruleFinder, sourcePathResolver, Optional.of(cxxPlatform), args), HeaderVisibility.PRIVATE, shouldCreatePrivateHeadersSymlinks); ImmutableList.Builder<HeaderSymlinkTree> privateHeaderSymlinkTrees = ImmutableList.builder(); privateHeaderSymlinkTrees.add(headerSymlinkTree); delegate.ifPresent(d -> d.getPrivateHeaderSymlinkTree(buildTarget, graphBuilder, cxxPlatform) .ifPresent(h -> privateHeaderSymlinkTrees.add(h))); // Create rule to build the object files. ImmutableMultimap<CxxSource.Type, Arg> compilerFlags = ImmutableListMultimap .copyOf(Multimaps.transformValues( CxxFlags.getLanguageFlagsWithMacros(args.getCompilerFlags(), args.getPlatformCompilerFlags(), args.getLangCompilerFlags(), args.getLangPlatformCompilerFlags(), cxxPlatform), f -> CxxDescriptionEnhancer.toStringWithMacrosArgs(buildTarget, cellRoots, graphBuilder, cxxPlatform, f))); return CxxSourceRuleFactory .of(projectFilesystem, buildTarget, graphBuilder, sourcePathResolver, ruleFinder, cxxBuckConfig, cxxPlatform, CxxLibraryDescription.getPreprocessorInputsForBuildingLibrarySources(cxxBuckConfig, graphBuilder, cellRoots, buildTarget, args, cxxPlatform, deps, transitivePreprocessorInputs, privateHeaderSymlinkTrees.build()), compilerFlags, args.getPrefixHeader(), args.getPrecompiledHeader(), pic) .requirePreprocessAndCompileRules(CxxDescriptionEnhancer.parseCxxSources(buildTarget, graphBuilder, ruleFinder, sourcePathResolver, cxxPlatform, args)); } private static NativeLinkableInput getSharedLibraryNativeLinkTargetInput(BuildTarget buildTarget, ProjectFilesystem projectFilesystem, ActionGraphBuilder graphBuilder, SourcePathResolver pathResolver, SourcePathRuleFinder ruleFinder, CellPathResolver cellRoots, CxxBuckConfig cxxBuckConfig, CxxPlatform cxxPlatform, CxxLibraryDescriptionArg arg, ImmutableSet<BuildRule> deps, ImmutableList<StringWithMacros> linkerFlags, ImmutableList<StringWithMacros> exportedLinkerFlags, ImmutableList<StringWithMacros> postLinkerFlags, ImmutableList<StringWithMacros> postExportedLinkerFlags, ImmutableSet<FrameworkPath> frameworks, ImmutableSet<FrameworkPath> libraries, CxxLibraryDescription.TransitiveCxxPreprocessorInputFunction transitiveCxxPreprocessorInputFunction, Optional<CxxLibraryDescriptionDelegate> delegate) { // Create rules for compiling the PIC object files. ImmutableList<SourcePath> objects = requireObjects(buildTarget, projectFilesystem, graphBuilder, pathResolver, ruleFinder, cellRoots, cxxBuckConfig, cxxPlatform, cxxPlatform.getPicTypeForSharedLinking(), arg, deps, transitiveCxxPreprocessorInputFunction, delegate); return NativeLinkableInput.builder() .addAllArgs(RichStream.<StringWithMacros>empty().concat(linkerFlags.stream()) .concat(exportedLinkerFlags.stream()) .map(f -> CxxDescriptionEnhancer.toStringWithMacrosArgs(buildTarget, cellRoots, graphBuilder, cxxPlatform, f)) .toImmutableList()) .addAllArgs(SourcePathArg.from(objects)) .addAllArgs(RichStream.from(postLinkerFlags) .map(f -> CxxDescriptionEnhancer.toStringWithMacrosArgs(buildTarget, cellRoots, graphBuilder, cxxPlatform, f)) .toImmutableList()) .addAllArgs(RichStream.from(postExportedLinkerFlags.stream()) .map(f -> CxxDescriptionEnhancer.toStringWithMacrosArgs(buildTarget, cellRoots, graphBuilder, cxxPlatform, f)) .toImmutableList()) .setFrameworks(frameworks).setLibraries(libraries).build(); } /** * Create all build rules needed to generate the shared library. * * @return the {@link CxxLink} rule representing the actual shared library. */ private static CxxLink createSharedLibrary(BuildTarget buildTargetMaybeWithLinkerMapMode, ProjectFilesystem projectFilesystem, ActionGraphBuilder graphBuilder, SourcePathResolver pathResolver, SourcePathRuleFinder ruleFinder, CellPathResolver cellRoots, CxxBuckConfig cxxBuckConfig, CxxPlatform cxxPlatform, CxxLibraryDescriptionArg args, ImmutableSet<BuildRule> deps, ImmutableList<StringWithMacros> linkerFlags, ImmutableList<StringWithMacros> postLinkerFlags, ImmutableSet<FrameworkPath> frameworks, ImmutableSet<FrameworkPath> libraries, Optional<String> soname, Optional<Linker.CxxRuntimeType> cxxRuntimeType, Linker.LinkType linkType, Linker.LinkableDepType linkableDepType, Optional<SourcePath> bundleLoader, ImmutableSet<BuildTarget> blacklist, CxxLibraryDescription.TransitiveCxxPreprocessorInputFunction transitiveCxxPreprocessorInputFunction, Optional<CxxLibraryDescriptionDelegate> delegate) { BuildTarget buildTargetWithoutLinkerMapMode = LinkerMapMode.removeLinkerMapModeFlavorInTarget( buildTargetMaybeWithLinkerMapMode, LinkerMapMode.FLAVOR_DOMAIN.getValue(buildTargetMaybeWithLinkerMapMode)); // Create rules for compiling the PIC object files. ImmutableList<SourcePath> objects = requireObjects(buildTargetWithoutLinkerMapMode, projectFilesystem, graphBuilder, pathResolver, ruleFinder, cellRoots, cxxBuckConfig, cxxPlatform, cxxPlatform.getPicTypeForSharedLinking(), args, deps, transitiveCxxPreprocessorInputFunction, delegate); // Setup the rules to link the shared library. BuildTarget sharedTarget = CxxDescriptionEnhancer.createSharedLibraryBuildTarget( buildTargetMaybeWithLinkerMapMode, cxxPlatform.getFlavor(), linkType); String sharedLibrarySoname = CxxDescriptionEnhancer.getSharedLibrarySoname(soname, buildTargetMaybeWithLinkerMapMode, cxxPlatform); Path sharedLibraryPath = CxxDescriptionEnhancer.getSharedLibraryPath(projectFilesystem, sharedTarget, sharedLibrarySoname); ImmutableList<NativeLinkable> delegateNativeLinkables = delegate .flatMap(d -> d.getNativeLinkableExportedDeps(sharedTarget, graphBuilder, cxxPlatform)) .orElse(ImmutableList.of()); ImmutableList<NativeLinkable> allNativeLinkables = RichStream.from(deps).filter(NativeLinkable.class) .concat(RichStream.from(delegateNativeLinkables)).toImmutableList(); CxxLinkOptions linkOptions = CxxLinkOptions.of(args.getThinLto(), args.getFatLto()); return CxxLinkableEnhancer.createCxxLinkableBuildRule(cxxBuckConfig, cxxPlatform, projectFilesystem, graphBuilder, pathResolver, ruleFinder, sharedTarget, linkType, Optional.of(sharedLibrarySoname), sharedLibraryPath, args.getLinkerExtraOutputs(), linkableDepType, linkOptions, allNativeLinkables, cxxRuntimeType, bundleLoader, blacklist, ImmutableSet.of(), NativeLinkableInput.builder() .addAllArgs(RichStream.from(linkerFlags) .map(f -> CxxDescriptionEnhancer.toStringWithMacrosArgs(sharedTarget, cellRoots, graphBuilder, cxxPlatform, f)) .toImmutableList()) .addAllArgs(SourcePathArg.from(objects)) .addAllArgs(RichStream.from(postLinkerFlags) .map(f -> CxxDescriptionEnhancer.toStringWithMacrosArgs(sharedTarget, cellRoots, graphBuilder, cxxPlatform, f)) .toImmutableList()) .setFrameworks(frameworks).setLibraries(libraries).build(), Optional.empty(), cellRoots); } /** @return a {@link HeaderSymlinkTree} for the headers of this C/C++ library. */ private HeaderSymlinkTree createHeaderSymlinkTreeBuildRule(BuildTarget buildTarget, ProjectFilesystem projectFilesystem, ActionGraphBuilder graphBuilder, CxxPlatform cxxPlatform, CxxLibraryDescriptionArg args) { boolean shouldCreatePrivateHeaderSymlinks = args.getXcodePrivateHeadersSymlinks() .orElse(cxxPlatform.getPrivateHeadersSymlinksEnabled()); SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(graphBuilder); SourcePathResolver pathResolver = DefaultSourcePathResolver.from(ruleFinder); return CxxDescriptionEnhancer.createHeaderSymlinkTree(buildTarget, projectFilesystem, ruleFinder, graphBuilder, cxxPlatform, CxxDescriptionEnhancer.parseHeaders(buildTarget, graphBuilder, ruleFinder, pathResolver, Optional.of(cxxPlatform), args), HeaderVisibility.PRIVATE, shouldCreatePrivateHeaderSymlinks); } /** @return a {@link HeaderSymlinkTree} for the exported headers of this C/C++ library. */ private HeaderSymlinkTree createExportedHeaderSymlinkTreeBuildRule(BuildTarget buildTarget, ProjectFilesystem projectFilesystem, ActionGraphBuilder graphBuilder, HeaderMode mode, CxxLibraryDescriptionArg args) { SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(graphBuilder); SourcePathResolver pathResolver = DefaultSourcePathResolver.from(ruleFinder); return CxxDescriptionEnhancer.createHeaderSymlinkTree(buildTarget, projectFilesystem, ruleFinder, mode, CxxDescriptionEnhancer.parseExportedHeaders(buildTarget, graphBuilder, ruleFinder, pathResolver, Optional.empty(), args), HeaderVisibility.PUBLIC); } /** @return a {@link HeaderSymlinkTree} for the exported headers of this C/C++ library. */ private HeaderSymlinkTree createExportedPlatformHeaderSymlinkTreeBuildRule(BuildTarget buildTarget, ProjectFilesystem projectFilesystem, ActionGraphBuilder graphBuilder, CxxPlatform cxxPlatform, CxxLibraryDescriptionArg args) { boolean shouldCreatePublicHeaderSymlinks = args.getXcodePublicHeadersSymlinks() .orElse(cxxPlatform.getPublicHeadersSymlinksEnabled()); SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(graphBuilder); SourcePathResolver pathResolver = DefaultSourcePathResolver.from(ruleFinder); return CxxDescriptionEnhancer.createHeaderSymlinkTree(buildTarget, projectFilesystem, ruleFinder, graphBuilder, cxxPlatform, CxxDescriptionEnhancer.parseExportedPlatformHeaders(buildTarget, graphBuilder, ruleFinder, pathResolver, cxxPlatform, args), HeaderVisibility.PUBLIC, shouldCreatePublicHeaderSymlinks); } /** * Create all build rules needed to generate the static library. * * @return build rule that builds the static library version of this C/C++ library. */ private static BuildRule createStaticLibraryBuildRule(BuildTarget buildTarget, ProjectFilesystem projectFilesystem, ActionGraphBuilder graphBuilder, CellPathResolver cellRoots, CxxBuckConfig cxxBuckConfig, CxxPlatform cxxPlatform, CxxLibraryDescriptionArg args, ImmutableSet<BuildRule> deps, PicType pic, CxxLibraryDescription.TransitiveCxxPreprocessorInputFunction transitiveCxxPreprocessorInputFunction, Optional<CxxLibraryDescriptionDelegate> delegate) { SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(graphBuilder); SourcePathResolver sourcePathResolver = DefaultSourcePathResolver.from(ruleFinder); // Create rules for compiling the object files. ImmutableList<SourcePath> objects = requireObjects(buildTarget, projectFilesystem, graphBuilder, sourcePathResolver, ruleFinder, cellRoots, cxxBuckConfig, cxxPlatform, pic, args, deps, transitiveCxxPreprocessorInputFunction, delegate); // Write a build rule to create the archive for this C/C++ library. BuildTarget staticTarget = CxxDescriptionEnhancer.createStaticLibraryBuildTarget(buildTarget, cxxPlatform.getFlavor(), pic); if (objects.isEmpty()) { return new NoopBuildRule(staticTarget, projectFilesystem); } Path staticLibraryPath = CxxDescriptionEnhancer.getStaticLibraryPath(projectFilesystem, buildTarget, cxxPlatform.getFlavor(), pic, args.getStaticLibraryBasename(), cxxPlatform.getStaticLibraryExtension(), cxxBuckConfig.isUniqueLibraryNameEnabled()); return Archive.from(staticTarget, projectFilesystem, graphBuilder, ruleFinder, cxxPlatform, staticLibraryPath, ImmutableList.copyOf(objects), /* cacheable */ true); } /** @return a {@link CxxLink} rule which builds a shared library version of this C/C++ library. */ private static CxxLink createSharedLibraryBuildRule(BuildTarget buildTarget, ProjectFilesystem projectFilesystem, ActionGraphBuilder graphBuilder, CellPathResolver cellRoots, CxxBuckConfig cxxBuckConfig, CxxPlatform cxxPlatform, CxxLibraryDescriptionArg args, ImmutableSet<BuildRule> deps, Linker.LinkType linkType, Linker.LinkableDepType linkableDepType, Optional<SourcePath> bundleLoader, ImmutableSet<BuildTarget> blacklist, CxxLibraryDescription.TransitiveCxxPreprocessorInputFunction transitiveCxxPreprocessorInputFunction, Optional<CxxLibraryDescriptionDelegate> delegate) { ImmutableList.Builder<StringWithMacros> linkerFlags = ImmutableList.builder(); linkerFlags.addAll(CxxFlags.getFlagsWithMacrosWithPlatformMacroExpansion(args.getLinkerFlags(), args.getPlatformLinkerFlags(), cxxPlatform)); linkerFlags.addAll(CxxFlags.getFlagsWithMacrosWithPlatformMacroExpansion(args.getExportedLinkerFlags(), args.getExportedPlatformLinkerFlags(), cxxPlatform)); ImmutableList.Builder<StringWithMacros> postLinkerFlags = ImmutableList.builder(); postLinkerFlags.addAll(CxxFlags.getFlagsWithMacrosWithPlatformMacroExpansion(args.getPostLinkerFlags(), args.getPostPlatformLinkerFlags(), cxxPlatform)); postLinkerFlags.addAll(CxxFlags.getFlagsWithMacrosWithPlatformMacroExpansion( args.getExportedPostLinkerFlags(), args.getExportedPostPlatformLinkerFlags(), cxxPlatform)); SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(graphBuilder); SourcePathResolver sourcePathResolver = DefaultSourcePathResolver.from(ruleFinder); return createSharedLibrary(buildTarget, projectFilesystem, graphBuilder, sourcePathResolver, ruleFinder, cellRoots, cxxBuckConfig, cxxPlatform, args, deps, linkerFlags.build(), postLinkerFlags.build(), args.getFrameworks(), args.getLibraries(), args.getSoname(), args.getCxxRuntimeType(), linkType, linkableDepType, bundleLoader, blacklist, transitiveCxxPreprocessorInputFunction, delegate); } // Create a shared library interface from the shared library built by this description. private BuildRule createDependentSharedLibraryInterface(BuildTarget baseTarget, ProjectFilesystem projectFilesystem, ActionGraphBuilder graphBuilder, CxxPlatform cxxPlatform, SharedLibraryInterfaceParams params) { SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(graphBuilder); SourcePathResolver pathResolver = DefaultSourcePathResolver.from(ruleFinder); // Otherwise, grab the rule's original shared library and use that. CxxLink sharedLibrary = (CxxLink) graphBuilder.requireRule(baseTarget .withAppendedFlavors(cxxPlatform.getFlavor(), CxxLibraryDescription.Type.SHARED.getFlavor())); return SharedLibraryInterfaceFactoryResolver.resolveFactory(params).createSharedInterfaceLibraryFromLibrary( baseTarget.withAppendedFlavors(CxxLibraryDescription.Type.SHARED_INTERFACE.getFlavor(), cxxPlatform.getFlavor()), projectFilesystem, graphBuilder, pathResolver, ruleFinder, cxxPlatform, sharedLibrary.getSourcePathToOutput()); } // Create a shared library interface directly from this rule's object files -- independent of the // the shared library built by this description. private BuildRule createIndependentSharedLibraryInterface(BuildTarget baseTarget, ProjectFilesystem projectFilesystem, ActionGraphBuilder graphBuilder, CxxPlatform cxxPlatform, SharedLibraryInterfaceParams params) { SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(graphBuilder); SourcePathResolver pathResolver = DefaultSourcePathResolver.from(ruleFinder); NativeLinkTarget linkTarget = (NativeLinkTarget) graphBuilder .requireRule(baseTarget.withoutFlavors(cxxPlatform.getFlavor())); NativeLinkTargetMode linkTargetMode = linkTarget.getNativeLinkTargetMode(cxxPlatform); Preconditions.checkArgument(linkTargetMode.getType().equals(LinkType.SHARED)); Linker linker = cxxPlatform.getLd().resolve(graphBuilder); // Build up the arguments to pass to the linker. ImmutableList.Builder<Arg> argsBuilder = ImmutableList.builder(); // Pass any platform specific or extra linker flags. argsBuilder.addAll(SanitizedArg.from(cxxPlatform.getCompilerDebugPathSanitizer().sanitize(Optional.empty()), cxxPlatform.getLdflags())); // Add flag to link a shared library. argsBuilder.addAll(linker.getSharedLibFlag()); // Add flag to embed an SONAME. String soname = linkTarget.getNativeLinkTargetMode(cxxPlatform).getLibraryName() .orElse(CxxDescriptionEnhancer.getDefaultSharedLibrarySoname(baseTarget, cxxPlatform)); argsBuilder.addAll(StringArg.from(linker.soname(soname))); // Add the args for the root link target first. NativeLinkableInput input = linkTarget.getNativeLinkTargetInput(cxxPlatform, graphBuilder, pathResolver, ruleFinder); argsBuilder.addAll(input.getArgs()); // Since we're linking against a dummy libomnibus, ignore undefined symbols. Put this at the // end to override any user-provided settings. argsBuilder.addAll(StringArg.from(linker.getIgnoreUndefinedSymbolsFlags())); // Add all arguments needed to link in the C/C++ platform runtime. argsBuilder.addAll(StringArg.from(cxxPlatform.getRuntimeLdflags().get(Linker.LinkableDepType.SHARED))); // Add in additional, user-configured flags. argsBuilder.addAll(StringArg.from(params.getLdflags())); ImmutableList<Arg> args = argsBuilder.build(); return SharedLibraryInterfaceFactoryResolver.resolveFactory(params) .createSharedInterfaceLibraryFromLinkableInput( baseTarget.withAppendedFlavors(CxxLibraryDescription.Type.SHARED_INTERFACE.getFlavor(), cxxPlatform.getFlavor()), projectFilesystem, graphBuilder, pathResolver, ruleFinder, soname, linker, args); } private BuildRule createSharedLibraryInterface(BuildTarget baseTarget, ProjectFilesystem projectFilesystem, ActionGraphBuilder graphBuilder, CxxPlatform cxxPlatform, boolean isIndependentInterfaces) { Optional<SharedLibraryInterfaceParams> params = cxxPlatform.getSharedLibraryInterfaceParams(); if (!params.isPresent()) { throw new HumanReadableException("%s: C/C++ platform %s does not support shared library interfaces", baseTarget, cxxPlatform.getFlavor()); } return isIndependentInterfaces ? createIndependentSharedLibraryInterface(baseTarget, projectFilesystem, graphBuilder, cxxPlatform, params.get()) : createDependentSharedLibraryInterface(baseTarget, projectFilesystem, graphBuilder, cxxPlatform, params.get()); } private CxxPlatformsProvider getCxxPlatformsProvider() { return toolchainProvider.getByName(CxxPlatformsProvider.DEFAULT_NAME, CxxPlatformsProvider.class); } }