/* * 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.cxx; import com.facebook.buck.graph.AbstractBreadthFirstThrowingTraversal; import com.facebook.buck.model.BuildTarget; 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.util.RichStream; import com.google.common.base.Preconditions; 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.ImmutableSortedSet; import java.nio.file.Path; import java.util.Optional; public final class CxxInferEnhancer { private CxxInferEnhancer() {} public enum InferFlavors { INFER(InternalFlavor.of("infer")), INFER_ANALYZE(InternalFlavor.of("infer-analyze")), INFER_CAPTURE(InternalFlavor.of("infer-capture")), INFER_CAPTURE_ALL(InternalFlavor.of("infer-capture-all")), INFER_CAPTURE_ONLY(InternalFlavor.of("infer-capture-only")); private final InternalFlavor flavor; InferFlavors(InternalFlavor flavor) { this.flavor = flavor; } public InternalFlavor get() { return flavor; } public static ImmutableSet<InternalFlavor> getAll() { ImmutableSet.Builder<InternalFlavor> builder = ImmutableSet.builder(); for (InferFlavors f : values()) { builder.add(f.get()); } return builder.build(); } private static BuildRuleParams paramsWithoutAnyInferFlavor(BuildRuleParams params) { BuildRuleParams result = params; for (InferFlavors f : values()) { result = result.withoutFlavor(f.get()); } return result; } private static void checkNoInferFlavors(ImmutableSet<Flavor> flavors) { for (InferFlavors f : InferFlavors.values()) { Preconditions.checkArgument( !flavors.contains(f.get()), "Unexpected infer-related flavor found: %s", f.toString()); } } } public static ImmutableMap<String, CxxSource> collectSources( BuildTarget buildTarget, BuildRuleResolver ruleResolver, CxxPlatform cxxPlatform, CxxConstructorArg args) { InferFlavors.checkNoInferFlavors(buildTarget.getFlavors()); SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(ruleResolver); SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder); return CxxDescriptionEnhancer.parseCxxSources( buildTarget, ruleResolver, ruleFinder, pathResolver, cxxPlatform, args); } public static BuildRule requireAllTransitiveCaptureBuildRules( BuildRuleParams params, BuildRuleResolver ruleResolver, CxxBuckConfig cxxBuckConfig, CxxPlatform cxxPlatform, InferBuckConfig inferBuckConfig, CxxInferSourceFilter sourceFilter, CxxConstructorArg args) throws NoSuchBuildTargetException { CxxInferCaptureRulesAggregator aggregator = requireInferCaptureAggregatorBuildRuleForCxxDescriptionArg( params, ruleResolver, cxxBuckConfig, cxxPlatform, args, inferBuckConfig, sourceFilter); ImmutableSet<CxxInferCapture> captureRules = aggregator.getAllTransitiveCaptures(); return ruleResolver.addToIndex( new CxxInferCaptureTransitive( params.copyReplacingDeclaredAndExtraDeps( Suppliers.ofInstance( ImmutableSortedSet.<BuildRule>naturalOrder().addAll(captureRules).build()), ImmutableSortedSet::of), captureRules)); } public static CxxInferComputeReport requireInferAnalyzeAndReportBuildRuleForCxxDescriptionArg( BuildRuleParams params, BuildRuleResolver resolver, CxxBuckConfig cxxBuckConfig, CxxPlatform cxxPlatform, CxxConstructorArg args, InferBuckConfig inferConfig, CxxInferSourceFilter sourceFilter) throws NoSuchBuildTargetException { BuildRuleParams cleanParams = InferFlavors.paramsWithoutAnyInferFlavor(params); BuildRuleParams paramsWithInferFlavor = cleanParams.withAppendedFlavor(InferFlavors.INFER.get()); Optional<CxxInferComputeReport> existingRule = resolver.getRuleOptionalWithType( paramsWithInferFlavor.getBuildTarget(), CxxInferComputeReport.class); if (existingRule.isPresent()) { return existingRule.get(); } CxxInferAnalyze analysisRule = requireInferAnalyzeBuildRuleForCxxDescriptionArg( cleanParams, resolver, cxxBuckConfig, cxxPlatform, args, inferConfig, sourceFilter); return createInferReportRule(paramsWithInferFlavor, resolver, analysisRule); } private static <T extends BuildRule> CxxInferCaptureAndAggregatingRules<T> requireTransitiveCaptureAndAggregatingRules( BuildRuleParams params, BuildRuleResolver resolver, CxxBuckConfig cxxBuckConfig, CxxPlatform cxxPlatform, CxxConstructorArg args, InferBuckConfig inferConfig, CxxInferSourceFilter sourceFilter, Flavor requiredFlavor, Class<T> aggregatingRuleClass) throws NoSuchBuildTargetException { BuildRuleParams cleanParams = InferFlavors.paramsWithoutAnyInferFlavor(params); ImmutableMap<String, CxxSource> sources = collectSources(cleanParams.getBuildTarget(), resolver, cxxPlatform, args); ImmutableSet<CxxInferCapture> captureRules = requireInferCaptureBuildRules( cleanParams, resolver, cxxBuckConfig, cxxPlatform, sources, inferConfig, sourceFilter, args); ImmutableSet<BuildRule> deps = args.getCxxDeps().get(resolver, cxxPlatform); // Build all the transitive dependencies build rules with the Infer's flavor ImmutableSet<T> transitiveDepsLibraryRules = requireTransitiveDependentLibraries( cxxPlatform, deps, requiredFlavor, aggregatingRuleClass); return new CxxInferCaptureAndAggregatingRules<>(captureRules, transitiveDepsLibraryRules); } public static CxxInferAnalyze requireInferAnalyzeBuildRuleForCxxDescriptionArg( BuildRuleParams params, BuildRuleResolver resolver, CxxBuckConfig cxxBuckConfig, CxxPlatform cxxPlatform, CxxConstructorArg args, InferBuckConfig inferConfig, CxxInferSourceFilter sourceFilter) throws NoSuchBuildTargetException { Flavor inferAnalyze = InferFlavors.INFER_ANALYZE.get(); BuildRuleParams paramsWithInferAnalyzeFlavor = InferFlavors.paramsWithoutAnyInferFlavor(params).withAppendedFlavor(inferAnalyze); Optional<CxxInferAnalyze> existingRule = resolver.getRuleOptionalWithType( paramsWithInferAnalyzeFlavor.getBuildTarget(), CxxInferAnalyze.class); if (existingRule.isPresent()) { return existingRule.get(); } CxxInferCaptureAndAggregatingRules<CxxInferAnalyze> cxxInferCaptureAndAnalyzeRules = requireTransitiveCaptureAndAggregatingRules( params, resolver, cxxBuckConfig, cxxPlatform, args, inferConfig, sourceFilter, inferAnalyze, CxxInferAnalyze.class); return createInferAnalyzeRule( paramsWithInferAnalyzeFlavor, resolver, inferConfig, cxxInferCaptureAndAnalyzeRules); } public static CxxInferCaptureRulesAggregator requireInferCaptureAggregatorBuildRuleForCxxDescriptionArg( BuildRuleParams params, BuildRuleResolver resolver, CxxBuckConfig cxxBuckConfig, CxxPlatform cxxPlatform, CxxConstructorArg args, InferBuckConfig inferConfig, CxxInferSourceFilter sourceFilter) throws NoSuchBuildTargetException { Flavor inferCaptureOnly = InferFlavors.INFER_CAPTURE_ONLY.get(); BuildRuleParams paramsWithInferCaptureOnlyFlavor = InferFlavors.paramsWithoutAnyInferFlavor(params).withAppendedFlavor(inferCaptureOnly); Optional<CxxInferCaptureRulesAggregator> existingRule = resolver.getRuleOptionalWithType( paramsWithInferCaptureOnlyFlavor.getBuildTarget(), CxxInferCaptureRulesAggregator.class); if (existingRule.isPresent()) { return existingRule.get(); } CxxInferCaptureAndAggregatingRules<CxxInferCaptureRulesAggregator> cxxInferCaptureAndAnalyzeRules = requireTransitiveCaptureAndAggregatingRules( params, resolver, cxxBuckConfig, cxxPlatform, args, inferConfig, sourceFilter, inferCaptureOnly, CxxInferCaptureRulesAggregator.class); return createInferCaptureAggregatorRule( paramsWithInferCaptureOnlyFlavor, resolver, cxxInferCaptureAndAnalyzeRules); } private static <T extends BuildRule> ImmutableSet<T> requireTransitiveDependentLibraries( final CxxPlatform cxxPlatform, final Iterable<? extends BuildRule> deps, final Flavor requiredFlavor, final Class<T> ruleClass) throws NoSuchBuildTargetException { final ImmutableSet.Builder<T> depsBuilder = ImmutableSet.builder(); new AbstractBreadthFirstThrowingTraversal<BuildRule, NoSuchBuildTargetException>(deps) { @Override public ImmutableSet<BuildRule> visit(BuildRule buildRule) throws NoSuchBuildTargetException { if (buildRule instanceof CxxLibrary) { CxxLibrary library = (CxxLibrary) buildRule; depsBuilder.add( (ruleClass.cast(library.requireBuildRule(requiredFlavor, cxxPlatform.getFlavor())))); return buildRule.getBuildDeps(); } return ImmutableSet.of(); } }.start(); return depsBuilder.build(); } private static ImmutableList<CxxPreprocessorInput> computePreprocessorInputForCxxBinaryDescriptionArg( BuildRuleParams params, BuildRuleResolver resolver, CxxPlatform cxxPlatform, CxxBinaryDescription.CommonArg args, HeaderSymlinkTree headerSymlinkTree, Optional<SymlinkTree> sandboxTree) throws NoSuchBuildTargetException { ImmutableSet<BuildRule> deps = args.getCxxDeps().get(resolver, cxxPlatform); return CxxDescriptionEnhancer.collectCxxPreprocessorInput( params, cxxPlatform, deps, CxxFlags.getLanguageFlags( args.getPreprocessorFlags(), args.getPlatformPreprocessorFlags(), args.getLangPreprocessorFlags(), cxxPlatform), ImmutableList.of(headerSymlinkTree), args.getFrameworks(), CxxPreprocessables.getTransitiveCxxPreprocessorInput( cxxPlatform, RichStream.from(deps).filter(CxxPreprocessorDep.class::isInstance).toImmutableList()), args.getIncludeDirs(), sandboxTree); } private static ImmutableList<CxxPreprocessorInput> computePreprocessorInputForCxxLibraryDescriptionArg( BuildRuleParams params, BuildRuleResolver resolver, CxxPlatform cxxPlatform, CxxLibraryDescription.CommonArg args, HeaderSymlinkTree headerSymlinkTree, ImmutableList<String> includeDirs, Optional<SymlinkTree> sandboxTree) throws NoSuchBuildTargetException { ImmutableSet<BuildRule> deps = args.getCxxDeps().get(resolver, cxxPlatform); return CxxDescriptionEnhancer.collectCxxPreprocessorInput( params, cxxPlatform, deps, CxxFlags.getLanguageFlags( args.getPreprocessorFlags(), args.getPlatformPreprocessorFlags(), args.getLangPreprocessorFlags(), cxxPlatform), ImmutableList.of(headerSymlinkTree), ImmutableSet.of(), CxxLibraryDescription.getTransitiveCxxPreprocessorInput( params, resolver, cxxPlatform, deps), includeDirs, sandboxTree); } private static ImmutableSet<CxxInferCapture> requireInferCaptureBuildRules( final BuildRuleParams params, final BuildRuleResolver resolver, CxxBuckConfig cxxBuckConfig, CxxPlatform cxxPlatform, ImmutableMap<String, CxxSource> sources, InferBuckConfig inferBuckConfig, CxxInferSourceFilter sourceFilter, CxxConstructorArg args) throws NoSuchBuildTargetException { InferFlavors.checkNoInferFlavors(params.getBuildTarget().getFlavors()); SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver); SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder); ImmutableMap<Path, SourcePath> headers = CxxDescriptionEnhancer.parseHeaders( params.getBuildTarget(), resolver, ruleFinder, pathResolver, Optional.of(cxxPlatform), args); // Setup the header symlink tree and combine all the preprocessor input from this rule // and all dependencies. boolean shouldCreateHeadersSymlinks = true; if (args instanceof CxxLibraryDescription.CommonArg) { shouldCreateHeadersSymlinks = ((CxxLibraryDescription.CommonArg) args) .getXcodePrivateHeadersSymlinks() .orElse(cxxPlatform.getPrivateHeadersSymlinksEnabled()); } HeaderSymlinkTree headerSymlinkTree = CxxDescriptionEnhancer.requireHeaderSymlinkTree( params, resolver, cxxPlatform, headers, HeaderVisibility.PRIVATE, shouldCreateHeadersSymlinks); Optional<SymlinkTree> sandboxTree = Optional.empty(); if (cxxBuckConfig.sandboxSources()) { sandboxTree = CxxDescriptionEnhancer.createSandboxTree(params, resolver, cxxPlatform); } ImmutableList<CxxPreprocessorInput> preprocessorInputs; if (args instanceof CxxBinaryDescription.CommonArg) { preprocessorInputs = computePreprocessorInputForCxxBinaryDescriptionArg( params, resolver, cxxPlatform, (CxxBinaryDescription.CommonArg) args, headerSymlinkTree, sandboxTree); } else if (args instanceof CxxLibraryDescription.CommonArg) { preprocessorInputs = computePreprocessorInputForCxxLibraryDescriptionArg( params, resolver, cxxPlatform, (CxxLibraryDescription.CommonArg) args, headerSymlinkTree, args.getIncludeDirs(), sandboxTree); } else { throw new IllegalStateException("Only Binary and Library args supported."); } CxxSourceRuleFactory factory = CxxSourceRuleFactory.of( params, resolver, pathResolver, ruleFinder, cxxBuckConfig, cxxPlatform, preprocessorInputs, CxxFlags.getLanguageFlags( args.getCompilerFlags(), args.getPlatformCompilerFlags(), args.getLangCompilerFlags(), cxxPlatform), args.getPrefixHeader(), args.getPrecompiledHeader(), CxxSourceRuleFactory.PicType.PDC, sandboxTree); return factory.requireInferCaptureBuildRules(sources, inferBuckConfig, sourceFilter); } private static CxxInferAnalyze createInferAnalyzeRule( BuildRuleParams params, BuildRuleResolver resolver, InferBuckConfig inferConfig, CxxInferCaptureAndAggregatingRules<CxxInferAnalyze> captureAnalyzeRules) { return resolver.addToIndex( new CxxInferAnalyze( params.copyReplacingDeclaredAndExtraDeps( Suppliers.ofInstance( ImmutableSortedSet.<BuildRule>naturalOrder() .addAll(captureAnalyzeRules.captureRules) .addAll(captureAnalyzeRules.aggregatingRules) .build()), ImmutableSortedSet::of), inferConfig, captureAnalyzeRules)); } private static CxxInferCaptureRulesAggregator createInferCaptureAggregatorRule( BuildRuleParams params, BuildRuleResolver resolver, CxxInferCaptureAndAggregatingRules<CxxInferCaptureRulesAggregator> captureAggregatorRules) { return resolver.addToIndex(new CxxInferCaptureRulesAggregator(params, captureAggregatorRules)); } private static CxxInferComputeReport createInferReportRule( BuildRuleParams buildRuleParams, BuildRuleResolver buildRuleResolver, CxxInferAnalyze analysisToReport) { return buildRuleResolver.addToIndex( new CxxInferComputeReport( buildRuleParams.copyReplacingDeclaredAndExtraDeps( Suppliers.ofInstance( ImmutableSortedSet.<BuildRule>naturalOrder() .addAll(analysisToReport.getTransitiveAnalyzeRules()) .add(analysisToReport) .build()), ImmutableSortedSet::of), analysisToReport)); } }