/* * Copyright 2013-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.log.Logger; import com.facebook.buck.model.BuildTarget; import com.facebook.buck.model.Flavor; import com.facebook.buck.model.FlavorConvertible; import com.facebook.buck.model.FlavorDomain; import com.facebook.buck.model.Flavored; import com.facebook.buck.model.InternalFlavor; import com.facebook.buck.parser.NoSuchBuildTargetException; import com.facebook.buck.rules.BuildRule; import com.facebook.buck.rules.BuildRuleParams; import com.facebook.buck.rules.BuildRuleResolver; import com.facebook.buck.rules.BuildRuleType; import com.facebook.buck.rules.CellPathResolver; import com.facebook.buck.rules.Description; import com.facebook.buck.rules.ImplicitDepsInferringDescription; import com.facebook.buck.rules.ImplicitFlavorsInferringDescription; import com.facebook.buck.rules.MetadataProvidingDescription; import com.facebook.buck.rules.NoopBuildRule; import com.facebook.buck.rules.SourcePath; import com.facebook.buck.rules.SourcePathResolver; import com.facebook.buck.rules.SourcePathRuleFinder; import com.facebook.buck.rules.SymlinkTree; import com.facebook.buck.rules.TargetGraph; import com.facebook.buck.rules.args.SourcePathArg; import com.facebook.buck.rules.coercer.FrameworkPath; import com.facebook.buck.rules.coercer.PatternMatchedCollection; import com.facebook.buck.rules.coercer.SourceList; import com.facebook.buck.rules.macros.StringWithMacros; import com.facebook.buck.util.HumanReadableException; import com.facebook.buck.util.RichStream; import com.facebook.buck.util.immutables.BuckStyleImmutable; import com.facebook.buck.versions.Version; import com.facebook.buck.versions.VersionPropagator; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import java.nio.file.Path; import java.util.Arrays; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.regex.Pattern; import org.immutables.value.Value; public class CxxLibraryDescription implements Description<CxxLibraryDescriptionArg>, ImplicitDepsInferringDescription<CxxLibraryDescription.CommonArg>, ImplicitFlavorsInferringDescription, Flavored, MetadataProvidingDescription<CxxLibraryDescriptionArg>, VersionPropagator<CxxLibraryDescriptionArg> { private static final Logger LOG = Logger.get(CxxLibraryDescription.class); public enum Type implements FlavorConvertible { HEADERS(CxxDescriptionEnhancer.HEADER_SYMLINK_TREE_FLAVOR), EXPORTED_HEADERS(CxxDescriptionEnhancer.EXPORTED_HEADER_SYMLINK_TREE_FLAVOR), SANDBOX_TREE(CxxDescriptionEnhancer.SANDBOX_TREE_FLAVOR), SHARED(CxxDescriptionEnhancer.SHARED_FLAVOR), SHARED_INTERFACE(InternalFlavor.of("shared-interface")), STATIC_PIC(CxxDescriptionEnhancer.STATIC_PIC_FLAVOR), STATIC(CxxDescriptionEnhancer.STATIC_FLAVOR), MACH_O_BUNDLE(CxxDescriptionEnhancer.MACH_O_BUNDLE_FLAVOR), ; private final Flavor flavor; Type(Flavor flavor) { this.flavor = flavor; } @Override public Flavor getFlavor() { return flavor; } } private static final FlavorDomain<Type> LIBRARY_TYPE = FlavorDomain.from("C/C++ Library Type", Type.class); public enum MetadataType implements FlavorConvertible { COMPILATION_DATABASE_DEPS(InternalFlavor.of("compilation-database-deps")), CXX_HEADERS(InternalFlavor.of("header-symlink-tree")), CXX_PREPROCESSOR_INPUT(InternalFlavor.of("cxx-preprocessor-input")), ; private final Flavor flavor; MetadataType(Flavor flavor) { this.flavor = flavor; } @Override public Flavor getFlavor() { return flavor; } } public static final FlavorDomain<MetadataType> METADATA_TYPE = FlavorDomain.from("C/C++ Metadata Type", MetadataType.class); private static final FlavorDomain<HeaderVisibility> HEADER_VISIBILITY = FlavorDomain.from("C/C++ Header Visibility", HeaderVisibility.class); private static final FlavorDomain<CxxPreprocessables.HeaderMode> HEADER_MODE = FlavorDomain.from("C/C++ Header Mode", CxxPreprocessables.HeaderMode.class); private final CxxBuckConfig cxxBuckConfig; private final CxxPlatform defaultCxxPlatform; private final InferBuckConfig inferBuckConfig; private final FlavorDomain<CxxPlatform> cxxPlatforms; public CxxLibraryDescription( CxxBuckConfig cxxBuckConfig, CxxPlatform defaultCxxPlatform, InferBuckConfig inferBuckConfig, FlavorDomain<CxxPlatform> cxxPlatforms) { this.cxxBuckConfig = cxxBuckConfig; this.defaultCxxPlatform = defaultCxxPlatform; this.inferBuckConfig = inferBuckConfig; this.cxxPlatforms = cxxPlatforms; } @Override public Optional<ImmutableSet<FlavorDomain<?>>> flavorDomains() { return Optional.of( ImmutableSet.of( // Missing: CXX Compilation Database // Missing: CXX Description Enhancer // Missing: CXX Infer Enhancer cxxPlatforms, LinkerMapMode.FLAVOR_DOMAIN, StripStyle.FLAVOR_DOMAIN)); } @Override public boolean hasFlavors(ImmutableSet<Flavor> flavors) { return cxxPlatforms.containsAnyOf(flavors) || flavors.contains(CxxCompilationDatabase.COMPILATION_DATABASE) || flavors.contains(CxxCompilationDatabase.UBER_COMPILATION_DATABASE) || flavors.contains(CxxInferEnhancer.InferFlavors.INFER.get()) || flavors.contains(CxxInferEnhancer.InferFlavors.INFER_ANALYZE.get()) || flavors.contains(CxxInferEnhancer.InferFlavors.INFER_CAPTURE_ALL.get()) || flavors.contains(CxxDescriptionEnhancer.EXPORTED_HEADER_SYMLINK_TREE_FLAVOR) || LinkerMapMode.FLAVOR_DOMAIN.containsAnyOf(flavors); } private static ImmutableMap<CxxPreprocessAndCompile, SourcePath> requireObjects( BuildRuleParams params, BuildRuleResolver ruleResolver, SourcePathResolver sourcePathResolver, SourcePathRuleFinder ruleFinder, CxxBuckConfig cxxBuckConfig, CxxPlatform cxxPlatform, CxxSourceRuleFactory.PicType pic, CxxLibraryDescriptionArg args, ImmutableSet<BuildRule> deps) throws NoSuchBuildTargetException { boolean shouldCreatePrivateHeadersSymlinks = args.getXcodePrivateHeadersSymlinks() .orElse(cxxPlatform.getPrivateHeadersSymlinksEnabled()); HeaderSymlinkTree headerSymlinkTree = CxxDescriptionEnhancer.requireHeaderSymlinkTree( params, ruleResolver, cxxPlatform, CxxDescriptionEnhancer.parseHeaders( params.getBuildTarget(), ruleResolver, ruleFinder, sourcePathResolver, Optional.of(cxxPlatform), args), HeaderVisibility.PRIVATE, shouldCreatePrivateHeadersSymlinks); Optional<SymlinkTree> sandboxTree = Optional.empty(); if (cxxBuckConfig.sandboxSources()) { sandboxTree = CxxDescriptionEnhancer.createSandboxTree(params, ruleResolver, cxxPlatform); } ImmutableList<CxxPreprocessorInput> cxxPreprocessorInputFromDependencies = CxxDescriptionEnhancer.collectCxxPreprocessorInput( params, cxxPlatform, deps, CxxFlags.getLanguageFlags( args.getPreprocessorFlags(), args.getPlatformPreprocessorFlags(), args.getLangPreprocessorFlags(), cxxPlatform), ImmutableList.of(headerSymlinkTree), ImmutableSet.of(), getTransitiveCxxPreprocessorInput(params, ruleResolver, cxxPlatform, deps), args.getIncludeDirs(), sandboxTree); // Create rule to build the object files. return CxxSourceRuleFactory.requirePreprocessAndCompileRules( params, ruleResolver, sourcePathResolver, ruleFinder, cxxBuckConfig, cxxPlatform, cxxPreprocessorInputFromDependencies, CxxFlags.getLanguageFlags( args.getCompilerFlags(), args.getPlatformCompilerFlags(), args.getLangCompilerFlags(), cxxPlatform), args.getPrefixHeader(), args.getPrecompiledHeader(), CxxDescriptionEnhancer.parseCxxSources( params.getBuildTarget(), ruleResolver, ruleFinder, sourcePathResolver, cxxPlatform, args), pic, sandboxTree); } public static ImmutableCollection<CxxPreprocessorInput> getTransitiveCxxPreprocessorInput( BuildRuleParams params, BuildRuleResolver ruleResolver, CxxPlatform cxxPlatform, ImmutableSet<BuildRule> deps) throws NoSuchBuildTargetException { // Check if there is a target node representative for the library in the action graph and, // if so, grab the cached transitive C/C++ preprocessor input from that. BuildTarget rawTarget = params .getBuildTarget() .withoutFlavors( ImmutableSet.<Flavor>builder() .addAll(LIBRARY_TYPE.getFlavors()) .add(cxxPlatform.getFlavor()) .build()); Optional<BuildRule> rawRule = ruleResolver.getRuleOptional(rawTarget); if (rawRule.isPresent()) { CxxLibrary rule = (CxxLibrary) rawRule.get(); return rule.getTransitiveCxxPreprocessorInput(cxxPlatform, HeaderVisibility.PUBLIC).values(); } Map<BuildTarget, CxxPreprocessorInput> input = Maps.newLinkedHashMap(); input.put( params.getBuildTarget(), ruleResolver .requireMetadata( params .getBuildTarget() .withAppendedFlavors( MetadataType.CXX_PREPROCESSOR_INPUT.getFlavor(), cxxPlatform.getFlavor(), HeaderVisibility.PUBLIC.getFlavor()), CxxPreprocessorInput.class) .orElseThrow(IllegalStateException::new)); for (BuildRule rule : deps) { if (rule instanceof CxxPreprocessorDep) { input.putAll( ((CxxPreprocessorDep) rule) .getTransitiveCxxPreprocessorInput(cxxPlatform, HeaderVisibility.PUBLIC)); } } return ImmutableList.copyOf(input.values()); } private static NativeLinkableInput getSharedLibraryNativeLinkTargetInput( BuildRuleParams params, BuildRuleResolver ruleResolver, SourcePathResolver pathResolver, SourcePathRuleFinder ruleFinder, CellPathResolver cellRoots, CxxBuckConfig cxxBuckConfig, CxxPlatform cxxPlatform, CxxLibraryDescriptionArg arg, ImmutableSet<BuildRule> deps, ImmutableList<StringWithMacros> linkerFlags, ImmutableList<StringWithMacros> exportedLinkerFlags, ImmutableSet<FrameworkPath> frameworks, ImmutableSet<FrameworkPath> libraries) throws NoSuchBuildTargetException { // Create rules for compiling the PIC object files. ImmutableMap<CxxPreprocessAndCompile, SourcePath> objects = requireObjects( params, ruleResolver, pathResolver, ruleFinder, cxxBuckConfig, cxxPlatform, CxxSourceRuleFactory.PicType.PIC, arg, deps); return NativeLinkableInput.builder() .addAllArgs( CxxDescriptionEnhancer.toStringWithMacrosArgs( params.getBuildTarget(), cellRoots, ruleResolver, cxxPlatform, Iterables.concat(linkerFlags, exportedLinkerFlags))) .addAllArgs(SourcePathArg.from(objects.values())) .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( BuildRuleParams params, BuildRuleResolver ruleResolver, SourcePathResolver pathResolver, SourcePathRuleFinder ruleFinder, CellPathResolver cellRoots, CxxBuckConfig cxxBuckConfig, CxxPlatform cxxPlatform, CxxLibraryDescriptionArg args, ImmutableSet<BuildRule> deps, ImmutableList<StringWithMacros> linkerFlags, 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) throws NoSuchBuildTargetException { Optional<LinkerMapMode> flavoredLinkerMapMode = LinkerMapMode.FLAVOR_DOMAIN.getValue(params.getBuildTarget()); params = LinkerMapMode.removeLinkerMapModeFlavorInParams(params, flavoredLinkerMapMode); // Create rules for compiling the PIC object files. ImmutableMap<CxxPreprocessAndCompile, SourcePath> objects = requireObjects( params, ruleResolver, pathResolver, ruleFinder, cxxBuckConfig, cxxPlatform, CxxSourceRuleFactory.PicType.PIC, args, deps); // Setup the rules to link the shared library. BuildTarget sharedTarget = CxxDescriptionEnhancer.createSharedLibraryBuildTarget( LinkerMapMode.restoreLinkerMapModeFlavorInParams(params, flavoredLinkerMapMode) .getBuildTarget(), cxxPlatform.getFlavor(), linkType); String sharedLibrarySoname = CxxDescriptionEnhancer.getSharedLibrarySoname(soname, params.getBuildTarget(), cxxPlatform); Path sharedLibraryPath = CxxDescriptionEnhancer.getSharedLibraryPath( params.getProjectFilesystem(), sharedTarget, sharedLibrarySoname); ImmutableList.Builder<StringWithMacros> extraLdFlagsBuilder = ImmutableList.builder(); extraLdFlagsBuilder.addAll(linkerFlags); ImmutableList<StringWithMacros> extraLdFlags = extraLdFlagsBuilder.build(); return CxxLinkableEnhancer.createCxxLinkableBuildRule( cxxBuckConfig, cxxPlatform, LinkerMapMode.restoreLinkerMapModeFlavorInParams(params, flavoredLinkerMapMode), ruleResolver, pathResolver, ruleFinder, sharedTarget, linkType, Optional.of(sharedLibrarySoname), sharedLibraryPath, linkableDepType, args.getThinLto(), RichStream.from(deps).filter(NativeLinkable.class).toImmutableList(), cxxRuntimeType, bundleLoader, blacklist, NativeLinkableInput.builder() .addAllArgs( CxxDescriptionEnhancer.toStringWithMacrosArgs( params.getBuildTarget(), cellRoots, ruleResolver, cxxPlatform, extraLdFlags)) .addAllArgs(SourcePathArg.from(objects.values())) .setFrameworks(frameworks) .setLibraries(libraries) .build(), Optional.empty()); } @Override public Class<CxxLibraryDescriptionArg> getConstructorArgType() { return CxxLibraryDescriptionArg.class; } /** @return a {@link HeaderSymlinkTree} for the headers of this C/C++ library. */ private HeaderSymlinkTree createHeaderSymlinkTreeBuildRule( BuildRuleParams params, BuildRuleResolver resolver, CxxPlatform cxxPlatform, CxxLibraryDescriptionArg args) throws NoSuchBuildTargetException { boolean shouldCreatePrivateHeaderSymlinks = args.getXcodePrivateHeadersSymlinks() .orElse(cxxPlatform.getPrivateHeadersSymlinksEnabled()); SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver); SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder); return CxxDescriptionEnhancer.createHeaderSymlinkTree( params, resolver, cxxPlatform, CxxDescriptionEnhancer.parseHeaders( params.getBuildTarget(), resolver, 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( BuildRuleParams params, BuildRuleResolver resolver, CxxPreprocessables.HeaderMode mode, CxxLibraryDescriptionArg args) throws NoSuchBuildTargetException { SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver); SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder); return CxxDescriptionEnhancer.createHeaderSymlinkTree( params, resolver, mode, CxxDescriptionEnhancer.parseExportedHeaders( params.getBuildTarget(), resolver, ruleFinder, pathResolver, Optional.empty(), args), HeaderVisibility.PUBLIC); } /** @return a {@link HeaderSymlinkTree} for the exported headers of this C/C++ library. */ private HeaderSymlinkTree createExportedPlatformHeaderSymlinkTreeBuildRule( BuildRuleParams params, BuildRuleResolver resolver, CxxPlatform cxxPlatform, CxxLibraryDescriptionArg args) throws NoSuchBuildTargetException { boolean shouldCreatePublicHeaderSymlinks = args.getXcodePublicHeadersSymlinks().orElse(cxxPlatform.getPublicHeadersSymlinksEnabled()); SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver); SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder); return CxxDescriptionEnhancer.createHeaderSymlinkTree( params, resolver, cxxPlatform, CxxDescriptionEnhancer.parseExportedPlatformHeaders( params.getBuildTarget(), resolver, 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( BuildRuleParams params, BuildRuleResolver resolver, CxxBuckConfig cxxBuckConfig, CxxPlatform cxxPlatform, CxxLibraryDescriptionArg args, ImmutableSet<BuildRule> deps, CxxSourceRuleFactory.PicType pic) throws NoSuchBuildTargetException { SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver); SourcePathResolver sourcePathResolver = new SourcePathResolver(ruleFinder); // Create rules for compiling the object files. ImmutableMap<CxxPreprocessAndCompile, SourcePath> objects = requireObjects( params, resolver, sourcePathResolver, ruleFinder, cxxBuckConfig, cxxPlatform, pic, args, deps); // Write a build rule to create the archive for this C/C++ library. BuildTarget staticTarget = CxxDescriptionEnhancer.createStaticLibraryBuildTarget( params.getBuildTarget(), cxxPlatform.getFlavor(), pic); if (objects.isEmpty()) { return new NoopBuildRule( new BuildRuleParams( staticTarget, Suppliers.ofInstance(ImmutableSortedSet.of()), Suppliers.ofInstance(ImmutableSortedSet.of()), ImmutableSortedSet.of(), params.getProjectFilesystem())); } Path staticLibraryPath = CxxDescriptionEnhancer.getStaticLibraryPath( params.getProjectFilesystem(), params.getBuildTarget(), cxxPlatform.getFlavor(), pic, cxxPlatform.getStaticLibraryExtension()); return Archive.from( staticTarget, params, ruleFinder, cxxPlatform, cxxBuckConfig.getArchiveContents(), staticLibraryPath, ImmutableList.copyOf(objects.values())); } /** @return a {@link CxxLink} rule which builds a shared library version of this C/C++ library. */ private static CxxLink createSharedLibraryBuildRule( BuildRuleParams params, BuildRuleResolver resolver, CellPathResolver cellRoots, CxxBuckConfig cxxBuckConfig, CxxPlatform cxxPlatform, CxxLibraryDescriptionArg args, ImmutableSet<BuildRule> deps, Linker.LinkType linkType, Linker.LinkableDepType linkableDepType, Optional<SourcePath> bundleLoader, ImmutableSet<BuildTarget> blacklist) throws NoSuchBuildTargetException { ImmutableList.Builder<StringWithMacros> linkerFlags = ImmutableList.builder(); linkerFlags.addAll( CxxFlags.getFlagsWithMacrosWithPlatformMacroExpansion( args.getLinkerFlags(), args.getPlatformLinkerFlags(), cxxPlatform)); linkerFlags.addAll( CxxFlags.getFlagsWithMacrosWithPlatformMacroExpansion( args.getExportedLinkerFlags(), args.getExportedPlatformLinkerFlags(), cxxPlatform)); SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver); SourcePathResolver sourcePathResolver = new SourcePathResolver(ruleFinder); return createSharedLibrary( params, resolver, sourcePathResolver, ruleFinder, cellRoots, cxxBuckConfig, cxxPlatform, args, deps, linkerFlags.build(), args.getFrameworks(), args.getLibraries(), args.getSoname(), args.getCxxRuntimeType(), linkType, linkableDepType, bundleLoader, blacklist); } private static BuildRule createSharedLibraryInterface( BuildRuleParams baseParams, BuildRuleResolver resolver, CxxPlatform cxxPlatform) throws NoSuchBuildTargetException { BuildTarget baseTarget = baseParams.getBuildTarget(); Optional<SharedLibraryInterfaceFactory> factory = cxxPlatform.getSharedLibraryInterfaceFactory(); if (!factory.isPresent()) { throw new HumanReadableException( "%s: C/C++ platform %s does not support shared library interfaces", baseTarget, cxxPlatform.getFlavor()); } CxxLink sharedLibrary = (CxxLink) resolver.requireRule( baseTarget.withAppendedFlavors(cxxPlatform.getFlavor(), Type.SHARED.getFlavor())); SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver); SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder); return factory .get() .createSharedInterfaceLibrary( baseTarget.withAppendedFlavors( Type.SHARED_INTERFACE.getFlavor(), cxxPlatform.getFlavor()), baseParams, resolver, pathResolver, ruleFinder, sharedLibrary.getSourcePathToOutput()); } @Override public BuildRule createBuildRule( TargetGraph targetGraph, BuildRuleParams params, BuildRuleResolver resolver, CellPathResolver cellRoots, CxxLibraryDescriptionArg args) throws NoSuchBuildTargetException { return createBuildRule( params, resolver, cellRoots, args, args.getLinkStyle(), Optional.empty(), ImmutableSet.of(), ImmutableSortedSet.of()); } public BuildRule createBuildRule( BuildRuleParams metadataRuleParams, final BuildRuleResolver resolver, CellPathResolver cellRoots, final CxxLibraryDescriptionArg args, Optional<Linker.LinkableDepType> linkableDepType, final Optional<SourcePath> bundleLoader, ImmutableSet<BuildTarget> blacklist, ImmutableSortedSet<BuildTarget> extraDeps) throws NoSuchBuildTargetException { // Create a copy of the metadata-rule params with the deps removed to pass around into library // code. This should prevent this code from using the over-specified deps when constructing // build rules. BuildRuleParams params = metadataRuleParams.copyInvalidatingDeps(); BuildTarget buildTarget = params.getBuildTarget(); // 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, Type>> type = getLibType(buildTarget); Optional<CxxPlatform> platform = cxxPlatforms.getValue(buildTarget); CxxDeps cxxDeps = CxxDeps.builder().addDeps(args.getCxxDeps()).addDeps(extraDeps).build(); if (params .getBuildTarget() .getFlavors() .contains(CxxCompilationDatabase.COMPILATION_DATABASE)) { // XXX: This needs bundleLoader for tests.. CxxPlatform cxxPlatform = platform.orElse(defaultCxxPlatform); SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver); SourcePathResolver sourcePathResolver = new SourcePathResolver(ruleFinder); BuildRuleParams paramsWithoutFlavor = params.withoutFlavor(CxxCompilationDatabase.COMPILATION_DATABASE); ImmutableMap<CxxPreprocessAndCompile, SourcePath> objects = requireObjects( paramsWithoutFlavor, resolver, sourcePathResolver, ruleFinder, cxxBuckConfig, cxxPlatform, CxxSourceRuleFactory.PicType.PIC, args, cxxDeps.get(resolver, cxxPlatform)); return CxxCompilationDatabase.createCompilationDatabase(params, objects.keySet()); } else if (params .getBuildTarget() .getFlavors() .contains(CxxCompilationDatabase.UBER_COMPILATION_DATABASE)) { return CxxDescriptionEnhancer.createUberCompilationDatabase( platform.isPresent() ? params : params.withAppendedFlavor(defaultCxxPlatform.getFlavor()), resolver); } else if (params .getBuildTarget() .getFlavors() .contains(CxxInferEnhancer.InferFlavors.INFER.get())) { return CxxInferEnhancer.requireInferAnalyzeAndReportBuildRuleForCxxDescriptionArg( params, resolver, cxxBuckConfig, platform.orElse(defaultCxxPlatform), args, inferBuckConfig, new CxxInferSourceFilter(inferBuckConfig)); } else if (params .getBuildTarget() .getFlavors() .contains(CxxInferEnhancer.InferFlavors.INFER_ANALYZE.get())) { return CxxInferEnhancer.requireInferAnalyzeBuildRuleForCxxDescriptionArg( params, resolver, cxxBuckConfig, platform.orElse(defaultCxxPlatform), args, inferBuckConfig, new CxxInferSourceFilter(inferBuckConfig)); } else if (params .getBuildTarget() .getFlavors() .contains(CxxInferEnhancer.InferFlavors.INFER_CAPTURE_ALL.get())) { return CxxInferEnhancer.requireAllTransitiveCaptureBuildRules( params, resolver, cxxBuckConfig, platform.orElse(defaultCxxPlatform), inferBuckConfig, new CxxInferSourceFilter(inferBuckConfig), args); } else if (params .getBuildTarget() .getFlavors() .contains(CxxInferEnhancer.InferFlavors.INFER_CAPTURE_ONLY.get())) { return CxxInferEnhancer.requireInferCaptureAggregatorBuildRuleForCxxDescriptionArg( params, resolver, cxxBuckConfig, platform.orElse(defaultCxxPlatform), args, inferBuckConfig, new CxxInferSourceFilter(inferBuckConfig)); } else if (type.isPresent() && !platform.isPresent()) { BuildRuleParams untypedParams = getUntypedParams(params); switch (type.get().getValue()) { case EXPORTED_HEADERS: Optional<CxxPreprocessables.HeaderMode> mode = HEADER_MODE.getValue(params.getBuildTarget()); if (mode.isPresent()) { return createExportedHeaderSymlinkTreeBuildRule( untypedParams, resolver, 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. BuildRuleParams untypedParams = getUntypedParams(params); switch (type.get().getValue()) { case HEADERS: return createHeaderSymlinkTreeBuildRule(untypedParams, resolver, platform.get(), args); case EXPORTED_HEADERS: return createExportedPlatformHeaderSymlinkTreeBuildRule( untypedParams, resolver, platform.get(), args); case SHARED: return createSharedLibraryBuildRule( untypedParams, resolver, cellRoots, cxxBuckConfig, platform.get(), args, cxxDeps.get(resolver, platform.get()), Linker.LinkType.SHARED, linkableDepType.orElse(Linker.LinkableDepType.SHARED), Optional.empty(), blacklist); case SHARED_INTERFACE: return createSharedLibraryInterface(untypedParams, resolver, platform.get()); case MACH_O_BUNDLE: return createSharedLibraryBuildRule( untypedParams, resolver, cellRoots, cxxBuckConfig, platform.get(), args, cxxDeps.get(resolver, platform.get()), Linker.LinkType.MACH_O_BUNDLE, linkableDepType.orElse(Linker.LinkableDepType.SHARED), bundleLoader, blacklist); case STATIC: return createStaticLibraryBuildRule( untypedParams, resolver, cxxBuckConfig, platform.get(), args, cxxDeps.get(resolver, platform.get()), CxxSourceRuleFactory.PicType.PDC); case STATIC_PIC: return createStaticLibraryBuildRule( untypedParams, resolver, cxxBuckConfig, platform.get(), args, cxxDeps.get(resolver, platform.get()), CxxSourceRuleFactory.PicType.PIC); case SANDBOX_TREE: return CxxDescriptionEnhancer.createSandboxTreeBuildRule( resolver, args, platform.get(), untypedParams); } 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. SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver); final SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder); return new CxxLibrary( metadataRuleParams, resolver, args.getPrivateCxxDeps(), args.getExportedCxxDeps(), Predicates.not(hasObjects), input -> { ImmutableList<StringWithMacros> flags = CxxFlags.getFlagsWithMacrosWithPlatformMacroExpansion( args.getExportedLinkerFlags(), args.getExportedPlatformLinkerFlags(), input); return CxxDescriptionEnhancer.toStringWithMacrosArgs( params.getBuildTarget(), cellRoots, resolver, input, flags); }, cxxPlatform -> { try { return getSharedLibraryNativeLinkTargetInput( params, resolver, pathResolver, ruleFinder, cellRoots, cxxBuckConfig, cxxPlatform, args, cxxDeps.get(resolver, cxxPlatform), CxxFlags.getFlagsWithMacrosWithPlatformMacroExpansion( args.getLinkerFlags(), args.getPlatformLinkerFlags(), cxxPlatform), CxxFlags.getFlagsWithMacrosWithPlatformMacroExpansion( args.getExportedLinkerFlags(), args.getExportedPlatformLinkerFlags(), cxxPlatform), args.getFrameworks(), args.getLibraries()); } catch (NoSuchBuildTargetException e) { throw new RuntimeException(e); } }, 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), !params .getBuildTarget() .getFlavors() .contains(CxxDescriptionEnhancer.EXPORTED_HEADER_SYMLINK_TREE_FLAVOR), args.isReexportAllHeaderDependencies()); } public static Optional<Map.Entry<Flavor, Type>> getLibType(BuildTarget buildTarget) { return LIBRARY_TYPE.getFlavorAndValue(buildTarget); } static BuildRuleParams getUntypedParams(BuildRuleParams params) { Optional<Map.Entry<Flavor, Type>> type = getLibType(params.getBuildTarget()); if (!type.isPresent()) { return params; } Set<Flavor> flavors = Sets.newHashSet(params.getBuildTarget().getFlavors()); flavors.remove(type.get().getKey()); BuildTarget target = BuildTarget.builder(params.getBuildTarget().getUnflavoredBuildTarget()) .addAllFlavors(flavors) .build(); return params.withBuildTarget(target); } @Override public void findDepsForTargetFromConstructorArgs( BuildTarget buildTarget, CellPathResolver cellRoots, CommonArg constructorArg, ImmutableCollection.Builder<BuildTarget> extraDepsBuilder, ImmutableCollection.Builder<BuildTarget> targetGraphOnlyDepsBuilder) { // Get any parse time deps from the C/C++ platforms. extraDepsBuilder.addAll(CxxPlatforms.getParseTimeDeps(cxxPlatforms.getValues())); } public FlavorDomain<CxxPlatform> getCxxPlatforms() { return cxxPlatforms; } public CxxPlatform getDefaultCxxPlatform() { return defaultCxxPlatform; } @Override public <U> Optional<U> createMetadata( BuildTarget buildTarget, BuildRuleResolver resolver, CxxLibraryDescriptionArg args, Optional<ImmutableMap<BuildTarget, Version>> selectedVersions, final Class<U> metadataClass) throws NoSuchBuildTargetException { Map.Entry<Flavor, MetadataType> type = METADATA_TYPE.getFlavorAndValue(buildTarget).orElseThrow(IllegalArgumentException::new); BuildTarget baseTarget = buildTarget.withoutFlavors(type.getKey()); switch (type.getValue()) { case CXX_HEADERS: { Optional<CxxHeaders> symlinkTree = Optional.empty(); if (!args.getExportedHeaders().isEmpty()) { CxxPreprocessables.HeaderMode mode = HEADER_MODE.getRequiredValue(buildTarget); baseTarget = baseTarget.withoutFlavors(mode.getFlavor()); symlinkTree = Optional.of( CxxSymlinkTreeHeaders.from( (HeaderSymlinkTree) resolver.requireRule( baseTarget.withAppendedFlavors( Type.EXPORTED_HEADERS.getFlavor(), mode.getFlavor())), CxxPreprocessables.IncludeType.LOCAL)); } return symlinkTree.map(metadataClass::cast); } case CXX_PREPROCESSOR_INPUT: { Map.Entry<Flavor, CxxPlatform> platform = cxxPlatforms .getFlavorAndValue(buildTarget) .orElseThrow(IllegalArgumentException::new); Map.Entry<Flavor, HeaderVisibility> visibility = HEADER_VISIBILITY .getFlavorAndValue(buildTarget) .orElseThrow(IllegalArgumentException::new); baseTarget = baseTarget.withoutFlavors(platform.getKey(), visibility.getKey()); CxxPreprocessorInput.Builder cxxPreprocessorInputBuilder = CxxPreprocessorInput.builder(); // TODO(agallagher): We currently always add exported flags and frameworks to the // preprocessor input to mimic existing behavior, but this should likely be fixed. cxxPreprocessorInputBuilder.putAllPreprocessorFlags( CxxFlags.getLanguageFlags( args.getExportedPreprocessorFlags(), args.getExportedPlatformPreprocessorFlags(), args.getExportedLangPreprocessorFlags(), platform.getValue())); cxxPreprocessorInputBuilder.addAllFrameworks(args.getFrameworks()); if (visibility.getValue() == HeaderVisibility.PRIVATE && !args.getHeaders().isEmpty()) { HeaderSymlinkTree symlinkTree = (HeaderSymlinkTree) resolver.requireRule( baseTarget.withAppendedFlavors( platform.getKey(), Type.HEADERS.getFlavor())); cxxPreprocessorInputBuilder.addIncludes( CxxSymlinkTreeHeaders.from(symlinkTree, CxxPreprocessables.IncludeType.LOCAL)); } if (visibility.getValue() == HeaderVisibility.PUBLIC) { // Add platform-agnostic headers. boolean shouldCreatePublicHeaderSymlinks = args.getXcodePublicHeadersSymlinks() .orElse(platform.getValue().getPublicHeadersSymlinksEnabled()); CxxPreprocessables.HeaderMode mode = CxxDescriptionEnhancer.getHeaderModeForPlatform( resolver, platform.getValue(), shouldCreatePublicHeaderSymlinks); Optional<CxxHeaders> exportedHeaders = resolver.requireMetadata( baseTarget.withAppendedFlavors( MetadataType.CXX_HEADERS.getFlavor(), mode.getFlavor()), CxxHeaders.class); exportedHeaders.ifPresent(cxxPreprocessorInputBuilder::addIncludes); // Add platform-specific headers. if (!args.getExportedPlatformHeaders() .getMatchingValues(platform.getKey().toString()) .isEmpty()) { HeaderSymlinkTree symlinkTree = (HeaderSymlinkTree) resolver.requireRule( baseTarget.withAppendedFlavors( platform.getKey(), Type.EXPORTED_HEADERS.getFlavor())); cxxPreprocessorInputBuilder.addIncludes( CxxSymlinkTreeHeaders.from(symlinkTree, CxxPreprocessables.IncludeType.LOCAL)); } } CxxPreprocessorInput cxxPreprocessorInput = cxxPreprocessorInputBuilder.build(); return Optional.of(cxxPreprocessorInput).map(metadataClass::cast); } case COMPILATION_DATABASE_DEPS: { return CxxDescriptionEnhancer.createCompilationDatabaseDependencies( buildTarget, cxxPlatforms, resolver, args) .map(metadataClass::cast); } } throw new IllegalStateException(String.format("unhandled metadata type: %s", type.getValue())); } @Override public ImmutableSortedSet<Flavor> addImplicitFlavors( ImmutableSortedSet<Flavor> argDefaultFlavors) { return addImplicitFlavorsForRuleTypes(argDefaultFlavors, Description.getBuildRuleType(this)); } public ImmutableSortedSet<Flavor> addImplicitFlavorsForRuleTypes( ImmutableSortedSet<Flavor> argDefaultFlavors, BuildRuleType... types) { Optional<Flavor> typeFlavor = LIBRARY_TYPE.getFlavor(argDefaultFlavors); Optional<Flavor> platformFlavor = getCxxPlatforms().getFlavor(argDefaultFlavors); LOG.debug("Got arg default type %s platform %s", typeFlavor, platformFlavor); for (BuildRuleType type : types) { ImmutableMap<String, Flavor> libraryDefaults = cxxBuckConfig.getDefaultFlavorsForRuleType(type); if (!typeFlavor.isPresent()) { typeFlavor = Optional.ofNullable(libraryDefaults.get(CxxBuckConfig.DEFAULT_FLAVOR_LIBRARY_TYPE)); } if (!platformFlavor.isPresent()) { platformFlavor = Optional.ofNullable(libraryDefaults.get(CxxBuckConfig.DEFAULT_FLAVOR_PLATFORM)); } } ImmutableSortedSet<Flavor> result = ImmutableSortedSet.of( // Default to static if not otherwise specified. typeFlavor.orElse(CxxDescriptionEnhancer.STATIC_FLAVOR), platformFlavor.orElse(defaultCxxPlatform.getFlavor())); LOG.debug("Got default flavors %s for rule types %s", result, Arrays.toString(types)); return result; } public interface CommonArg extends LinkableCxxConstructorArg { @Value.Default default SourceList getExportedHeaders() { return SourceList.EMPTY; } @Value.Default default PatternMatchedCollection<SourceList> getExportedPlatformHeaders() { return PatternMatchedCollection.of(); } ImmutableList<String> getExportedPreprocessorFlags(); @Value.Default default PatternMatchedCollection<ImmutableList<String>> getExportedPlatformPreprocessorFlags() { return PatternMatchedCollection.of(); } ImmutableMap<CxxSource.Type, ImmutableList<String>> getExportedLangPreprocessorFlags(); ImmutableList<StringWithMacros> getExportedLinkerFlags(); @Value.Default default PatternMatchedCollection<ImmutableList<StringWithMacros>> getExportedPlatformLinkerFlags() { return PatternMatchedCollection.of(); } @Value.NaturalOrder ImmutableSortedSet<BuildTarget> getExportedDeps(); @Value.Default default PatternMatchedCollection<ImmutableSortedSet<BuildTarget>> getExportedPlatformDeps() { return PatternMatchedCollection.of(); } Optional<Pattern> getSupportedPlatformsRegex(); Optional<String> getSoname(); Optional<Boolean> getForceStatic(); Optional<Boolean> getLinkWhole(); Optional<Boolean> getCanBeAsset(); Optional<NativeLinkable.Linkage> getPreferredLinkage(); Optional<Boolean> getXcodePublicHeadersSymlinks(); Optional<Boolean> getXcodePrivateHeadersSymlinks(); /** * extra_xcode_sources will add the files to the list of files to be compiled in the Xcode * target. */ ImmutableList<SourcePath> getExtraXcodeSources(); /** * extra_xcode_sources will add the files to the list of files in the project and won't add them * to an Xcode target. */ ImmutableList<SourcePath> getExtraXcodeFiles(); /** * Controls whether the headers of dependencies in "deps" is re-exported for compiling targets * that depend on this one. */ @Value.Default default boolean isReexportAllHeaderDependencies() { return true; } /** * These fields are passed through to SwiftLibrary for mixed C/Swift targets; they are not used * otherwise. */ Optional<SourcePath> getBridgingHeader(); Optional<String> getModuleName(); /** @return C/C++ deps which are propagated to dependents. */ @Value.Derived default CxxDeps getExportedCxxDeps() { return CxxDeps.builder() .addDeps(getExportedDeps()) .addPlatformDeps(getExportedPlatformDeps()) .build(); } /** * Override parent class's deps to include exported deps. * * @return the C/C++ deps this rule builds against. */ @Override @Value.Derived default CxxDeps getCxxDeps() { return CxxDeps.concat(getPrivateCxxDeps(), getExportedCxxDeps()); } } @BuckStyleImmutable @Value.Immutable interface AbstractCxxLibraryDescriptionArg extends CommonArg {} }