// Copyright 2016 The Bazel Authors. All rights reserved. // // 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.google.devtools.build.lib.rules.objc; import static com.google.devtools.build.lib.rules.objc.ObjcProvider.TOP_LEVEL_MODULE_MAP; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.base.Predicates; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.analysis.PrerequisiteArtifacts; import com.google.devtools.build.lib.analysis.RuleContext; import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.packages.BuildType; import com.google.devtools.build.lib.rules.cpp.CcCommon; import com.google.devtools.build.lib.rules.cpp.CppModuleMap; import com.google.devtools.build.lib.syntax.Type; import com.google.devtools.build.lib.util.Pair; import com.google.devtools.build.lib.vfs.PathFragment; import java.util.List; /** * Provides a way to access attributes that are common to all compilation rules. */ // TODO(bazel-team): Delete and move into support-specific attributes classes once ObjcCommon is // gone. final class CompilationAttributes { static class Builder { private final NestedSetBuilder hdrs = NestedSetBuilder.stableOrder(); private final NestedSetBuilder textualHdrs = NestedSetBuilder.stableOrder(); private final NestedSetBuilder includes = NestedSetBuilder.stableOrder(); private final NestedSetBuilder sdkIncludes = NestedSetBuilder.stableOrder(); private final ImmutableList.Builder copts = ImmutableList.builder(); private final ImmutableList.Builder linkopts = ImmutableList.builder(); private final NestedSetBuilder moduleMapsForDirectDeps = NestedSetBuilder.stableOrder(); private final NestedSetBuilder sdkFrameworks = NestedSetBuilder.stableOrder(); private final NestedSetBuilder weakSdkFrameworks = NestedSetBuilder.stableOrder(); private final NestedSetBuilder sdkDylibs = NestedSetBuilder.stableOrder(); private Optional packageFragment = Optional.absent(); private boolean enableModules; /** * Adds the default values available through the rule's context. */ static Builder fromRuleContext(RuleContext ruleContext) { Builder builder = new Builder(); addHeadersFromRuleContext(builder, ruleContext); addIncludesFromRuleContext(builder, ruleContext); addSdkAttributesFromRuleContext(builder, ruleContext); addCompileOptionsFromRuleContext(builder, ruleContext); addModuleOptionsFromRuleContext(builder, ruleContext); return builder; } /** * Adds headers to be made available for dependents. */ public Builder addHdrs(NestedSet hdrs) { this.hdrs.addTransitive(hdrs); return this; } /** * Adds headers that cannot be compiled individually. */ public Builder addTextualHdrs(NestedSet textualHdrs) { this.textualHdrs.addTransitive(textualHdrs); return this; } /** * Adds include paths to be made available for compilation. */ public Builder addIncludes(NestedSet includes) { this.includes.addTransitive(includes); return this; } /** * Adds paths for SDK includes. */ public Builder addSdkIncludes(NestedSet sdkIncludes) { this.sdkIncludes.addTransitive(sdkIncludes); return this; } /** * Adds compile-time options. */ public Builder addCopts(Iterable copts) { this.copts.addAll(copts); return this; } /** * Adds link-time options. */ public Builder addLinkopts(Iterable linkopts) { this.linkopts.addAll(linkopts); return this; } /** * Adds clang module maps for direct dependencies of the rule. These are needed to generate * module maps. */ public Builder addModuleMapsForDirectDeps(NestedSet moduleMapsForDirectDeps) { this.moduleMapsForDirectDeps.addTransitive(moduleMapsForDirectDeps); return this; } /** * Adds SDK frameworks to link against. */ public Builder addSdkFrameworks(NestedSet sdkFrameworks) { this.sdkFrameworks.addTransitive(sdkFrameworks); return this; } /** * Adds SDK frameworks to be linked weakly. */ public Builder addWeakSdkFrameworks(NestedSet weakSdkFrameworks) { this.weakSdkFrameworks.addTransitive(weakSdkFrameworks); return this; } /** * Adds SDK Dylibs to link against. */ public Builder addSdkDylibs(NestedSet sdkDylibs) { this.sdkDylibs.addTransitive(sdkDylibs); return this; } /** * Sets the package path from which to base the header search paths. */ public Builder setPackageFragment(PathFragment packageFragment) { Preconditions.checkState( !this.packageFragment.isPresent(), "packageFragment is already set to %s", this.packageFragment); this.packageFragment = Optional.of(packageFragment); return this; } /** * Enables the usage of clang modules maps during compilation. */ public Builder enableModules() { this.enableModules = true; return this; } /** * Builds a {@code CompilationAttributes} object. */ public CompilationAttributes build() { return new CompilationAttributes( this.hdrs.build(), this.textualHdrs.build(), this.includes.build(), this.sdkIncludes.build(), this.sdkFrameworks.build(), this.weakSdkFrameworks.build(), this.sdkDylibs.build(), this.packageFragment, this.copts.build(), this.linkopts.build(), this.moduleMapsForDirectDeps.build(), this.enableModules); } private static void addHeadersFromRuleContext(Builder builder, RuleContext ruleContext) { if (ruleContext.attributes().has("hdrs", BuildType.LABEL_LIST)) { NestedSetBuilder headers = NestedSetBuilder.stableOrder(); for (Pair header : CcCommon.getHeaders(ruleContext)) { headers.add(header.first); } builder.addHdrs(headers.build()); } if (ruleContext.attributes().has("textual_hdrs", BuildType.LABEL_LIST)) { builder.addTextualHdrs( PrerequisiteArtifacts.nestedSet(ruleContext, "textual_hdrs", Mode.TARGET)); } } private static void addIncludesFromRuleContext(Builder builder, RuleContext ruleContext) { if (ruleContext.attributes().has("includes", Type.STRING_LIST)) { NestedSetBuilder includes = NestedSetBuilder.stableOrder(); includes.addAll( Iterables.transform( ruleContext.attributes().get("includes", Type.STRING_LIST), PathFragment::create)); builder.addIncludes(includes.build()); } if (ruleContext.attributes().has("sdk_includes", Type.STRING_LIST)) { NestedSetBuilder sdkIncludes = NestedSetBuilder.stableOrder(); sdkIncludes.addAll( Iterables.transform( ruleContext.attributes().get("sdk_includes", Type.STRING_LIST), PathFragment::create)); builder.addSdkIncludes(sdkIncludes.build()); } } private static void addSdkAttributesFromRuleContext(Builder builder, RuleContext ruleContext) { if (ruleContext.attributes().has("sdk_frameworks", Type.STRING_LIST)) { NestedSetBuilder frameworks = NestedSetBuilder.stableOrder(); for (String explicit : ruleContext.attributes().get("sdk_frameworks", Type.STRING_LIST)) { frameworks.add(new SdkFramework(explicit)); } builder.addSdkFrameworks(frameworks.build()); } if (ruleContext.attributes().has("weak_sdk_frameworks", Type.STRING_LIST)) { NestedSetBuilder weakFrameworks = NestedSetBuilder.stableOrder(); for (String frameworkName : ruleContext.attributes().get("weak_sdk_frameworks", Type.STRING_LIST)) { weakFrameworks.add(new SdkFramework(frameworkName)); } builder.addWeakSdkFrameworks(weakFrameworks.build()); } if (ruleContext.attributes().has("sdk_dylibs", Type.STRING_LIST)) { NestedSetBuilder sdkDylibs = NestedSetBuilder.stableOrder(); sdkDylibs.addAll(ruleContext.attributes().get("sdk_dylibs", Type.STRING_LIST)); builder.addSdkDylibs(sdkDylibs.build()); } } private static void addCompileOptionsFromRuleContext(Builder builder, RuleContext ruleContext) { if (ruleContext.attributes().has("copts", Type.STRING_LIST)) { builder.addCopts(ruleContext.getExpander().withDataLocations().tokenized("copts")); } if (ruleContext.attributes().has("linkopts", Type.STRING_LIST)) { builder.addLinkopts(ruleContext.getExpander().withDataLocations().tokenized("linkopts")); } } private static void addModuleOptionsFromRuleContext(Builder builder, RuleContext ruleContext) { NestedSetBuilder moduleMaps = NestedSetBuilder.stableOrder(); ObjcConfiguration objcConfiguration = ObjcRuleClasses.objcConfiguration(ruleContext); if (objcConfiguration.moduleMapsEnabled()) { // Make sure all dependencies that have headers are included here. If a module map is // missing, its private headers will be treated as public! if (ruleContext.attributes().has("deps", BuildType.LABEL_LIST)) { Iterable providers = ruleContext.getPrerequisites("deps", Mode.TARGET, ObjcProvider.SKYLARK_CONSTRUCTOR); for (ObjcProvider provider : providers) { moduleMaps.addTransitive(provider.get(TOP_LEVEL_MODULE_MAP)); } } } builder.addModuleMapsForDirectDeps(moduleMaps.build()); PathFragment packageFragment = ruleContext.getLabel().getPackageIdentifier().getSourceRoot(); if (packageFragment != null) { builder.setPackageFragment(packageFragment); } if (ruleContext.attributes().has("enable_modules", Type.BOOLEAN) && ruleContext.attributes().get("enable_modules", Type.BOOLEAN)) { builder.enableModules(); } } } private final NestedSet hdrs; private final NestedSet textualHdrs; private final NestedSet includes; private final NestedSet sdkIncludes; private final NestedSet sdkFrameworks; private final NestedSet weakSdkFrameworks; private final NestedSet sdkDylibs; private final Optional packageFragment; private final ImmutableList copts; private final ImmutableList linkopts; private final NestedSet moduleMapsForDirectDeps; private final boolean enableModules; private CompilationAttributes( NestedSet hdrs, NestedSet textualHdrs, NestedSet includes, NestedSet sdkIncludes, NestedSet sdkFrameworks, NestedSet weakSdkFrameworks, NestedSet sdkDylibs, Optional packageFragment, ImmutableList copts, ImmutableList linkopts, NestedSet moduleMapsForDirectDeps, boolean enableModules) { this.hdrs = hdrs; this.textualHdrs = textualHdrs; this.includes = includes; this.sdkIncludes = sdkIncludes; this.sdkFrameworks = sdkFrameworks; this.weakSdkFrameworks = weakSdkFrameworks; this.sdkDylibs = sdkDylibs; this.packageFragment = packageFragment; this.copts = copts; this.linkopts = linkopts; this.moduleMapsForDirectDeps = moduleMapsForDirectDeps; this.enableModules = enableModules; } /** * Returns the headers to be made available for dependents. */ public NestedSet hdrs() { return this.hdrs; } /** * Returns the headers that cannot be compiled individually. */ public NestedSet textualHdrs() { return this.textualHdrs; } /** * Returns the include paths to be made available for compilation. */ public NestedSet includes() { return this.includes; } /** * Returns the paths for SDK includes. */ public NestedSet sdkIncludes() { return this.sdkIncludes; } /** * Returns the SDK frameworks to link against. */ public NestedSet sdkFrameworks() { return this.sdkFrameworks; } /** * Returns the SDK frameworks to be linked weakly. */ public NestedSet weakSdkFrameworks() { return this.weakSdkFrameworks; } /** * Returns the SDK Dylibs to link against. */ public NestedSet sdkDylibs() { return this.sdkDylibs; } /** * Returns the exec paths of all header search paths that should be added to this target and * dependers on this target, obtained from the {@code includes} attribute. */ public NestedSet headerSearchPaths(PathFragment genfilesFragment) { NestedSetBuilder paths = NestedSetBuilder.stableOrder(); if (packageFragment.isPresent()) { List rootFragments = ImmutableList.of( packageFragment.get(), genfilesFragment.getRelative(packageFragment.get())); Iterable relativeIncludes = Iterables.filter(includes(), Predicates.not(PathFragment::isAbsolute)); for (PathFragment include : relativeIncludes) { for (PathFragment rootFragment : rootFragments) { paths.add(rootFragment.getRelative(include)); } } } return paths.build(); } /** * Returns the compile-time options. */ public ImmutableList copts() { return this.copts; } /** * Returns the link-time options. */ public ImmutableList linkopts() { return this.linkopts; } /** * Returns the clang module maps of direct dependencies of this rule. These are needed to generate * this rule's module map. */ public NestedSet moduleMapsForDirectDeps() { return this.moduleMapsForDirectDeps; } /** * Returns whether this target uses language features that require clang modules, such as * {@literal @}import. */ public boolean enableModules() { return this.enableModules; } }