Java tutorial
/* * Copyright 2014-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.android.AndroidPackageable; import com.facebook.buck.android.AndroidPackageableCollector; import com.facebook.buck.io.ProjectFilesystem; import com.facebook.buck.model.BuildTarget; import com.facebook.buck.model.BuildTargets; import com.facebook.buck.model.Flavor; import com.facebook.buck.model.FlavorConvertible; import com.facebook.buck.model.FlavorDomain; import com.facebook.buck.model.ImmutableFlavor; import com.facebook.buck.model.MacroException; import com.facebook.buck.model.MacroFinder; import com.facebook.buck.model.Pair; import com.facebook.buck.parser.NoSuchBuildTargetException; import com.facebook.buck.rules.AbstractDescriptionArg; import com.facebook.buck.rules.BuildRule; import com.facebook.buck.rules.BuildRuleParams; import com.facebook.buck.rules.BuildRuleResolver; import com.facebook.buck.rules.BuildTargetSourcePath; import com.facebook.buck.rules.CellPathResolver; import com.facebook.buck.rules.Description; import com.facebook.buck.rules.ImplicitDepsInferringDescription; import com.facebook.buck.rules.PathSourcePath; import com.facebook.buck.rules.SourcePath; import com.facebook.buck.rules.SourcePathResolver; import com.facebook.buck.rules.SourcePathRuleFinder; import com.facebook.buck.rules.TargetGraph; import com.facebook.buck.rules.args.FileListableLinkerInputArg; 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.coercer.PatternMatchedCollection; import com.facebook.buck.rules.coercer.SourceList; import com.facebook.buck.rules.coercer.VersionMatchedCollection; import com.facebook.buck.rules.macros.LocationMacroExpander; import com.facebook.buck.rules.macros.MacroHandler; import com.facebook.buck.rules.macros.StringExpander; import com.facebook.buck.util.HumanReadableException; import com.facebook.buck.util.MoreCollectors; import com.facebook.buck.util.Optionals; import com.facebook.buck.versions.Version; import com.facebook.buck.versions.VersionPropagator; import com.facebook.infer.annotation.SuppressFieldNotInitialized; import com.google.common.base.Preconditions; import com.google.common.cache.LoadingCache; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableListMultimap; 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 java.nio.file.Path; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.regex.Pattern; public class PrebuiltCxxLibraryDescription implements Description<PrebuiltCxxLibraryDescription.Arg>, ImplicitDepsInferringDescription<PrebuiltCxxLibraryDescription.Arg>, VersionPropagator<PrebuiltCxxLibraryDescription.Arg> { private static final MacroFinder MACRO_FINDER = new MacroFinder(); enum Type implements FlavorConvertible { EXPORTED_HEADERS(CxxDescriptionEnhancer.EXPORTED_HEADER_SYMLINK_TREE_FLAVOR), SHARED( CxxDescriptionEnhancer.SHARED_FLAVOR), SHARED_INTERFACE(ImmutableFlavor.of("shared-interface")); 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); private final CxxBuckConfig cxxBuckConfig; private final FlavorDomain<CxxPlatform> cxxPlatforms; public PrebuiltCxxLibraryDescription(CxxBuckConfig cxxBuckConfig, FlavorDomain<CxxPlatform> cxxPlatforms) { this.cxxBuckConfig = cxxBuckConfig; this.cxxPlatforms = cxxPlatforms; } @Override public Arg createUnpopulatedConstructorArg() { return new Arg(); } // Using the {@code MACRO_FINDER} above, return the given string with any `platform` or // `location` macros replaced with the name of the given platform or build rule location. private static String expandMacros(MacroHandler handler, BuildTarget target, CellPathResolver cellNames, BuildRuleResolver ruleResolver, String arg) { try { return MACRO_FINDER.replace(handler.getMacroReplacers(target, cellNames, ruleResolver), arg, true); } catch (MacroException e) { throw new HumanReadableException("%s: %s", target, e.getMessage()); } } // Platform unlike most macro expanders needs access to the cxx build flavor. // Because of that it can't be like normal expanders. So just create a handler here. private static MacroHandler getMacroHandler(final Optional<CxxPlatform> cxxPlatform) { String flav = cxxPlatform.map(input -> input.getFlavor().toString()).orElse(""); return new MacroHandler( ImmutableMap.of("location", new LocationMacroExpander(), "platform", new StringExpander(flav))); } private static SourcePath getApplicableSourcePath(final BuildTarget target, final CellPathResolver cellRoots, final ProjectFilesystem filesystem, final BuildRuleResolver ruleResolver, final CxxPlatform cxxPlatform, Optional<String> versionSubDir, final String basePathString, final Optional<String> addedPathString) { ImmutableList<BuildRule> deps; MacroHandler handler = getMacroHandler(Optional.of(cxxPlatform)); try { deps = handler.extractBuildTimeDeps(target, cellRoots, ruleResolver, basePathString); } catch (MacroException e) { deps = ImmutableList.of(); } Path libDirPath = filesystem .getPath(expandMacros(handler, target, cellRoots, ruleResolver, basePathString)); if (versionSubDir.isPresent()) { libDirPath = filesystem.getPath(versionSubDir.get()).resolve(libDirPath); } // If there are no deps then this is just referencing a path that should already be there // So just expand the macros and return a PathSourcePath if (deps.isEmpty()) { Path resultPath = libDirPath; if (addedPathString.isPresent()) { resultPath = libDirPath .resolve(expandMacros(handler, target, cellRoots, ruleResolver, addedPathString.get())); } resultPath = target.getBasePath().resolve(resultPath); return new PathSourcePath(filesystem, resultPath); } // If we get here then this is referencing the output from a build rule. // This always return a BuildTargetSourcePath Path p = filesystem.resolve(libDirPath); if (addedPathString.isPresent()) { p = p.resolve(addedPathString.get()); } p = filesystem.relativize(p); return new BuildTargetSourcePath(deps.iterator().next().getBuildTarget(), p); } public static String getSoname(BuildTarget target, CellPathResolver cellNames, BuildRuleResolver ruleResolver, CxxPlatform cxxPlatform, Optional<String> soname, Optional<String> libName) { String unexpanded = soname.orElse(String.format("lib%s.%s", libName.orElse(target.getShortName()), cxxPlatform.getSharedLibraryExtension())); return expandMacros(getMacroHandler(Optional.of(cxxPlatform)), target, cellNames, ruleResolver, unexpanded); } private static SourcePath getLibraryPath(BuildTarget target, CellPathResolver cellRoots, ProjectFilesystem filesystem, BuildRuleResolver ruleResolver, CxxPlatform cxxPlatform, Optional<String> versionSubDir, Optional<String> libDir, Optional<String> libName, String suffix) { String libDirString = libDir.orElse("lib"); String fileNameString = String.format("lib%s%s", libName.orElse(target.getShortName()), suffix); return getApplicableSourcePath(target, cellRoots, filesystem, ruleResolver, cxxPlatform, versionSubDir, libDirString, Optional.of(fileNameString)); } static SourcePath getSharedLibraryPath(BuildTarget target, CellPathResolver cellNames, ProjectFilesystem filesystem, BuildRuleResolver ruleResolver, CxxPlatform cxxPlatform, Optional<String> versionSubDir, Optional<String> libDir, Optional<String> libName) { return getLibraryPath(target, cellNames, filesystem, ruleResolver, cxxPlatform, versionSubDir, libDir, libName, String.format(".%s", cxxPlatform.getSharedLibraryExtension())); } static SourcePath getStaticLibraryPath(BuildTarget target, CellPathResolver cellNames, ProjectFilesystem filesystem, BuildRuleResolver ruleResolver, CxxPlatform cxxPlatform, Optional<String> versionSubDir, Optional<String> libDir, Optional<String> libName) { return getLibraryPath(target, cellNames, filesystem, ruleResolver, cxxPlatform, versionSubDir, libDir, libName, ".a"); } private static SourcePath getStaticPicLibraryPath(BuildTarget target, CellPathResolver cellNames, final ProjectFilesystem filesystem, BuildRuleResolver ruleResolver, CxxPlatform cxxPlatform, Optional<String> versionSubDir, Optional<String> libDir, Optional<String> libName) { return getLibraryPath(target, cellNames, filesystem, ruleResolver, cxxPlatform, versionSubDir, libDir, libName, "_pic.a"); } /** * @return a {@link HeaderSymlinkTree} for the exported headers of this prebuilt C/C++ library. */ private static <A extends Arg> HeaderSymlinkTree createExportedHeaderSymlinkTreeBuildRule( BuildRuleParams params, BuildRuleResolver resolver, CxxPlatform cxxPlatform, A args) { return CxxDescriptionEnhancer.createHeaderSymlinkTree(params, resolver, new SourcePathResolver(new SourcePathRuleFinder(resolver)), cxxPlatform, parseExportedHeaders(params, resolver, cxxPlatform, args), HeaderVisibility.PUBLIC, true); } private static <A extends Arg> ImmutableMap<Path, SourcePath> parseExportedHeaders(BuildRuleParams params, BuildRuleResolver resolver, CxxPlatform cxxPlatform, A args) { ImmutableMap.Builder<String, SourcePath> headers = ImmutableMap.builder(); SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(resolver)); CxxDescriptionEnhancer.putAllHeaders(args.exportedHeaders, headers, pathResolver, "exported_headers", params.getBuildTarget()); for (SourceList sourceList : args.exportedPlatformHeaders .getMatchingValues(cxxPlatform.getFlavor().toString())) { CxxDescriptionEnhancer.putAllHeaders(sourceList, headers, pathResolver, "exported_platform_headers", params.getBuildTarget()); } return CxxPreprocessables.resolveHeaderMap( args.headerNamespace.map(Paths::get).orElse(params.getBuildTarget().getBasePath()), headers.build()); } /** * @return a {@link CxxLink} rule for a shared library version of this prebuilt C/C++ library. */ private <A extends Arg> BuildRule createSharedLibraryBuildRule(BuildRuleParams params, BuildRuleResolver ruleResolver, CxxPlatform cxxPlatform, Optional<ImmutableMap<BuildTarget, Version>> selectedVersions, A args) throws NoSuchBuildTargetException { SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(ruleResolver); final SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder); BuildTarget target = params.getBuildTarget(); String soname = getSoname(target, params.getCellRoots(), ruleResolver, cxxPlatform, args.soname, args.libName); Optional<String> versionSubDir = selectedVersions.isPresent() && args.versionedSubDir.isPresent() ? Optional.of(args.versionedSubDir.get().getOnlyMatchingValue(selectedVersions.get())) : Optional.empty(); // Use the static PIC variant, if available. SourcePath staticLibraryPath = getStaticPicLibraryPath(target, params.getCellRoots(), params.getProjectFilesystem(), ruleResolver, cxxPlatform, versionSubDir, args.libDir, args.libName); if (!params.getProjectFilesystem().exists(pathResolver.getAbsolutePath(staticLibraryPath))) { staticLibraryPath = getStaticLibraryPath(target, params.getCellRoots(), params.getProjectFilesystem(), ruleResolver, cxxPlatform, versionSubDir, args.libDir, args.libName); } // Otherwise, we need to build it from the static lib. BuildTarget sharedTarget = BuildTarget.builder(params.getBuildTarget()) .addFlavors(CxxDescriptionEnhancer.SHARED_FLAVOR).build(); // If not, setup a single link rule to link it from the static lib. Path builtSharedLibraryPath = BuildTargets.getGenPath(params.getProjectFilesystem(), sharedTarget, "%s") .resolve(soname); return CxxLinkableEnhancer .createCxxLinkableBuildRule( cxxBuckConfig, cxxPlatform, params .appendExtraDeps( getBuildRules(params.getBuildTarget(), params.getCellRoots(), ruleResolver, Optionals.toStream(args.libDir) .collect(MoreCollectors.toImmutableList()))) .appendExtraDeps(getBuildRules(params.getBuildTarget(), params.getCellRoots(), ruleResolver, args.includeDirs)), ruleResolver, pathResolver, ruleFinder, sharedTarget, Linker.LinkType.SHARED, Optional.of(soname), builtSharedLibraryPath, Linker.LinkableDepType.SHARED, FluentIterable.from(params.getDeps()).filter(NativeLinkable.class), Optional.empty(), Optional.empty(), ImmutableSet.of(), NativeLinkableInput.builder() .addAllArgs(StringArg.from(CxxFlags.getFlags(args.exportedLinkerFlags, args.exportedPlatformLinkerFlags, cxxPlatform))) .addAllArgs(cxxPlatform.getLd().resolve(ruleResolver) .linkWhole(new SourcePathArg(pathResolver, staticLibraryPath))) .build()); } /** * Makes sure all build rules needed to produce the shared library are added to the action * graph. * * @return the {@link SourcePath} representing the actual shared library. */ private SourcePath requireSharedLibrary(BuildTarget target, BuildRuleResolver resolver, SourcePathResolver pathResolver, CellPathResolver cellRoots, ProjectFilesystem filesystem, CxxPlatform cxxPlatform, Optional<String> versionSubdir, Arg args) throws NoSuchBuildTargetException { SourcePath sharedLibraryPath = PrebuiltCxxLibraryDescription.getSharedLibraryPath(target, cellRoots, filesystem, resolver, cxxPlatform, versionSubdir, args.libDir, args.libName); // If the shared library is prebuilt, just return a reference to it. // TODO(alisdair04): this code misbehaves. whether the file exists should have been figured // out earlier during parsing/target graph creation, or it should be later when steps being // produced. This is preventing distributed build loading files lazily. if (sharedLibraryPath instanceof BuildTargetSourcePath || filesystem.exists(pathResolver.getAbsolutePath(sharedLibraryPath))) { return sharedLibraryPath; } // Otherwise, generate it's build rule. BuildRule sharedLibrary = resolver.requireRule( target.withAppendedFlavors(cxxPlatform.getFlavor(), CxxDescriptionEnhancer.SHARED_FLAVOR)); return new BuildTargetSourcePath(sharedLibrary.getBuildTarget()); } private <A extends Arg> BuildRule createSharedLibraryInterface(BuildTarget baseTarget, BuildRuleParams baseParams, BuildRuleResolver resolver, CxxPlatform cxxPlatform, Optional<String> versionSubdir, A args) throws NoSuchBuildTargetException { if (!args.supportsSharedLibraryInterface) { throw new HumanReadableException("%s: rule does not support shared library interfaces", baseTarget, cxxPlatform.getFlavor()); } 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()); } SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver); SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder); SourcePath sharedLibrary = requireSharedLibrary(baseTarget.getBuildTarget(), resolver, pathResolver, baseParams.getCellRoots(), baseParams.getProjectFilesystem(), cxxPlatform, versionSubdir, args); return factory.get().createSharedInterfaceLibrary( baseTarget.withAppendedFlavors(Type.SHARED_INTERFACE.getFlavor(), cxxPlatform.getFlavor()), baseParams, resolver, pathResolver, ruleFinder, sharedLibrary); } @Override public <A extends Arg> BuildRule createBuildRule(TargetGraph targetGraph, final BuildRuleParams params, final BuildRuleResolver ruleResolver, final A args) throws NoSuchBuildTargetException { // See if we're building a particular "type" of this library, and if so, extract // it as an enum. Optional<Map.Entry<Flavor, Type>> type = LIBRARY_TYPE.getFlavorAndValue(params.getBuildTarget()); Optional<Map.Entry<Flavor, CxxPlatform>> platform = cxxPlatforms.getFlavorAndValue(params.getBuildTarget()); Optional<ImmutableMap<BuildTarget, Version>> selectedVersions = targetGraph.get(params.getBuildTarget()) .getSelectedVersions(); final Optional<String> versionSubdir = selectedVersions.isPresent() && args.versionedSubDir.isPresent() ? Optional.of(args.versionedSubDir.get().getOnlyMatchingValue(selectedVersions.get())) : Optional.empty(); // If we *are* building a specific type of this lib, call into the type specific // rule builder methods. Currently, we only support building a shared lib from the // pre-existing static lib, which we do here. if (type.isPresent()) { Preconditions.checkState(platform.isPresent()); BuildTarget baseTarget = params.getBuildTarget().withoutFlavors(type.get().getKey(), platform.get().getKey()); if (type.get().getValue() == Type.EXPORTED_HEADERS) { return createExportedHeaderSymlinkTreeBuildRule(params, ruleResolver, platform.get().getValue(), args); } else if (type.get().getValue() == Type.SHARED) { return createSharedLibraryBuildRule(params, ruleResolver, platform.get().getValue(), selectedVersions, args); } else if (type.get().getValue() == Type.SHARED_INTERFACE) { return createSharedLibraryInterface(baseTarget, params, ruleResolver, platform.get().getValue(), versionSubdir, args); } } if (selectedVersions.isPresent() && args.versionedSubDir.isPresent()) { ImmutableList<String> versionSubDirs = args.versionedSubDir .orElse(VersionMatchedCollection.<String>of()).getMatchingValues(selectedVersions.get()); if (versionSubDirs.size() != 1) { throw new HumanReadableException("%s: could not get a single version sub dir: %s, %s, %s", params.getBuildTarget(), args.versionedSubDir, versionSubDirs, selectedVersions); } } // Otherwise, we return the generic placeholder of this library, that dependents can use // get the real build rules via querying the action graph. final SourcePathResolver pathResolver = new SourcePathResolver(new SourcePathRuleFinder(ruleResolver)); final boolean headerOnly = args.headerOnly.orElse(false); final boolean forceStatic = args.forceStatic.orElse(false); return new PrebuiltCxxLibrary(params, pathResolver) { private final Map<Pair<Flavor, Linker.LinkableDepType>, NativeLinkableInput> nativeLinkableCache = new HashMap<>(); private final LoadingCache<CxxPreprocessables.CxxPreprocessorInputCacheKey, ImmutableMap<BuildTarget, CxxPreprocessorInput>> transitiveCxxPreprocessorInputCache = CxxPreprocessables .getTransitiveCxxPreprocessorInputCache(this); private boolean hasHeaders(CxxPlatform cxxPlatform) { if (!args.exportedHeaders.isEmpty()) { return true; } for (SourceList sourceList : args.exportedPlatformHeaders .getMatchingValues(cxxPlatform.getFlavor().toString())) { if (!sourceList.isEmpty()) { return true; } } return false; } private ImmutableListMultimap<CxxSource.Type, String> getExportedPreprocessorFlags( CxxPlatform cxxPlatform) { return CxxFlags.getLanguageFlags(args.exportedPreprocessorFlags, args.exportedPlatformPreprocessorFlags, args.exportedLangPreprocessorFlags, cxxPlatform); } @Override public ImmutableList<String> getExportedLinkerFlags(CxxPlatform cxxPlatform) { return CxxFlags.getFlags(args.exportedLinkerFlags, args.exportedPlatformLinkerFlags, cxxPlatform); } private String getSoname(CxxPlatform cxxPlatform) { return PrebuiltCxxLibraryDescription.getSoname(getBuildTarget(), params.getCellRoots(), ruleResolver, cxxPlatform, args.soname, args.libName); } private boolean isPlatformSupported(CxxPlatform cxxPlatform) { return !args.supportedPlatformsRegex.isPresent() || args.supportedPlatformsRegex.get().matcher(cxxPlatform.getFlavor().toString()).find(); } /** * Makes sure all build rules needed to produce the shared library are added to the action * graph. * * @return the {@link SourcePath} representing the actual shared library. */ private SourcePath requireSharedLibrary(CxxPlatform cxxPlatform, boolean link) throws NoSuchBuildTargetException { if (link && args.supportsSharedLibraryInterface && cxxPlatform.getSharedLibraryInterfaceFactory().isPresent()) { BuildTarget target = params.getBuildTarget().withAppendedFlavors(cxxPlatform.getFlavor(), Type.SHARED_INTERFACE.getFlavor()); ruleResolver.requireRule(target); return new BuildTargetSourcePath(target); } return PrebuiltCxxLibraryDescription.this.requireSharedLibrary(params.getBuildTarget(), ruleResolver, pathResolver, params.getCellRoots(), params.getProjectFilesystem(), cxxPlatform, versionSubdir, args); } /** * @return the {@link Optional} containing a {@link SourcePath} representing the actual * static PIC library. */ private Optional<SourcePath> getStaticPicLibrary(CxxPlatform cxxPlatform) { SourcePath staticPicLibraryPath = PrebuiltCxxLibraryDescription.getStaticPicLibraryPath( getBuildTarget(), params.getCellRoots(), params.getProjectFilesystem(), ruleResolver, cxxPlatform, versionSubdir, args.libDir, args.libName); if (params.getProjectFilesystem().exists(pathResolver.getAbsolutePath(staticPicLibraryPath))) { return Optional.of(staticPicLibraryPath); } // If a specific static-pic variant isn't available, then just use the static variant. SourcePath staticLibraryPath = PrebuiltCxxLibraryDescription.getStaticLibraryPath(getBuildTarget(), params.getCellRoots(), getProjectFilesystem(), ruleResolver, cxxPlatform, versionSubdir, args.libDir, args.libName); if (params.getProjectFilesystem().exists(pathResolver.getAbsolutePath(staticLibraryPath))) { return Optional.of(staticLibraryPath); } return Optional.empty(); } @Override public Iterable<? extends CxxPreprocessorDep> getCxxPreprocessorDeps(CxxPlatform cxxPlatform) { if (!isPlatformSupported(cxxPlatform)) { return ImmutableList.of(); } return FluentIterable.from(getDeps()).filter(CxxPreprocessorDep.class); } @Override public CxxPreprocessorInput getCxxPreprocessorInput(final CxxPlatform cxxPlatform, HeaderVisibility headerVisibility) throws NoSuchBuildTargetException { CxxPreprocessorInput.Builder builder = CxxPreprocessorInput.builder(); switch (headerVisibility) { case PUBLIC: if (hasHeaders(cxxPlatform)) { CxxPreprocessables.addHeaderSymlinkTree(builder, getBuildTarget(), ruleResolver, cxxPlatform, headerVisibility, CxxPreprocessables.IncludeType.SYSTEM); } builder.putAllPreprocessorFlags( Preconditions.checkNotNull(getExportedPreprocessorFlags(cxxPlatform))); builder.addAllFrameworks(args.frameworks); final Iterable<SourcePath> includePaths = args.includeDirs.stream() .map(input -> PrebuiltCxxLibraryDescription.getApplicableSourcePath( params.getBuildTarget(), params.getCellRoots(), params.getProjectFilesystem(), ruleResolver, cxxPlatform, versionSubdir, input, Optional.empty())) .collect(MoreCollectors.toImmutableList()); for (SourcePath includePath : includePaths) { builder.addIncludes(CxxHeadersDir.of(CxxPreprocessables.IncludeType.SYSTEM, includePath)); } return builder.build(); case PRIVATE: return builder.build(); } // We explicitly don't put this in a default statement because we // want the compiler to warn if someone modifies the HeaderVisibility enum. throw new RuntimeException("Invalid header visibility: " + headerVisibility); } @Override public Optional<HeaderSymlinkTree> getExportedHeaderSymlinkTree(CxxPlatform cxxPlatform) { if (hasHeaders(cxxPlatform)) { return Optional.of(CxxPreprocessables.requireHeaderSymlinkTreeForLibraryTarget(ruleResolver, getBuildTarget(), cxxPlatform.getFlavor())); } else { return Optional.empty(); } } @Override public ImmutableMap<BuildTarget, CxxPreprocessorInput> getTransitiveCxxPreprocessorInput( CxxPlatform cxxPlatform, HeaderVisibility headerVisibility) throws NoSuchBuildTargetException { return transitiveCxxPreprocessorInputCache .getUnchecked(ImmutableCxxPreprocessorInputCacheKey.of(cxxPlatform, headerVisibility)); } @Override public Iterable<NativeLinkable> getNativeLinkableDeps() { return getDeclaredDeps().stream().filter(r -> r instanceof NativeLinkable) .map(r -> (NativeLinkable) r).collect(MoreCollectors.toImmutableList()); } @Override public Iterable<NativeLinkable> getNativeLinkableDepsForPlatform(CxxPlatform cxxPlatform) { if (!isPlatformSupported(cxxPlatform)) { return ImmutableList.of(); } return getNativeLinkableDeps(); } @Override public Iterable<? extends NativeLinkable> getNativeLinkableExportedDeps() { return args.exportedDeps.stream().map(ruleResolver::getRule) .filter(r -> r instanceof NativeLinkable).map(r -> (NativeLinkable) r) .collect(MoreCollectors.toImmutableList()); } @Override public Iterable<? extends NativeLinkable> getNativeLinkableExportedDepsForPlatform( CxxPlatform cxxPlatform) { if (!isPlatformSupported(cxxPlatform)) { return ImmutableList.of(); } return getNativeLinkableExportedDeps(); } private NativeLinkableInput getNativeLinkableInputUncached(CxxPlatform cxxPlatform, Linker.LinkableDepType type) throws NoSuchBuildTargetException { if (!isPlatformSupported(cxxPlatform)) { return NativeLinkableInput.of(); } // Build the library path and linker arguments that we pass through the // {@link NativeLinkable} interface for linking. ImmutableList.Builder<com.facebook.buck.rules.args.Arg> linkerArgsBuilder = ImmutableList.builder(); linkerArgsBuilder .addAll(StringArg.from(Preconditions.checkNotNull(getExportedLinkerFlags(cxxPlatform)))); if (!headerOnly) { if (type == Linker.LinkableDepType.SHARED) { Preconditions.checkState(getPreferredLinkage(cxxPlatform) != Linkage.STATIC); final SourcePath sharedLibrary = requireSharedLibrary(cxxPlatform, true); if (args.linkWithoutSoname) { if (!(sharedLibrary instanceof PathSourcePath)) { throw new HumanReadableException("%s: can only link prebuilt DSOs without sonames", getBuildTarget()); } linkerArgsBuilder.add(new RelativeLinkArg((PathSourcePath) sharedLibrary)); } else { linkerArgsBuilder .add(new SourcePathArg(pathResolver, requireSharedLibrary(cxxPlatform, true))); } } else { Preconditions.checkState(getPreferredLinkage(cxxPlatform) != Linkage.SHARED); SourcePath staticLibraryPath = type == Linker.LinkableDepType.STATIC_PIC ? getStaticPicLibrary(cxxPlatform).get() : PrebuiltCxxLibraryDescription.getStaticLibraryPath(getBuildTarget(), params.getCellRoots(), params.getProjectFilesystem(), ruleResolver, cxxPlatform, versionSubdir, args.libDir, args.libName); SourcePathArg staticLibrary = new SourcePathArg(pathResolver, staticLibraryPath); if (args.linkWhole) { Linker linker = cxxPlatform.getLd().resolve(ruleResolver); linkerArgsBuilder.addAll(linker.linkWhole(staticLibrary)); } else { linkerArgsBuilder.add(FileListableLinkerInputArg.withSourcePathArg(staticLibrary)); } } } final ImmutableList<com.facebook.buck.rules.args.Arg> linkerArgs = linkerArgsBuilder.build(); return NativeLinkableInput.of(linkerArgs, args.frameworks, args.libraries); } @Override public NativeLinkableInput getNativeLinkableInput(CxxPlatform cxxPlatform, Linker.LinkableDepType type) throws NoSuchBuildTargetException { Pair<Flavor, Linker.LinkableDepType> key = new Pair<>(cxxPlatform.getFlavor(), type); NativeLinkableInput input = nativeLinkableCache.get(key); if (input == null) { input = getNativeLinkableInputUncached(cxxPlatform, type); nativeLinkableCache.put(key, input); } return input; } @Override public NativeLinkable.Linkage getPreferredLinkage(CxxPlatform cxxPlatform) { if (headerOnly) { return Linkage.ANY; } if (forceStatic) { return Linkage.STATIC; } if (args.provided || !getStaticPicLibrary(cxxPlatform).isPresent()) { return Linkage.SHARED; } return Linkage.ANY; } @Override public Iterable<AndroidPackageable> getRequiredPackageables() { return AndroidPackageableCollector.getPackageableRules(params.getDeps()); } @Override public void addToCollector(AndroidPackageableCollector collector) { if (args.canBeAsset) { collector.addNativeLinkableAsset(this); } else { collector.addNativeLinkable(this); } } @Override public ImmutableMap<String, SourcePath> getSharedLibraries(CxxPlatform cxxPlatform) throws NoSuchBuildTargetException { if (!isPlatformSupported(cxxPlatform)) { return ImmutableMap.of(); } String resolvedSoname = getSoname(cxxPlatform); ImmutableMap.Builder<String, SourcePath> solibs = ImmutableMap.builder(); if (!headerOnly && !args.provided) { SourcePath sharedLibrary = requireSharedLibrary(cxxPlatform, false); solibs.put(resolvedSoname, sharedLibrary); } return solibs.build(); } @Override public Optional<NativeLinkTarget> getNativeLinkTarget(CxxPlatform cxxPlatform) { if (getPreferredLinkage(cxxPlatform) == Linkage.SHARED) { return Optional.empty(); } return Optional.of(new NativeLinkTarget() { @Override public BuildTarget getBuildTarget() { return params.getBuildTarget(); } @Override public NativeLinkTargetMode getNativeLinkTargetMode(CxxPlatform cxxPlatform) { return NativeLinkTargetMode.library(getSoname(cxxPlatform)); } @Override public Iterable<? extends NativeLinkable> getNativeLinkTargetDeps(CxxPlatform cxxPlatform) { return Iterables.concat(getNativeLinkableDepsForPlatform(cxxPlatform), getNativeLinkableExportedDepsForPlatform(cxxPlatform)); } @Override public NativeLinkableInput getNativeLinkTargetInput(CxxPlatform cxxPlatform) throws NoSuchBuildTargetException { return NativeLinkableInput.builder() .addAllArgs(StringArg.from(getExportedLinkerFlags(cxxPlatform))) .addAllArgs(cxxPlatform.getLd().resolve(ruleResolver).linkWhole( new SourcePathArg(pathResolver, getStaticPicLibrary(cxxPlatform).get()))) .build(); } @Override public Optional<Path> getNativeLinkTargetOutputPath(CxxPlatform cxxPlatform) { return Optional.empty(); } }); } }; } @Override public Iterable<BuildTarget> findDepsForTargetFromConstructorArgs(BuildTarget buildTarget, CellPathResolver cellRoots, PrebuiltCxxLibraryDescription.Arg constructorArg) { ImmutableSet.Builder<BuildTarget> targets = ImmutableSet.builder(); if (constructorArg.libDir.isPresent()) { addDepsFromParam(buildTarget, cellRoots, constructorArg.libDir.get(), targets); } for (String include : constructorArg.includeDirs) { addDepsFromParam(buildTarget, cellRoots, include, targets); } return targets.build(); } private ImmutableList<BuildRule> getBuildRules(BuildTarget target, CellPathResolver cellNames, BuildRuleResolver ruleResolver, Iterable<String> paramValues) { ImmutableList.Builder<BuildRule> builder = ImmutableList.builder(); MacroHandler macroHandler = getMacroHandler(Optional.empty()); for (String p : paramValues) { try { builder.addAll(macroHandler.extractBuildTimeDeps(target, cellNames, ruleResolver, p)); } catch (MacroException e) { throw new HumanReadableException(e, "%s : %s in \"%s\"", target, e.getMessage(), p); } } return builder.build(); } private void addDepsFromParam(BuildTarget target, CellPathResolver cellNames, String paramValue, ImmutableSet.Builder<BuildTarget> targets) { try { // doesn't matter that the platform expander doesn't do anything. MacroHandler macroHandler = getMacroHandler(Optional.empty()); // Then get the parse time deps. targets.addAll(macroHandler.extractParseTimeDeps(target, cellNames, paramValue)); } catch (MacroException e) { throw new HumanReadableException(e, "%s : %s in \"%s\"", target, e.getMessage(), paramValue); } } @SuppressFieldNotInitialized public static class Arg extends AbstractDescriptionArg { public ImmutableList<String> includeDirs = ImmutableList.of(); public Optional<String> libName; public Optional<String> libDir; public Optional<Boolean> headerOnly; public SourceList exportedHeaders = SourceList.EMPTY; public PatternMatchedCollection<SourceList> exportedPlatformHeaders = PatternMatchedCollection.of(); public Optional<String> headerNamespace; public boolean provided = false; public boolean linkWhole = false; public Optional<Boolean> forceStatic; public ImmutableList<String> exportedPreprocessorFlags = ImmutableList.of(); public PatternMatchedCollection<ImmutableList<String>> exportedPlatformPreprocessorFlags = PatternMatchedCollection .of(); public ImmutableMap<CxxSource.Type, ImmutableList<String>> exportedLangPreprocessorFlags = ImmutableMap .of(); public ImmutableList<String> exportedLinkerFlags = ImmutableList.of(); public PatternMatchedCollection<ImmutableList<String>> exportedPlatformLinkerFlags = PatternMatchedCollection .of(); public Optional<String> soname; public Boolean linkWithoutSoname = false; public Boolean canBeAsset = false; public ImmutableSortedSet<FrameworkPath> frameworks = ImmutableSortedSet.of(); public ImmutableSortedSet<FrameworkPath> libraries = ImmutableSortedSet.of(); public ImmutableSortedSet<BuildTarget> deps = ImmutableSortedSet.of(); public ImmutableSortedSet<BuildTarget> exportedDeps = ImmutableSortedSet.of(); public Optional<Pattern> supportedPlatformsRegex; public Optional<VersionMatchedCollection<String>> versionedSubDir; public boolean supportsSharedLibraryInterface = false; } }