com.google.devtools.build.lib.rules.objc.CompilationAttributes.java Source code

Java tutorial

Introduction

Here is the source code for com.google.devtools.build.lib.rules.objc.CompilationAttributes.java

Source

// 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.RuleConfiguredTarget.Mode;
import com.google.devtools.build.lib.analysis.RuleContext;
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<Artifact> hdrs = NestedSetBuilder.stableOrder();
        private final NestedSetBuilder<Artifact> textualHdrs = NestedSetBuilder.stableOrder();
        private final NestedSetBuilder<PathFragment> includes = NestedSetBuilder.stableOrder();
        private final NestedSetBuilder<PathFragment> sdkIncludes = NestedSetBuilder.stableOrder();
        private final NestedSetBuilder<String> copts = NestedSetBuilder.stableOrder();
        private final NestedSetBuilder<String> linkopts = NestedSetBuilder.stableOrder();
        private final NestedSetBuilder<CppModuleMap> moduleMapsForDirectDeps = NestedSetBuilder.stableOrder();
        private final NestedSetBuilder<SdkFramework> sdkFrameworks = NestedSetBuilder.stableOrder();
        private final NestedSetBuilder<SdkFramework> weakSdkFrameworks = NestedSetBuilder.stableOrder();
        private final NestedSetBuilder<String> sdkDylibs = NestedSetBuilder.stableOrder();
        private Optional<PathFragment> 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<Artifact> hdrs) {
            this.hdrs.addTransitive(hdrs);
            return this;
        }

        /**
         * Adds headers that cannot be compiled individually.
         */
        public Builder addTextualHdrs(NestedSet<Artifact> textualHdrs) {
            this.textualHdrs.addTransitive(textualHdrs);
            return this;
        }

        /**
         * Adds include paths to be made available for compilation.
         */
        public Builder addIncludes(NestedSet<PathFragment> includes) {
            this.includes.addTransitive(includes);
            return this;
        }

        /**
         * Adds paths for SDK includes.
         */
        public Builder addSdkIncludes(NestedSet<PathFragment> sdkIncludes) {
            this.sdkIncludes.addTransitive(sdkIncludes);
            return this;
        }

        /**
         * Adds compile-time options.
         */
        public Builder addCopts(NestedSet<String> copts) {
            this.copts.addTransitive(copts);
            return this;
        }

        /**
         * Adds link-time options.
         */
        public Builder addLinkopts(NestedSet<String> linkopts) {
            this.linkopts.addTransitive(linkopts);
            return this;
        }

        /**
         * Adds clang module maps for direct dependencies of the rule. These are needed to generate
         * module maps.
         */
        public Builder addModuleMapsForDirectDeps(NestedSet<CppModuleMap> moduleMapsForDirectDeps) {
            this.moduleMapsForDirectDeps.addTransitive(moduleMapsForDirectDeps);
            return this;
        }

        /**
         * Adds SDK frameworks to link against.
         */
        public Builder addSdkFrameworks(NestedSet<SdkFramework> sdkFrameworks) {
            this.sdkFrameworks.addTransitive(sdkFrameworks);
            return this;
        }

        /**
         * Adds SDK frameworks to be linked weakly.
         */
        public Builder addWeakSdkFrameworks(NestedSet<SdkFramework> weakSdkFrameworks) {
            this.weakSdkFrameworks.addTransitive(weakSdkFrameworks);
            return this;
        }

        /**
         * Adds SDK Dylibs to link against.
         */
        public Builder addSdkDylibs(NestedSet<String> 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<Artifact> headers = NestedSetBuilder.stableOrder();
                for (Pair<Artifact, Label> 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<PathFragment> includes = NestedSetBuilder.stableOrder();
                includes.addAll(Iterables.transform(ruleContext.attributes().get("includes", Type.STRING_LIST),
                        PathFragment.TO_PATH_FRAGMENT));
                builder.addIncludes(includes.build());
            }

            if (ruleContext.attributes().has("sdk_includes", Type.STRING_LIST)) {
                NestedSetBuilder<PathFragment> sdkIncludes = NestedSetBuilder.stableOrder();
                sdkIncludes
                        .addAll(Iterables.transform(ruleContext.attributes().get("sdk_includes", Type.STRING_LIST),
                                PathFragment.TO_PATH_FRAGMENT));
                builder.addSdkIncludes(sdkIncludes.build());
            }
        }

        private static void addSdkAttributesFromRuleContext(Builder builder, RuleContext ruleContext) {
            if (ruleContext.attributes().has("sdk_frameworks", Type.STRING_LIST)) {
                NestedSetBuilder<SdkFramework> frameworks = NestedSetBuilder.stableOrder();
                // TODO(bazel-team): Move the inclusion of the default frameworks to CompilationSupport.
                frameworks.addAll(ObjcRuleClasses.AUTOMATIC_SDK_FRAMEWORKS);
                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<SdkFramework> 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<String> 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)) {
                NestedSetBuilder<String> copts = NestedSetBuilder.stableOrder();
                copts.addAll(ruleContext.getTokenizedStringListAttr("copts"));
                builder.addCopts(copts.build());
            }

            if (ruleContext.attributes().has("linkopts", Type.STRING_LIST)) {
                NestedSetBuilder<String> linkopts = NestedSetBuilder.stableOrder();
                linkopts.addAll(ruleContext.getTokenizedStringListAttr("linkopts"));
                builder.addLinkopts(linkopts.build());
            }
        }

        private static void addModuleOptionsFromRuleContext(Builder builder, RuleContext ruleContext) {
            NestedSetBuilder<CppModuleMap> 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<ObjcProvider> providers = ruleContext.getPrerequisites("deps", Mode.TARGET,
                            ObjcProvider.class);
                    for (ObjcProvider provider : providers) {
                        moduleMaps.addTransitive(provider.get(TOP_LEVEL_MODULE_MAP));
                    }
                }
                if (ruleContext.attributes().has("non_propagated_deps", BuildType.LABEL_LIST)) {
                    Iterable<ObjcProvider> providers = ruleContext.getPrerequisites("non_propagated_deps",
                            Mode.TARGET, ObjcProvider.class);
                    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<Artifact> hdrs;
    private final NestedSet<Artifact> textualHdrs;
    private final NestedSet<PathFragment> includes;
    private final NestedSet<PathFragment> sdkIncludes;
    private final NestedSet<SdkFramework> sdkFrameworks;
    private final NestedSet<SdkFramework> weakSdkFrameworks;
    private final NestedSet<String> sdkDylibs;
    private final Optional<PathFragment> packageFragment;
    private final NestedSet<String> copts;
    private final NestedSet<String> linkopts;
    private final NestedSet<CppModuleMap> moduleMapsForDirectDeps;
    private final boolean enableModules;

    private CompilationAttributes(NestedSet<Artifact> hdrs, NestedSet<Artifact> textualHdrs,
            NestedSet<PathFragment> includes, NestedSet<PathFragment> sdkIncludes,
            NestedSet<SdkFramework> sdkFrameworks, NestedSet<SdkFramework> weakSdkFrameworks,
            NestedSet<String> sdkDylibs, Optional<PathFragment> packageFragment, NestedSet<String> copts,
            NestedSet<String> linkopts, NestedSet<CppModuleMap> 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<Artifact> hdrs() {
        return this.hdrs;
    }

    /**
     * Returns the headers that cannot be compiled individually.
     */
    public NestedSet<Artifact> textualHdrs() {
        return this.textualHdrs;
    }

    /**
     * Returns the include paths to be made available for compilation.
     */
    public NestedSet<PathFragment> includes() {
        return this.includes;
    }

    /**
     * Returns the paths for SDK includes.
     */
    public NestedSet<PathFragment> sdkIncludes() {
        return this.sdkIncludes;
    }

    /**
     * Returns the SDK frameworks to link against.
     */
    public NestedSet<SdkFramework> sdkFrameworks() {
        return this.sdkFrameworks;
    }

    /**
     * Returns the SDK frameworks to be linked weakly.
     */
    public NestedSet<SdkFramework> weakSdkFrameworks() {
        return this.weakSdkFrameworks;
    }

    /**
     * Returns the SDK Dylibs to link against.
     */
    public NestedSet<String> 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<PathFragment> headerSearchPaths(PathFragment genfilesFragment) {
        NestedSetBuilder<PathFragment> paths = NestedSetBuilder.stableOrder();
        if (packageFragment.isPresent()) {
            List<PathFragment> rootFragments = ImmutableList.of(packageFragment.get(),
                    genfilesFragment.getRelative(packageFragment.get()));

            Iterable<PathFragment> relativeIncludes = Iterables.filter(includes(),
                    Predicates.not(PathFragment.IS_ABSOLUTE));
            for (PathFragment include : relativeIncludes) {
                for (PathFragment rootFragment : rootFragments) {
                    paths.add(rootFragment.getRelative(include).normalize());
                }
            }
        }
        return paths.build();
    }

    /**
     * Returns the compile-time options.
     */
    public NestedSet<String> copts() {
        return this.copts;
    }

    /**
     * Returns the link-time options.
     */
    public NestedSet<String> 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<CppModuleMap> 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;
    }
}