/* * Copyright 2015-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.halide; import com.facebook.buck.cxx.Archive; import com.facebook.buck.cxx.CxxBinary; import com.facebook.buck.cxx.CxxBinaryDescription; import com.facebook.buck.cxx.CxxBuckConfig; import com.facebook.buck.cxx.CxxDescriptionEnhancer; import com.facebook.buck.cxx.CxxFlags; import com.facebook.buck.cxx.CxxLinkAndCompileRules; import com.facebook.buck.cxx.CxxPlatform; import com.facebook.buck.cxx.CxxPlatforms; import com.facebook.buck.cxx.CxxSource; import com.facebook.buck.cxx.CxxSourceRuleFactory; import com.facebook.buck.cxx.CxxStrip; import com.facebook.buck.cxx.HeaderVisibility; import com.facebook.buck.cxx.Linker; import com.facebook.buck.cxx.LinkerMapMode; import com.facebook.buck.cxx.StripStyle; import com.facebook.buck.model.BuildTarget; import com.facebook.buck.model.Flavor; 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.CellPathResolver; import com.facebook.buck.rules.Description; import com.facebook.buck.rules.ExplicitBuildTargetSourcePath; 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.SourceWithFlags; import com.facebook.buck.rules.TargetGraph; import com.facebook.buck.rules.args.RuleKeyAppendableFunction; import com.facebook.buck.rules.coercer.FrameworkPath; import com.facebook.buck.rules.coercer.PatternMatchedCollection; import com.facebook.buck.rules.macros.StringWithMacros; import com.facebook.buck.util.HumanReadableException; import com.facebook.buck.util.immutables.BuckStyleImmutable; import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.ImmutableSortedSet; import java.nio.file.Path; import java.util.Optional; import java.util.regex.Pattern; import org.immutables.value.Value; public class HalideLibraryDescription implements Description<HalideLibraryDescriptionArg>, Flavored { public static final Flavor HALIDE_COMPILER_FLAVOR = InternalFlavor.of("halide-compiler"); public static final Flavor HALIDE_COMPILE_FLAVOR = InternalFlavor.of("halide-compile"); private final CxxPlatform defaultCxxPlatform; private final CxxBuckConfig cxxBuckConfig; private final FlavorDomain<CxxPlatform> cxxPlatforms; private final HalideBuckConfig halideBuckConfig; public HalideLibraryDescription( CxxBuckConfig cxxBuckConfig, CxxPlatform defaultCxxPlatform, FlavorDomain<CxxPlatform> cxxPlatforms, HalideBuckConfig halideBuckConfig) { this.cxxBuckConfig = cxxBuckConfig; this.defaultCxxPlatform = defaultCxxPlatform; this.cxxPlatforms = cxxPlatforms; this.halideBuckConfig = halideBuckConfig; } @Override public boolean hasFlavors(ImmutableSet<Flavor> flavors) { return cxxPlatforms.containsAnyOf(flavors) || flavors.contains(HALIDE_COMPILE_FLAVOR) || flavors.contains(HALIDE_COMPILER_FLAVOR) || StripStyle.FLAVOR_DOMAIN.containsAnyOf(flavors); } @Override public Class<HalideLibraryDescriptionArg> getConstructorArgType() { return HalideLibraryDescriptionArg.class; } public static BuildTarget createHalideCompilerBuildTarget(BuildTarget target) { return target.withFlavors(HALIDE_COMPILER_FLAVOR); } public static boolean isPlatformSupported( HalideLibraryDescriptionArg arg, CxxPlatform cxxPlatform) { return !arg.getSupportedPlatformsRegex().isPresent() || arg.getSupportedPlatformsRegex() .get() .matcher(cxxPlatform.getFlavor().toString()) .find(); } private CxxBinary createHalideCompiler( BuildRuleParams params, BuildRuleResolver ruleResolver, SourcePathResolver pathResolver, SourcePathRuleFinder ruleFinder, CellPathResolver cellRoots, CxxPlatform cxxPlatform, ImmutableSortedSet<SourceWithFlags> halideSources, ImmutableList<String> compilerFlags, PatternMatchedCollection<ImmutableList<String>> platformCompilerFlags, ImmutableMap<CxxSource.Type, ImmutableList<String>> langCompilerFlags, ImmutableList<StringWithMacros> linkerFlags, PatternMatchedCollection<ImmutableList<StringWithMacros>> platformLinkerFlags, ImmutableList<String> includeDirs) throws NoSuchBuildTargetException { Optional<StripStyle> flavoredStripStyle = StripStyle.FLAVOR_DOMAIN.getValue(params.getBuildTarget()); Optional<LinkerMapMode> flavoredLinkerMapMode = LinkerMapMode.FLAVOR_DOMAIN.getValue(params.getBuildTarget()); params = CxxStrip.removeStripStyleFlavorInParams(params, flavoredStripStyle); params = LinkerMapMode.removeLinkerMapModeFlavorInParams(params, flavoredLinkerMapMode); ImmutableMap<String, CxxSource> srcs = CxxDescriptionEnhancer.parseCxxSources( params.getBuildTarget(), ruleResolver, ruleFinder, pathResolver, cxxPlatform, halideSources, PatternMatchedCollection.of()); ImmutableList<String> preprocessorFlags = ImmutableList.of(); PatternMatchedCollection<ImmutableList<String>> platformPreprocessorFlags = PatternMatchedCollection.of(); ImmutableMap<CxxSource.Type, ImmutableList<String>> langPreprocessorFlags = ImmutableMap.of(); ImmutableSortedSet<FrameworkPath> frameworks = ImmutableSortedSet.of(); ImmutableSortedSet<FrameworkPath> libraries = ImmutableSortedSet.of(); Optional<SourcePath> prefixHeader = Optional.empty(); Optional<SourcePath> precompiledHeader = Optional.empty(); Optional<Linker.CxxRuntimeType> cxxRuntimeType = Optional.empty(); CxxLinkAndCompileRules cxxLinkAndCompileRules = CxxDescriptionEnhancer.createBuildRulesForCxxBinary( params, ruleResolver, cellRoots, cxxBuckConfig, cxxPlatform, srcs, /* headers */ ImmutableMap.of(), params.getBuildDeps(), flavoredStripStyle, flavoredLinkerMapMode, Linker.LinkableDepType.STATIC, /* thinLto */ false, preprocessorFlags, platformPreprocessorFlags, langPreprocessorFlags, frameworks, libraries, compilerFlags, langCompilerFlags, platformCompilerFlags, prefixHeader, precompiledHeader, linkerFlags, platformLinkerFlags, cxxRuntimeType, includeDirs, Optional.empty()); params = CxxStrip.restoreStripStyleFlavorInParams(params, flavoredStripStyle); params = LinkerMapMode.restoreLinkerMapModeFlavorInParams(params, flavoredLinkerMapMode); CxxBinary cxxBinary = new CxxBinary( params.copyAppendingExtraDeps(cxxLinkAndCompileRules.executable.getDeps(ruleFinder)), ruleResolver, ruleFinder, cxxPlatform, cxxLinkAndCompileRules.getBinaryRule(), cxxLinkAndCompileRules.executable, ImmutableSortedSet.of(), ImmutableSortedSet.of(), params.getBuildTarget().withoutFlavors(cxxPlatforms.getFlavors())); ruleResolver.addToIndex(cxxBinary); return cxxBinary; } private BuildRule createHalideStaticLibrary( BuildRuleParams params, BuildRuleResolver ruleResolver, SourcePathRuleFinder ruleFinder, CxxPlatform platform, HalideLibraryDescriptionArg args) throws NoSuchBuildTargetException { if (!isPlatformSupported(args, platform)) { return new NoopBuildRule(params); } BuildRule halideCompile = ruleResolver.requireRule( params.getBuildTarget().withFlavors(HALIDE_COMPILE_FLAVOR, platform.getFlavor())); BuildTarget buildTarget = halideCompile.getBuildTarget(); return Archive.from( params.getBuildTarget(), params, ruleFinder, platform, cxxBuckConfig.getArchiveContents(), CxxDescriptionEnhancer.getStaticLibraryPath( params.getProjectFilesystem(), params.getBuildTarget(), platform.getFlavor(), CxxSourceRuleFactory.PicType.PIC, platform.getStaticLibraryExtension()), ImmutableList.of( new ExplicitBuildTargetSourcePath( buildTarget, HalideCompile.objectOutputPath( buildTarget, params.getProjectFilesystem(), args.getFunctionName())))); } private Optional<ImmutableList<String>> expandInvocationFlags( Optional<ImmutableList<String>> optionalFlags, CxxPlatform platform) { if (optionalFlags.isPresent()) { RuleKeyAppendableFunction<String, String> macroMapper = CxxFlags.getTranslateMacrosFn(platform); ImmutableList<String> flags = optionalFlags.get(); ImmutableList.Builder<String> builder = ImmutableList.builder(); for (String flag : flags) { builder.add(macroMapper.apply(flag)); } optionalFlags = Optional.of(builder.build()); } return optionalFlags; } private BuildRule createHalideCompile( BuildRuleParams params, BuildRuleResolver resolver, CxxPlatform platform, Optional<ImmutableList<String>> compilerInvocationFlags, Optional<String> functionName) throws NoSuchBuildTargetException { CxxBinary halideCompiler = (CxxBinary) resolver.requireRule(params.getBuildTarget().withFlavors(HALIDE_COMPILER_FLAVOR)); return new HalideCompile( params.copyReplacingExtraDeps(Suppliers.ofInstance(ImmutableSortedSet.of(halideCompiler))), halideCompiler.getExecutableCommand(), halideBuckConfig.getHalideTargetForPlatform(platform), expandInvocationFlags(compilerInvocationFlags, platform), functionName); } @Override public BuildRule createBuildRule( TargetGraph targetGraph, BuildRuleParams params, BuildRuleResolver resolver, CellPathResolver cellRoots, HalideLibraryDescriptionArg args) throws NoSuchBuildTargetException { SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver); SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder); BuildTarget target = params.getBuildTarget(); ImmutableSet<Flavor> flavors = ImmutableSet.copyOf(target.getFlavors()); CxxPlatform cxxPlatform = cxxPlatforms.getValue(flavors).orElse(defaultCxxPlatform); if (flavors.contains(CxxDescriptionEnhancer.EXPORTED_HEADER_SYMLINK_TREE_FLAVOR)) { ImmutableMap.Builder<Path, SourcePath> headersBuilder = ImmutableMap.builder(); BuildTarget compileTarget = resolver .requireRule(target.withFlavors(HALIDE_COMPILE_FLAVOR, cxxPlatform.getFlavor())) .getBuildTarget(); Path outputPath = HalideCompile.headerOutputPath( compileTarget, params.getProjectFilesystem(), args.getFunctionName()); headersBuilder.put( outputPath.getFileName(), new ExplicitBuildTargetSourcePath(compileTarget, outputPath)); return CxxDescriptionEnhancer.createHeaderSymlinkTree( params, resolver, cxxPlatform, headersBuilder.build(), HeaderVisibility.PUBLIC, true); } else if (flavors.contains(CxxDescriptionEnhancer.SANDBOX_TREE_FLAVOR)) { CxxPlatform hostCxxPlatform = cxxPlatforms.getValue(CxxPlatforms.getHostFlavor()); return CxxDescriptionEnhancer.createSandboxTreeBuildRule( resolver, args, hostCxxPlatform, params); } else if (flavors.contains(HALIDE_COMPILER_FLAVOR)) { // We always want to build the halide "compiler" for the host platform, so // we use the host flavor here, regardless of the flavors on the build // target. CxxPlatform hostCxxPlatform = cxxPlatforms.getValue(CxxPlatforms.getHostFlavor()); final ImmutableSortedSet<BuildTarget> compilerDeps = args.getCompilerDeps(); return createHalideCompiler( params .withAppendedFlavor(HALIDE_COMPILER_FLAVOR) .copyReplacingDeclaredAndExtraDeps( Suppliers.ofInstance(resolver.getAllRules(compilerDeps)), Suppliers.ofInstance(ImmutableSortedSet.of())), resolver, pathResolver, ruleFinder, cellRoots, hostCxxPlatform, args.getSrcs(), args.getCompilerFlags(), args.getPlatformCompilerFlags(), args.getLangCompilerFlags(), args.getLinkerFlags(), args.getPlatformLinkerFlags(), args.getIncludeDirs()); } else if (flavors.contains(CxxDescriptionEnhancer.STATIC_FLAVOR) || flavors.contains(CxxDescriptionEnhancer.STATIC_PIC_FLAVOR)) { // Halide always output PIC, so it's output can be used for both cases. // See: https://github.com/halide/Halide/blob/e3c301f3/src/LLVM_Output.cpp#L152 return createHalideStaticLibrary(params, resolver, ruleFinder, cxxPlatform, args); } else if (flavors.contains(CxxDescriptionEnhancer.SHARED_FLAVOR)) { throw new HumanReadableException( "halide_library '%s' does not support shared libraries as output", params.getBuildTarget()); } else if (flavors.contains(HALIDE_COMPILE_FLAVOR)) { return createHalideCompile( params.copyReplacingDeclaredAndExtraDeps( Suppliers.ofInstance(ImmutableSortedSet.of()), Suppliers.ofInstance(ImmutableSortedSet.of())), resolver, cxxPlatform, Optional.of(args.getCompilerInvocationFlags()), args.getFunctionName()); } return new HalideLibrary(params, resolver, args.getSupportedPlatformsRegex()); } @BuckStyleImmutable @Value.Immutable interface AbstractHalideLibraryDescriptionArg extends CxxBinaryDescription.CommonArg { @Value.NaturalOrder ImmutableSortedSet<BuildTarget> getCompilerDeps(); @Value.NaturalOrder ImmutableSortedMap<String, ImmutableMap<String, String>> getConfigs(); Optional<Pattern> getSupportedPlatformsRegex(); ImmutableList<String> getCompilerInvocationFlags(); Optional<String> getFunctionName(); } }