blob: f430ec775d454599e3f22f16405598e3753fcc90 [file] [log] [blame]
/*
* 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;
}
}