| /* |
| * 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.model.BuildTarget; |
| import com.facebook.buck.model.BuildTargets; |
| import com.facebook.buck.model.Flavor; |
| import com.facebook.buck.model.FlavorDomain; |
| import com.facebook.buck.model.FlavorDomainException; |
| import com.facebook.buck.model.Pair; |
| 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.Description; |
| import com.facebook.buck.rules.ImmutableBuildRuleType; |
| import com.facebook.buck.rules.PathSourcePath; |
| import com.facebook.buck.rules.SourcePath; |
| import com.facebook.buck.rules.SourcePathResolver; |
| import com.facebook.buck.rules.macros.MacroException; |
| import com.facebook.buck.rules.macros.MacroFinder; |
| import com.facebook.buck.rules.macros.MacroReplacer; |
| import com.facebook.buck.util.HumanReadableException; |
| import com.facebook.infer.annotation.SuppressFieldNotInitialized; |
| import com.google.common.base.Function; |
| import com.google.common.base.Optional; |
| import com.google.common.base.Preconditions; |
| import com.google.common.collect.FluentIterable; |
| 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 java.nio.file.Path; |
| import java.util.Map; |
| |
| public class PrebuiltCxxLibraryDescription |
| implements Description<PrebuiltCxxLibraryDescription.Arg> { |
| |
| private static final MacroFinder MACRO_FINDER = new MacroFinder(); |
| |
| private static enum Type { |
| SHARED, |
| } |
| |
| private static final FlavorDomain<Type> LIBRARY_TYPE = |
| new FlavorDomain<>( |
| "C/C++ Library Type", |
| ImmutableMap.of( |
| CxxDescriptionEnhancer.SHARED_FLAVOR, Type.SHARED)); |
| |
| public static final BuildRuleType TYPE = ImmutableBuildRuleType.of("prebuilt_cxx_library"); |
| |
| private final FlavorDomain<CxxPlatform> cxxPlatforms; |
| |
| public PrebuiltCxxLibraryDescription(FlavorDomain<CxxPlatform> cxxPlatforms) { |
| this.cxxPlatforms = cxxPlatforms; |
| } |
| |
| @Override |
| public BuildRuleType getBuildRuleType() { |
| return TYPE; |
| } |
| |
| @Override |
| public Arg createUnpopulatedConstructorArg() { |
| return new Arg(); |
| } |
| |
| // Using the {@code MACRO_FINDER} above, return the given string with any `platform` macros |
| // replaced with the name of the given platform. |
| private static String expandPlatform( |
| BuildTarget target, |
| final CxxPlatform cxxPlatform, |
| String arg) { |
| try { |
| return MACRO_FINDER.replace( |
| ImmutableMap.<String, MacroReplacer>of( |
| "platform", |
| new MacroReplacer() { |
| @Override |
| public String replace(String input) throws MacroException { |
| return cxxPlatform.getFlavor().toString(); |
| } |
| }), |
| arg); |
| } catch (MacroException e) { |
| throw new HumanReadableException("%s: %s", target, e.getMessage()); |
| } |
| } |
| |
| // Resolve the given optional arg, falling back to the given default if not present and |
| // expanding platform macros otherwise. |
| private static String getOptionalArg( |
| BuildTarget target, |
| CxxPlatform cxxPlatform, |
| Optional<String> arg, |
| String defaultValue) { |
| if (!arg.isPresent()) { |
| return defaultValue; |
| } |
| return expandPlatform(target, cxxPlatform, arg.get()); |
| } |
| |
| private static String getLibDir( |
| BuildTarget target, |
| CxxPlatform cxxPlatform, |
| Optional<String> libDir) { |
| return getOptionalArg(target, cxxPlatform, libDir, "lib"); |
| } |
| |
| private static String getLibName( |
| BuildTarget target, |
| CxxPlatform cxxPlatform, |
| Optional<String> libName) { |
| return getOptionalArg(target, cxxPlatform, libName, target.getShortName()); |
| } |
| |
| public static String getSoname( |
| BuildTarget target, |
| CxxPlatform cxxPlatform, |
| Optional<String> soname, |
| Optional<String> libName) { |
| return getOptionalArg( |
| target, |
| cxxPlatform, |
| soname, |
| String.format("lib%s.so", getLibName(target, cxxPlatform, libName))); |
| } |
| |
| private static Path getLibraryPath( |
| BuildTarget target, |
| CxxPlatform cxxPlatform, |
| Optional<String> libDir, |
| Optional<String> libName, |
| String suffix) { |
| return target.getBasePath() |
| .resolve(getLibDir(target, cxxPlatform, libDir)) |
| .resolve(String.format("lib%s%s", getLibName(target, cxxPlatform, libName), suffix)); |
| } |
| |
| public static Path getSharedLibraryPath( |
| BuildTarget target, |
| CxxPlatform cxxPlatform, |
| Optional<String> libDir, |
| Optional<String> libName) { |
| return getLibraryPath(target, cxxPlatform, libDir, libName, ".so"); |
| } |
| |
| public static Path getStaticLibraryPath( |
| BuildTarget target, |
| CxxPlatform cxxPlatform, |
| Optional<String> libDir, |
| Optional<String> libName) { |
| return getLibraryPath(target, cxxPlatform, libDir, libName, ".a"); |
| } |
| |
| private <A extends Arg> BuildRule createSharedLibraryBuildRule( |
| BuildRuleParams params, |
| BuildRuleResolver ruleResolver, |
| CxxPlatform cxxPlatform, |
| A args) { |
| |
| SourcePathResolver pathResolver = new SourcePathResolver(ruleResolver); |
| |
| BuildTarget target = params.getBuildTarget(); |
| String soname = getSoname(target, cxxPlatform, args.soname, args.libName); |
| Path staticLibraryPath = getStaticLibraryPath(target, cxxPlatform, 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.getBinPath(sharedTarget, "%s").resolve(soname); |
| return CxxLinkableEnhancer.createCxxLinkableBuildRule( |
| cxxPlatform, |
| params, |
| pathResolver, |
| /* extraCxxLdFlags */ ImmutableList.<String>of(), |
| /* extraLdFlags */ ImmutableList.<String>of(), |
| sharedTarget, |
| Linker.LinkType.SHARED, |
| Optional.of(soname), |
| builtSharedLibraryPath, |
| ImmutableList.<SourcePath>of( |
| new PathSourcePath(params.getProjectFilesystem(), staticLibraryPath)), |
| Linker.LinkableDepType.SHARED, |
| params.getDeps()); |
| } |
| |
| @Override |
| public <A extends Arg> BuildRule createBuildRule( |
| BuildRuleParams params, |
| BuildRuleResolver resolver, |
| A args) { |
| |
| // 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; |
| Optional<Map.Entry<Flavor, CxxPlatform>> platform; |
| try { |
| type = LIBRARY_TYPE.getFlavorAndValue( |
| ImmutableSet.copyOf(params.getBuildTarget().getFlavors())); |
| platform = cxxPlatforms.getFlavorAndValue( |
| ImmutableSet.copyOf(params.getBuildTarget().getFlavors())); |
| } catch (FlavorDomainException e) { |
| throw new HumanReadableException("%s: %s", params.getBuildTarget(), e.getMessage()); |
| } |
| |
| // 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(type.get().getValue() == Type.SHARED); |
| Preconditions.checkState(platform.isPresent()); |
| return createSharedLibraryBuildRule(params, resolver, platform.get().getValue(), args); |
| } |
| |
| // Otherwise, we return the generic placeholder of this library, that dependents can use |
| // get the real build rules via querying the action graph. |
| final BuildTarget target = params.getBuildTarget(); |
| |
| // Resolve all the target-base-path-relative include paths to their full paths. |
| Function<String, Path> fullPathFn = new Function<String, Path>() { |
| @Override |
| public Path apply(String input) { |
| return target.getBasePath().resolve(input); |
| } |
| }; |
| final ImmutableList<Path> includeDirs = FluentIterable |
| .from(args.includeDirs.or(ImmutableList.of("include"))) |
| .transform(fullPathFn) |
| .toList(); |
| |
| SourcePathResolver pathResolver = new SourcePathResolver(resolver); |
| return new PrebuiltCxxLibrary( |
| params, |
| resolver, |
| pathResolver, |
| includeDirs, |
| args.libDir, |
| args.libName, |
| args.linkerFlags.get(), |
| args.platformLinkerFlags.get(), |
| args.soname, |
| args.headerOnly.or(false), |
| args.linkWhole.or(false), |
| args.provided.or(false)); |
| } |
| |
| @SuppressFieldNotInitialized |
| public static class Arg { |
| public Optional<ImmutableList<String>> includeDirs; |
| public Optional<String> libName; |
| public Optional<String> libDir; |
| public Optional<Boolean> headerOnly; |
| public Optional<Boolean> provided; |
| public Optional<Boolean> linkWhole; |
| public Optional<ImmutableList<String>> linkerFlags; |
| public Optional<ImmutableList<Pair<String, ImmutableList<String>>>> platformLinkerFlags; |
| public Optional<String> soname; |
| public Optional<ImmutableSortedSet<BuildTarget>> deps; |
| } |
| |
| } |