/* * 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.d; import com.facebook.buck.cxx.CxxBuckConfig; import com.facebook.buck.cxx.CxxLink; import com.facebook.buck.cxx.CxxLinkableEnhancer; import com.facebook.buck.cxx.CxxPlatform; import com.facebook.buck.cxx.Linker; import com.facebook.buck.cxx.NativeLinkable; import com.facebook.buck.cxx.NativeLinkableInput; import com.facebook.buck.graph.AbstractBreadthFirstTraversal; import com.facebook.buck.io.MorePaths; import com.facebook.buck.model.BuildTarget; import com.facebook.buck.model.BuildTargets; import com.facebook.buck.model.Flavor; 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.SourcePath; import com.facebook.buck.rules.SourcePathResolver; import com.facebook.buck.rules.SourcePathRuleFinder; import com.facebook.buck.rules.SymlinkTree; import com.facebook.buck.rules.Tool; import com.facebook.buck.rules.args.SourcePathArg; import com.facebook.buck.rules.args.StringArg; import com.facebook.buck.rules.coercer.SourceList; import com.facebook.buck.util.MoreMaps; import com.google.common.base.Preconditions; import com.google.common.base.Suppliers; 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.util.Map; import java.util.Optional; import java.util.TreeMap; /** Utility functions for use in D Descriptions. */ abstract class DDescriptionUtils { public static final Flavor SOURCE_LINK_TREE = InternalFlavor.of("source-link-tree"); /** * Creates a BuildTarget, based on an existing build target, but flavored with a CxxPlatform and * an additional flavor created by combining a prefix and an output file name. * * @param existingTarget the existing target * @param flavorPrefix prefix to be used for added flavor * @param fileName filename to be used for added flavor * @param cxxPlatform the C++ platform to compile for * @return the new BuildTarget */ public static BuildTarget createBuildTargetForFile( BuildTarget existingTarget, String flavorPrefix, String fileName, CxxPlatform cxxPlatform) { return BuildTarget.builder(existingTarget) .addFlavors( cxxPlatform.getFlavor(), InternalFlavor.of(flavorPrefix + Flavor.replaceInvalidCharacters(fileName))) .build(); } /** * Creates a new BuildTarget, based on an existing target, for a file to be compiled. * * @param existingTarget the existing target * @param src the source file to be compiled * @param cxxPlatform the C++ platform to compile the file for * @return a BuildTarget to compile a D source file to an object file */ public static BuildTarget createDCompileBuildTarget( BuildTarget existingTarget, String src, CxxPlatform cxxPlatform) { return createBuildTargetForFile( existingTarget, "compile-", DCompileStep.getObjectNameForSourceName(src), cxxPlatform); } /** * Creates a {@link com.facebook.buck.cxx.NativeLinkable} using sources compiled by the D * compiler. * * @param params build parameters for the build target * @param sources source files to compile * @param compilerFlags flags to pass to the compiler * @param buildRuleResolver resolver for build rules * @param cxxPlatform the C++ platform to compile for * @param dBuckConfig the Buck configuration for D * @return the new build rule */ public static CxxLink createNativeLinkable( BuildRuleParams params, BuildRuleResolver buildRuleResolver, CxxPlatform cxxPlatform, DBuckConfig dBuckConfig, CxxBuckConfig cxxBuckConfig, ImmutableList<String> compilerFlags, SourceList sources, ImmutableList<String> linkerFlags, DIncludes includes) throws NoSuchBuildTargetException { BuildTarget buildTarget = params.getBuildTarget(); SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(buildRuleResolver); SourcePathResolver sourcePathResolver = new SourcePathResolver(ruleFinder); ImmutableList<SourcePath> sourcePaths = sourcePathsForCompiledSources( params, buildRuleResolver, sourcePathResolver, ruleFinder, cxxPlatform, dBuckConfig, compilerFlags, sources, includes); // Return a rule to link the .o for the binary together with its // dependencies. return CxxLinkableEnhancer.createCxxLinkableBuildRule( cxxBuckConfig, cxxPlatform, params, buildRuleResolver, sourcePathResolver, ruleFinder, buildTarget, Linker.LinkType.EXECUTABLE, Optional.empty(), BuildTargets.getGenPath( params.getProjectFilesystem(), buildTarget, "%s/" + buildTarget.getShortName()), Linker.LinkableDepType.STATIC, /* thinLto */ false, FluentIterable.from(params.getBuildDeps()).filter(NativeLinkable.class), /* cxxRuntimeType */ Optional.empty(), /* bundleLoader */ Optional.empty(), ImmutableSet.of(), NativeLinkableInput.builder() .addAllArgs(StringArg.from(dBuckConfig.getLinkerFlags())) .addAllArgs(StringArg.from(linkerFlags)) .addAllArgs(SourcePathArg.from(sourcePaths)) .build()); } public static BuildTarget getSymlinkTreeTarget(BuildTarget baseTarget) { return BuildTarget.builder(baseTarget).addFlavors(SOURCE_LINK_TREE).build(); } public static SymlinkTree createSourceSymlinkTree( BuildTarget target, BuildRuleParams baseParams, SourcePathRuleFinder ruleFinder, SourcePathResolver pathResolver, SourceList sources) { Preconditions.checkState(target.getFlavors().contains(SOURCE_LINK_TREE)); return new SymlinkTree( target, baseParams.getProjectFilesystem(), BuildTargets.getGenPath( baseParams.getProjectFilesystem(), baseParams.getBuildTarget(), "%s"), MoreMaps.transformKeys( sources.toNameMap(baseParams.getBuildTarget(), pathResolver, "srcs"), MorePaths.toPathFn(baseParams.getProjectFilesystem().getRootPath().getFileSystem())), ruleFinder); } private static ImmutableMap<BuildTarget, DLibrary> getTransitiveDLibraryRules( Iterable<? extends BuildRule> inputs) { final ImmutableMap.Builder<BuildTarget, DLibrary> libraries = ImmutableMap.builder(); new AbstractBreadthFirstTraversal<BuildRule>(inputs) { @Override public ImmutableSet<BuildRule> visit(BuildRule rule) { if (rule instanceof DLibrary) { libraries.put(rule.getBuildTarget(), (DLibrary) rule); return rule.getBuildDeps(); } return ImmutableSet.of(); } }.start(); return libraries.build(); } /** * Ensures that a DCompileBuildRule exists for the given target, creating a DCompileBuildRule if * neccesary. * * @param baseParams build parameters for the rule * @param buildRuleResolver BuildRuleResolver the rule should be in * @param src the source file to be compiled * @param compilerFlags flags to pass to the compiler * @param compileTarget the target the rule should be for * @param dBuckConfig the Buck configuration for D * @return the build rule */ public static DCompileBuildRule requireBuildRule( BuildTarget compileTarget, BuildRuleParams baseParams, BuildRuleResolver buildRuleResolver, SourcePathRuleFinder ruleFinder, DBuckConfig dBuckConfig, ImmutableList<String> compilerFlags, String name, SourcePath src, DIncludes includes) throws NoSuchBuildTargetException { Optional<BuildRule> existingRule = buildRuleResolver.getRuleOptional(compileTarget); if (existingRule.isPresent()) { return (DCompileBuildRule) existingRule.get(); } else { Tool compiler = dBuckConfig.getDCompiler(); Map<BuildTarget, DIncludes> transitiveIncludes = new TreeMap<>(); transitiveIncludes.put(baseParams.getBuildTarget(), includes); for (Map.Entry<BuildTarget, DLibrary> library : getTransitiveDLibraryRules(baseParams.getBuildDeps()).entrySet()) { transitiveIncludes.put(library.getKey(), library.getValue().getIncludes()); } ImmutableSortedSet.Builder<BuildRule> depsBuilder = ImmutableSortedSet.naturalOrder(); depsBuilder.addAll(compiler.getDeps(ruleFinder)); depsBuilder.addAll(ruleFinder.filterBuildRuleInputs(src)); for (DIncludes dIncludes : transitiveIncludes.values()) { depsBuilder.addAll(dIncludes.getDeps(ruleFinder)); } ImmutableSortedSet<BuildRule> deps = depsBuilder.build(); return buildRuleResolver.addToIndex( new DCompileBuildRule( baseParams .withBuildTarget(compileTarget) .copyReplacingDeclaredAndExtraDeps( Suppliers.ofInstance(deps), Suppliers.ofInstance(ImmutableSortedSet.of())), compiler, ImmutableList.<String>builder() .addAll(dBuckConfig.getBaseCompilerFlags()) .addAll(compilerFlags) .build(), name, ImmutableSortedSet.of(src), ImmutableList.copyOf(transitiveIncludes.values()))); } } /** * Generates BuildTargets and BuildRules to compile D sources to object files, and returns a list * of SourcePaths referring to the generated object files. * * @param sources source files to compile * @param compilerFlags flags to pass to the compiler * @param baseParams build parameters for the compilation * @param buildRuleResolver resolver for build rules * @param sourcePathResolver resolver for source paths * @param cxxPlatform the C++ platform to compile for * @param dBuckConfig the Buck configuration for D * @return SourcePaths of the generated object files */ public static ImmutableList<SourcePath> sourcePathsForCompiledSources( BuildRuleParams baseParams, BuildRuleResolver buildRuleResolver, SourcePathResolver sourcePathResolver, SourcePathRuleFinder ruleFinder, CxxPlatform cxxPlatform, DBuckConfig dBuckConfig, ImmutableList<String> compilerFlags, SourceList sources, DIncludes includes) throws NoSuchBuildTargetException { ImmutableList.Builder<SourcePath> sourcePaths = ImmutableList.builder(); for (Map.Entry<String, SourcePath> source : sources.toNameMap(baseParams.getBuildTarget(), sourcePathResolver, "srcs").entrySet()) { BuildTarget compileTarget = createDCompileBuildTarget(baseParams.getBuildTarget(), source.getKey(), cxxPlatform); BuildRule rule = requireBuildRule( compileTarget, baseParams, buildRuleResolver, ruleFinder, dBuckConfig, compilerFlags, source.getKey(), source.getValue(), includes); sourcePaths.add(rule.getSourcePathToOutput()); } return sourcePaths.build(); } }