// Copyright 2014 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.analysis.config; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.google.common.base.Predicate; import com.google.common.base.Splitter; import com.google.common.base.Verify; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ClassToInstanceMap; import com.google.common.collect.ImmutableCollection; 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.Interner; import com.google.common.collect.Interners; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.MutableClassToInstanceMap; import com.google.devtools.build.lib.actions.ActionEnvironment; import com.google.devtools.build.lib.actions.Root; import com.google.devtools.build.lib.analysis.BlazeDirectories; import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider; import com.google.devtools.build.lib.analysis.RuleContext; import com.google.devtools.build.lib.analysis.actions.FileWriteAction; import com.google.devtools.build.lib.buildeventstream.BuildEvent; import com.google.devtools.build.lib.buildeventstream.BuildEventConverters; import com.google.devtools.build.lib.buildeventstream.BuildEventId; import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos; import com.google.devtools.build.lib.buildeventstream.GenericBuildEvent; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.cmdline.LabelSyntaxException; import com.google.devtools.build.lib.cmdline.RepositoryName; import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.events.EventHandler; import com.google.devtools.build.lib.packages.RuleClassProvider; import com.google.devtools.build.lib.packages.Target; import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable; import com.google.devtools.build.lib.skylarkinterface.SkylarkModule; import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory; import com.google.devtools.build.lib.util.Fingerprint; import com.google.devtools.build.lib.util.OS; import com.google.devtools.build.lib.util.RegexFilter; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.common.options.Converter; import com.google.devtools.common.options.Converters; import com.google.devtools.common.options.EnumConverter; import com.google.devtools.common.options.Option; import com.google.devtools.common.options.OptionDocumentationCategory; import com.google.devtools.common.options.OptionEffectTag; import com.google.devtools.common.options.OptionMetadataTag; import com.google.devtools.common.options.OptionsParsingException; import com.google.devtools.common.options.TriState; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.TreeMap; import javax.annotation.Nullable; /** * Instances of BuildConfiguration represent a collection of context * information which may affect a build (for example: the target platform for * compilation, or whether or not debug tables are required). In fact, all * "environmental" information (e.g. from the tool's command-line, as opposed * to the BUILD file) that can affect the output of any build tool should be * explicitly represented in the BuildConfiguration instance. * *

A single build may require building tools to run on a variety of * platforms: when compiling a server application for production, we must build * the build tools (like compilers) to run on the host platform, but cross-compile * the application for the production environment. * *

There is always at least one BuildConfiguration instance in any build: * the one representing the host platform. Additional instances may be created, * in a cross-compilation build, for example. * *

Instances of BuildConfiguration are canonical: *

c1.equals(c2) <=> c1==c2.
*/ @SkylarkModule(name = "configuration", category = SkylarkModuleCategory.BUILTIN, doc = "Data required for the analysis of a target that comes from targets that " + "depend on it and not targets that it depends on.") public final class BuildConfiguration implements BuildEvent { /** * An interface for language-specific configurations. * *

All implementations must be immutable and communicate this as clearly as possible * (e.g. declare {@link ImmutableList} signatures on their interfaces vs. {@link List}). * This is because fragment instances may be shared across configurations. */ public abstract static class Fragment { /** * Validates the options for this Fragment. Issues warnings for the * use of deprecated options, and warnings or errors for any option settings * that conflict. */ @SuppressWarnings("unused") public void reportInvalidOptions(EventHandler reporter, BuildOptions buildOptions) { } /** * Adds mapping of names to values of "Make" variables defined by this configuration. */ @SuppressWarnings("unused") public void addGlobalMakeVariables(ImmutableMap.Builder globalMakeEnvBuilder) { } /** * Returns a fragment of the output directory name for this configuration. The output * directory for the whole configuration contains all the short names by all fragments. */ @Nullable public String getOutputDirectoryName() { return null; } /** * The platform name is a concatenation of fragment platform names. */ public String getPlatformName() { return ""; } /** * Add items to the action environment. * * @param builder the map to add environment variables to */ public void setupActionEnvironment(Map builder) { } /** * Returns the shell to be used. * *

Each configuration instance must have at most one fragment that returns non-null. */ @SuppressWarnings("unused") public PathFragment getShellExecutable() { return null; } /** * Returns { 'option name': 'alternative default' } entries for options where the * "real default" should be something besides the default specified in the {@link Option} * declaration. */ public Map lateBoundOptionDefaults() { return ImmutableMap.of(); } /** * Return set of features enabled by this configuration. */ public ImmutableSet configurationEnabledFeatures(RuleContext ruleContext) { return ImmutableSet.of(); } /** * @return false if a Fragment understands that it won't be able to work with a given strategy, * or true otherwise. */ public boolean compatibleWithStrategy(String strategyName) { return true; } /** * Returns the transition that produces the "artifact owner" for this configuration, or null * if this configuration is its own owner. * *

If multiple fragments return the same transition, that transition is only applied * once. Multiple fragments may not return different non-null transitions. */ @Nullable public PatchTransition getArtifactOwnerTransition() { return null; } /** * Returns an extra transition that should apply to top-level targets in this * configuration. Returns null if no transition is needed. * *

Overriders should not change {@link FragmentOptions} not associated with their fragment. * *

If multiple fragments specify a transition, they're composed together in a * deterministic but undocumented order (so don't write code expecting a specific order). */ @Nullable public PatchTransition topLevelConfigurationHook(Target toTarget) { return null; } /** Returns a reserved set of action mnemonics. These cannot be used from a Skylark action. */ public ImmutableSet getReservedActionMnemonics() { return ImmutableSet.of(); } } public static final Label convertOptionsLabel(String input) throws OptionsParsingException { try { // Check if the input starts with '/'. We don't check for "//" so that // we get a better error message if the user accidentally tries to use // an absolute path (starting with '/') for a label. if (!input.startsWith("/") && !input.startsWith("@")) { input = "//" + input; } return Label.parseAbsolute(input); } catch (LabelSyntaxException e) { throw new OptionsParsingException(e.getMessage()); } } /** * A converter from strings to Labels. */ public static class LabelConverter implements Converter

(Note: any client that creates a view will also need to declare * BuildView.Options, which affect the mechanism of view construction, * even if they don't affect the value of the BuildConfiguration instances.) * *

IMPORTANT: when adding new options, be sure to consider whether those * values should be propagated to the host configuration or not. * *

ALSO IMPORTANT: all option types MUST define a toString method that * gives identical results for semantically identical option values. The * simplest way to ensure that is to return the input string. */ public static class Options extends FragmentOptions implements Cloneable { @Option( name = "experimental_separate_genfiles_directory", defaultValue = "true", category = "semantics", documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, effectTags = { OptionEffectTag.AFFECTS_OUTPUTS }, help = "Whether to have a separate genfiles directory or fold it into the bin directory" ) public boolean separateGenfilesDirectory; @Option( name = "define", converter = Converters.AssignmentConverter.class, defaultValue = "", category = "semantics", allowMultiple = true, documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, effectTags = {OptionEffectTag.UNKNOWN}, help = "Each --define option specifies an assignment for a build variable." ) public List> commandLineBuildVariables; @Option( name = "cpu", defaultValue = "", category = "semantics", converter = AutoCpuConverter.class, documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, effectTags = {OptionEffectTag.UNKNOWN}, help = "The target CPU." ) public String cpu; /** * Allows a configuration to record if --experimental_multi_cpu was used to set a cpu value. * This is necessary to ensure that a configuration transition that sets cpu does not erase the * difference between a pair of configurations created by --experimental_multi_cpu, leading to a * crash when the configurations are treated as the same. * *

TODO(b/33780512): Remove once dynamic configurations are used. */ @Option( name = "experimental multi cpu distinguisher", defaultValue = "", documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, effectTags = {OptionEffectTag.UNKNOWN}, metadataTags = {OptionMetadataTag.INTERNAL} ) public String experimentalMultiCpuDistinguisher; @Option( name = "min_param_file_size", defaultValue = "32768", documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, effectTags = {OptionEffectTag.UNKNOWN}, help = "Minimum command line length before creating a parameter file." ) public int minParamFileSize; @Option( name = "experimental_extended_sanity_checks", defaultValue = "false", documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, effectTags = {OptionEffectTag.UNKNOWN}, help = "Enables internal validation checks to make sure that configured target " + "implementations only access things they should. Causes a performance hit." ) public boolean extendedSanityChecks; @Option( name = "experimental_allow_runtime_deps_on_neverlink", defaultValue = "true", documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, effectTags = {OptionEffectTag.UNKNOWN}, help = "Flag to help transition from allowing to disallowing runtime_deps on neverlink" + " Java archives. The depot needs to be cleaned up to roll this out by default." ) public boolean allowRuntimeDepsOnNeverLink; @Option( name = "strict_filesets", defaultValue = "false", category = "semantics", documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, effectTags = {OptionEffectTag.UNKNOWN}, help = "If this option is enabled, filesets crossing package boundaries are reported " + "as errors. It does not work when check_fileset_dependencies_recursively is " + "disabled." ) public boolean strictFilesets; // Plugins are build using the host config. To avoid cycles we just don't propagate // this option to the host config. If one day we decide to use plugins when building // host tools, we can improve this by (for example) creating a compiler configuration that is // used only for building plugins. @Option( name = "plugin", converter = LabelListConverter.class, allowMultiple = true, defaultValue = "", category = "flags", documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, effectTags = {OptionEffectTag.UNKNOWN}, help = "Plugins to use in the build. Currently works with java_plugin." ) public List

The computation of the output directory should be a non-injective mapping from * BuildConfiguration instances to strings. The result should identify the aspects of the * configuration that should be reflected in the output file names. Furthermore the * returned string must not contain shell metacharacters. * *

For configuration settings which are NOT part of the output directory name, * rebuilding with a different value of such a setting will build in * the same output directory. This means that any actions whose * keys (see Action.getKey()) have changed will be rerun. That * may result in a lot of recompilation. * *

For configuration settings which ARE part of the output directory name, * rebuilding with a different value of such a setting will rebuild * in a different output directory; this will result in higher disk * usage and more work the first time you rebuild with a different * setting, but will result in less work if you regularly switch * back and forth between different settings. * *

With one important exception, it's sound to choose any subset of the * config's components for this string, it just alters the dimensionality * of the cache. In other words, it's a trade-off on the "injectiveness" * scale: at one extreme (output directory name contains all data in the config, and is * thus injective) you get extremely precise caching (no competition for the * same output-file locations) but you have to rebuild for even the * slightest change in configuration. At the other extreme (the output * (directory name is a constant) you have very high competition for * output-file locations, but if a slight change in configuration doesn't * affect a particular build step, you're guaranteed not to have to * rebuild it. The important exception has to do with multiple configurations: every * configuration in the build must have a different output directory name so that * their artifacts do not conflict. * *

The host configuration is special-cased: in order to guarantee that its output directory * is always separate from that of the target configuration, we simply pin it to "host". We do * this so that the build works even if the two configurations are too close (which is common) * and so that the path of artifacts in the host configuration is a bit more readable. */ private enum OutputDirectory { BIN("bin"), GENFILES("genfiles"), MIDDLEMAN(true), TESTLOGS("testlogs"), COVERAGE("coverage-metadata"), INCLUDE(BlazeDirectories.RELATIVE_INCLUDE_DIR), OUTPUT(false); private final PathFragment nameFragment; private final boolean middleman; /** * This constructor is for roots without suffixes, e.g., * [[execroot/repo]/bazel-out/local-fastbuild]. * @param isMiddleman whether the root should be a middleman root or a "normal" derived root. */ OutputDirectory(boolean isMiddleman) { this.nameFragment = PathFragment.EMPTY_FRAGMENT; this.middleman = isMiddleman; } OutputDirectory(String name) { this.nameFragment = PathFragment.create(name); this.middleman = false; } Root getRoot( RepositoryName repositoryName, String outputDirName, BlazeDirectories directories, RepositoryName mainRepositoryName) { // e.g., execroot/repo1 Path execRoot = directories.getExecRoot(mainRepositoryName.strippedName()); // e.g., execroot/repo1/bazel-out/config/bin Path outputDir = execRoot.getRelative(directories.getRelativeOutputPath()) .getRelative(outputDirName); if (middleman) { return INTERNER.intern(Root.middlemanRoot(execRoot, outputDir, repositoryName.equals(mainRepositoryName))); } // e.g., [[execroot/repo1]/bazel-out/config/bin] return INTERNER.intern( Root.asDerivedRoot(execRoot, outputDir.getRelative(nameFragment), repositoryName.equals(mainRepositoryName))); } } private final BlazeDirectories directories; private final String outputDirName; // We intern the roots for non-main repositories, so we don't keep around thousands of copies of // the same root. private static Interner INTERNER = Interners.newWeakInterner(); // We precompute the roots for the main repository, since that's the common case. private final Root outputDirectoryForMainRepository; private final Root binDirectoryForMainRepository; private final Root includeDirectoryForMainRepository; private final Root genfilesDirectoryForMainRepository; private final Root coverageDirectoryForMainRepository; private final Root testlogsDirectoryForMainRepository; private final Root middlemanDirectoryForMainRepository; private final boolean separateGenfilesDirectory; // Cache this value for quicker access. We don't cache it inside BuildOptions because BuildOptions // is mutable, so a cached value there could fall out of date when it's updated. private final boolean actionsEnabled; // TODO(bazel-team): Move this to a configuration fragment. private final PathFragment shellExecutable; /** * The global "make variables" such as "$(TARGET_CPU)"; these get applied to all rules analyzed in * this configuration. */ private final ImmutableMap globalMakeEnv; private final ActionEnvironment actionEnv; private final ActionEnvironment testEnv; private final BuildOptions buildOptions; private final Options options; private final String mnemonic; private final String platformName; private final ImmutableMap commandLineBuildVariables; private final int hashCode; // We can precompute the hash code as all its inputs are immutable. /** Data for introspecting the options used by this configuration. */ private final TransitiveOptionDetails transitiveOptionDetails; /** * Returns true if this configuration is semantically equal to the other, with * the possible exception that the other has fewer fragments. * *

This is useful for dynamic configurations - as the same configuration gets "trimmed" while * going down a dependency chain, it's still the same configuration but loses some of its * fragments. So we need a more nuanced concept of "equality" than simple reference equality. */ public boolean equalsOrIsSupersetOf(BuildConfiguration other) { return this.equals(other) || (other != null // TODO(gregce): add back in output root checking. This requires a better approach to // configuration-safe output paths. If the parent config has a fragment the child config // doesn't, it may inject $(FOO) into the output roots. So the child bindir might be // "bazel-out/arm-linux-fastbuild/bin" while the parent bindir is // "bazel-out/android-arm-linux-fastbuild/bin". That's pretty awkward to check here. // && outputRoots.equals(other.outputRoots) && actionsEnabled == other.actionsEnabled && fragments.values().containsAll(other.fragments.values()) && buildOptions.getOptions().containsAll(other.buildOptions.getOptions())); } /** * Returns {@code true} if this configuration is semantically equal to the other, including * checking that both have the same sets of fragments and options. */ @Override public boolean equals(Object other) { if (this == other) { return true; } if (!(other instanceof BuildConfiguration)) { return false; } BuildConfiguration otherConfig = (BuildConfiguration) other; return actionsEnabled == otherConfig.actionsEnabled && fragments.values().equals(otherConfig.fragments.values()) && buildOptions.getOptions().equals(otherConfig.buildOptions.getOptions()); } private int computeHashCode() { return Objects.hash(isActionsEnabled(), fragments, buildOptions.getOptions()); } @Override public int hashCode() { return hashCode; } /** * Returns map of all the fragments for this configuration. */ public ImmutableMap, Fragment> getAllFragments() { return fragments; } /** * Validates the options for this BuildConfiguration. Issues warnings for the * use of deprecated options, and warnings or errors for any option settings * that conflict. */ public void reportInvalidOptions(EventHandler reporter) { for (Fragment fragment : fragments.values()) { fragment.reportInvalidOptions(reporter, this.buildOptions); } Set plugins = new HashSet<>(); for (Label plugin : options.pluginList) { String name = plugin.getName(); if (plugins.contains(name)) { reporter.handle(Event.error("A build cannot have two plugins with the same name")); } plugins.add(name); } for (Map.Entry opt : options.pluginCoptList) { if (!plugins.contains(opt.getKey())) { reporter.handle(Event.error("A plugin_copt must refer to an existing plugin")); } } if (options.outputDirectoryName != null) { reporter.handle(Event.error( "The internal '--output directory name' option cannot be used on the command line")); } } /** * @return false if any of the fragments don't work well with the supplied strategy. */ public boolean compatibleWithStrategy(final String strategyName) { return Iterables.all( fragments.values(), new Predicate() { @Override public boolean apply(@Nullable Fragment fragment) { return fragment.compatibleWithStrategy(strategyName); } }); } /** * Compute the shell environment, which, at configuration level, is a pair consisting of the * statically set environment variables with their values and the set of environment variables to * be inherited from the client environment. */ private ActionEnvironment setupActionEnvironment() { // We make a copy first to remove duplicate entries; last one wins. Map actionEnv = new HashMap<>(); // TODO(ulfjack): Remove all env variables from configuration fragments. for (Fragment fragment : fragments.values()) { fragment.setupActionEnvironment(actionEnv); } // Shell environment variables specified via options take precedence over the // ones inherited from the fragments. In the long run, these fragments will // be replaced by appropriate default rc files anyway. for (Map.Entry entry : options.actionEnvironment) { actionEnv.put(entry.getKey(), entry.getValue()); } return ActionEnvironment.split(actionEnv); } /** * Compute the test environment, which, at configuration level, is a pair consisting of the * statically set environment variables with their values and the set of environment variables to * be inherited from the client environment. */ private ActionEnvironment setupTestEnvironment() { // We make a copy first to remove duplicate entries; last one wins. Map testEnv = new HashMap<>(); for (Map.Entry entry : options.testEnvironment) { testEnv.put(entry.getKey(), entry.getValue()); } return ActionEnvironment.split(testEnv); } /** * Sorts fragments by class name. This produces a stable order which, e.g., facilitates * consistent output from buildMneumonic. */ private static final Comparator lexicalFragmentSorter = new Comparator>() { @Override public int compare(Class o1, Class o2) { return o1.getName().compareTo(o2.getName()); } }; /** * Constructs a new BuildConfiguration instance. * *

Callers that pass null for {@code dynamicTransitionMapper} should not use dynamic * configurations. */ public BuildConfiguration(BlazeDirectories directories, Map, Fragment> fragmentsMap, BuildOptions buildOptions, String repositoryName) { this.directories = directories; this.fragments = ImmutableSortedMap.copyOf(fragmentsMap, lexicalFragmentSorter); this.skylarkVisibleFragments = buildIndexOfSkylarkVisibleFragments(); this.buildOptions = buildOptions.clone(); this.actionsEnabled = buildOptions.enableActions(); this.options = buildOptions.get(Options.class); this.separateGenfilesDirectory = options.separateGenfilesDirectory; this.mainRepositoryName = RepositoryName.createFromValidStrippedName(repositoryName); // We can't use an ImmutableMap.Builder here; we need the ability to add entries with keys that // are already in the map so that the same define can be specified on the command line twice, // and ImmutableMap.Builder does not support that. Map commandLineDefinesBuilder = new TreeMap<>(); for (Map.Entry define : options.commandLineBuildVariables) { commandLineDefinesBuilder.put(define.getKey(), define.getValue()); } commandLineBuildVariables = ImmutableMap.copyOf(commandLineDefinesBuilder); this.mnemonic = buildMnemonic(); this.outputDirName = (options.outputDirectoryName != null) ? options.outputDirectoryName : mnemonic; this.outputDirectoryForMainRepository = OutputDirectory.OUTPUT.getRoot( RepositoryName.MAIN, outputDirName, directories, mainRepositoryName); this.binDirectoryForMainRepository = OutputDirectory.BIN.getRoot( RepositoryName.MAIN, outputDirName, directories, mainRepositoryName); this.includeDirectoryForMainRepository = OutputDirectory.INCLUDE.getRoot( RepositoryName.MAIN, outputDirName, directories, mainRepositoryName); this.genfilesDirectoryForMainRepository = OutputDirectory.GENFILES.getRoot( RepositoryName.MAIN, outputDirName, directories, mainRepositoryName); this.coverageDirectoryForMainRepository = OutputDirectory.COVERAGE.getRoot( RepositoryName.MAIN, outputDirName, directories, mainRepositoryName); this.testlogsDirectoryForMainRepository = OutputDirectory.TESTLOGS.getRoot( RepositoryName.MAIN, outputDirName, directories, mainRepositoryName); this.middlemanDirectoryForMainRepository = OutputDirectory.MIDDLEMAN.getRoot( RepositoryName.MAIN, outputDirName, directories, mainRepositoryName); this.platformName = buildPlatformName(); this.shellExecutable = computeShellExecutable(); this.actionEnv = setupActionEnvironment(); this.testEnv = setupTestEnvironment(); this.transitiveOptionDetails = computeOptionsMap(buildOptions, fragments.values()); ImmutableMap.Builder globalMakeEnvBuilder = ImmutableMap.builder(); for (Fragment fragment : fragments.values()) { fragment.addGlobalMakeVariables(globalMakeEnvBuilder); } globalMakeEnvBuilder.put("COMPILATION_MODE", options.compilationMode.toString()); /* * Attention! Document these in the build-encyclopedia */ // the bin directory and the genfiles directory // These variables will be used on Windows as well, so we need to make sure // that paths use the correct system file-separator. globalMakeEnvBuilder.put("BINDIR", getBinDirectory().getExecPath().getPathString()); globalMakeEnvBuilder.put("GENDIR", getGenfilesDirectory().getExecPath().getPathString()); globalMakeEnv = globalMakeEnvBuilder.build(); checksum = Fingerprint.md5Digest(buildOptions.computeCacheKey()); hashCode = computeHashCode(); ImmutableSet.Builder reservedActionMnemonics = ImmutableSet.builder(); for (Fragment fragment : fragments.values()) { reservedActionMnemonics.addAll(fragment.getReservedActionMnemonics()); } this.reservedActionMnemonics = reservedActionMnemonics.build(); } /** * Returns a copy of this configuration only including the given fragments (which the current * configuration is assumed to have). */ public BuildConfiguration clone( Set> fragmentClasses, RuleClassProvider ruleClassProvider) { ClassToInstanceMap fragmentsMap = MutableClassToInstanceMap.create(); for (Fragment fragment : fragments.values()) { if (fragmentClasses.contains(fragment.getClass())) { fragmentsMap.put(fragment.getClass(), fragment); } } BuildOptions options = buildOptions.trim( getOptionsClasses(fragmentsMap.keySet(), ruleClassProvider)); BuildConfiguration newConfig = new BuildConfiguration( directories, fragmentsMap, options, mainRepositoryName.strippedName()); return newConfig; } /** * Returns the config fragment options classes used by the given fragment types. */ public static Set> getOptionsClasses( Iterable> fragmentClasses, RuleClassProvider ruleClassProvider) { Multimap, Class> fragmentToRequiredOptions = ArrayListMultimap.create(); for (ConfigurationFragmentFactory fragmentLoader : ((ConfiguredRuleClassProvider) ruleClassProvider).getConfigurationFragments()) { fragmentToRequiredOptions.putAll(fragmentLoader.creates(), fragmentLoader.requiredOptions()); } Set> options = new HashSet<>(); for (Class fragmentClass : fragmentClasses) { options.addAll(fragmentToRequiredOptions.get(fragmentClass)); } return options; } private ImmutableMap> buildIndexOfSkylarkVisibleFragments() { ImmutableMap.Builder> builder = ImmutableMap.builder(); for (Class fragmentClass : fragments.keySet()) { String name = SkylarkModule.Resolver.resolveName(fragmentClass); if (name != null) { builder.put(name, fragmentClass); } } return builder.build(); } /** * Retrieves the {@link TransitiveOptionDetails} containing data on this configuration's options. * * @see BuildConfigurationOptionDetails */ TransitiveOptionDetails getTransitiveOptionDetails() { return transitiveOptionDetails; } /** Computes and returns the {@link TransitiveOptionDetails} for this configuration. */ private static TransitiveOptionDetails computeOptionsMap( BuildOptions buildOptions, Iterable fragments) { // Collect from our fragments "alternative defaults" for options where the default // should be something other than what's specified in Option.defaultValue. Map lateBoundDefaults = Maps.newHashMap(); for (Fragment fragment : fragments) { lateBoundDefaults.putAll(fragment.lateBoundOptionDefaults()); } return TransitiveOptionDetails.forOptionsWithDefaults( buildOptions.getOptions(), lateBoundDefaults); } private String buildMnemonic() { // See explanation at declaration for outputRoots. String platformSuffix = (options.platformSuffix != null) ? options.platformSuffix : ""; ArrayList nameParts = new ArrayList<>(); for (Fragment fragment : fragments.values()) { nameParts.add(fragment.getOutputDirectoryName()); } nameParts.add(getCompilationMode() + platformSuffix); return Joiner.on('-').skipNulls().join(nameParts); } private String buildPlatformName() { StringBuilder platformNameBuilder = new StringBuilder(); for (Fragment fragment : fragments.values()) { platformNameBuilder.append(fragment.getPlatformName()); } return platformNameBuilder.toString(); } /** * The platform string, suitable for use as a key into a MakeEnvironment. */ public String getPlatformName() { return platformName; } /** * Returns the output directory for this build configuration. */ public Root getOutputDirectory(RepositoryName repositoryName) { return repositoryName.isMain() || repositoryName.equals(mainRepositoryName) ? outputDirectoryForMainRepository : OutputDirectory.OUTPUT.getRoot( repositoryName, outputDirName, directories, mainRepositoryName); } /** * Returns the bin directory for this build configuration. */ @SkylarkCallable(name = "bin_dir", structField = true, documented = false) @Deprecated public Root getBinDirectory() { return getBinDirectory(RepositoryName.MAIN); } /** * TODO(kchodorow): This (and the other get*Directory functions) won't work with external * repositories without changes to how ArtifactFactory resolves derived roots. This is not an * issue right now because it only effects Blaze's include scanning (internal) and Bazel's * repositories (external) but will need to be fixed. */ public Root getBinDirectory(RepositoryName repositoryName) { return repositoryName.isMain() || repositoryName.equals(mainRepositoryName) ? binDirectoryForMainRepository : OutputDirectory.BIN.getRoot( repositoryName, outputDirName, directories, mainRepositoryName); } /** * Returns a relative path to the bin directory at execution time. */ public PathFragment getBinFragment() { return getBinDirectory().getExecPath(); } /** * Returns the include directory for this build configuration. */ public Root getIncludeDirectory(RepositoryName repositoryName) { return repositoryName.isMain() || repositoryName.equals(mainRepositoryName) ? includeDirectoryForMainRepository : OutputDirectory.INCLUDE.getRoot( repositoryName, outputDirName, directories, mainRepositoryName); } /** * Returns the genfiles directory for this build configuration. */ @SkylarkCallable(name = "genfiles_dir", structField = true, documented = false) @Deprecated public Root getGenfilesDirectory() { return getGenfilesDirectory(RepositoryName.MAIN); } public Root getGenfilesDirectory(RepositoryName repositoryName) { if (!separateGenfilesDirectory) { return getBinDirectory(repositoryName); } return repositoryName.isMain() || repositoryName.equals(mainRepositoryName) ? genfilesDirectoryForMainRepository : OutputDirectory.GENFILES.getRoot( repositoryName, outputDirName, directories, mainRepositoryName); } /** * Returns the directory where coverage-related artifacts and metadata files * should be stored. This includes for example uninstrumented class files * needed for Jacoco's coverage reporting tools. */ public Root getCoverageMetadataDirectory(RepositoryName repositoryName) { return repositoryName.isMain() || repositoryName.equals(mainRepositoryName) ? coverageDirectoryForMainRepository : OutputDirectory.COVERAGE.getRoot( repositoryName, outputDirName, directories, mainRepositoryName); } /** * Returns the testlogs directory for this build configuration. */ public Root getTestLogsDirectory(RepositoryName repositoryName) { return repositoryName.isMain() || repositoryName.equals(mainRepositoryName) ? testlogsDirectoryForMainRepository : OutputDirectory.TESTLOGS.getRoot( repositoryName, outputDirName, directories, mainRepositoryName); } /** * Returns a relative path to the genfiles directory at execution time. */ public PathFragment getGenfilesFragment() { return getGenfilesDirectory().getExecPath(); } /** * Returns the path separator for the host platform. This is basically the same as {@link * java.io.File#pathSeparator}, except that that returns the value for this JVM, which may or may * not match the host platform. You should only use this when invoking tools that are known to use * the native path separator, i.e., the path separator for the machine that they run on. */ @SkylarkCallable(name = "host_path_separator", structField = true, doc = "Returns the separator for PATH environment variable, which is ':' on Unix.") public String getHostPathSeparator() { // TODO(bazel-team): Maybe do this in the constructor instead? This isn't serialization-safe. return OS.getCurrent() == OS.WINDOWS ? ";" : ":"; } /** * Returns the internal directory (used for middlemen) for this build configuration. */ public Root getMiddlemanDirectory(RepositoryName repositoryName) { return repositoryName.isMain() || repositoryName.equals(mainRepositoryName) ? middlemanDirectoryForMainRepository : OutputDirectory.MIDDLEMAN.getRoot( repositoryName, outputDirName, directories, mainRepositoryName); } public boolean getAllowRuntimeDepsOnNeverLink() { return options.allowRuntimeDepsOnNeverLink; } public boolean isStrictFilesets() { return options.strictFilesets; } public List

An action's full set of environment variables consist of a "fixed" part and of a "variable" * part. The "fixed" variables are independent of the Bazel client's own environment, and are * returned by this function. The "variable" ones are inherited from the Bazel client's own * environment, and are returned by {@link getVariableShellEnvironment}. * *

Since values of the "fixed" variables are already known at analysis phase, it is returned * here as a map. */ @Deprecated // Use getActionEnvironment instead. public ImmutableMap getLocalShellEnvironment() { return actionEnv.getFixedEnv(); } /** * Return the "variable" part of the actions' environment variables. * *

An action's full set of environment variables consist of a "fixed" part and of a "variable" * part. The "fixed" variables are independent of the Bazel client's own environment, and are * returned by {@link #getLocalShellEnvironment}. The "variable" ones are inherited from the Bazel * client's own environment, and are returned by this function. * *

The values of the "variable" variables are tracked in Skyframe via the {@link * com.google.devtools.build.lib.skyframe.SkyFunctions.CLIENT_ENVIRONMENT_VARIABLE} skyfunction. * This method only returns the names of those variables to be inherited, if set in the client's * environment. (Variables where the name is not returned in this set should not be taken from the * client environment.) */ @Deprecated // Use getActionEnvironment instead. public ImmutableSet getVariableShellEnvironment() { return actionEnv.getInheritedEnv(); } /** * Returns the path to sh. */ public PathFragment getShellExecutable() { return shellExecutable; } /** * Returns a regex-based instrumentation filter instance that used to match label * names to identify targets to be instrumented in the coverage mode. */ public RegexFilter getInstrumentationFilter() { return options.instrumentationFilter; } /** * Returns a boolean of whether to include targets created by *_test rules in the set of targets * matched by --instrumentation_filter. If this is false, all test targets are excluded from * instrumentation. */ public boolean shouldInstrumentTestTargets() { return options.instrumentTestTargets; } /** * Returns a new, unordered mapping of names to values of "Make" variables defined by this * configuration. * *

This does *not* include package-defined overrides (e.g. vardef) * and so should not be used by the build logic. This is used only for * the 'info' command. * *

Command-line definitions of make enviroments override variables defined by * {@code Fragment.addGlobalMakeVariables()}. */ public Map getMakeEnvironment() { Map makeEnvironment = new HashMap<>(); makeEnvironment.putAll(globalMakeEnv); makeEnvironment.putAll(commandLineBuildVariables); return ImmutableMap.copyOf(makeEnvironment); } /** * Returns a new, unordered mapping of names that are set through the command lines. * (Fragments, in particular the Google C++ support, can set variables through the * command line.) */ public ImmutableMap getCommandLineBuildVariables() { return commandLineBuildVariables; } /** * Returns the global defaults for this configuration for the Make environment. */ public ImmutableMap getGlobalMakeEnvironment() { return globalMakeEnv; } /** * Returns the default value for the specified "Make" variable for this * configuration. Returns null if no value was found. */ public String getMakeVariableDefault(String var) { return globalMakeEnv.get(var); } /** * Returns a configuration fragment instances of the given class. */ public T getFragment(Class clazz) { return clazz.cast(fragments.get(clazz)); } /** * Returns true if the requested configuration fragment is present. */ public boolean hasFragment(Class clazz) { return getFragment(clazz) != null; } /** * Returns true if all requested configuration fragment are present (this may be slow). */ public boolean hasAllFragments(Set> fragmentClasses) { for (Class fragmentClass : fragmentClasses) { if (!hasFragment(fragmentClass.asSubclass(Fragment.class))) { return false; } } return true; } /** * Which fragments does this configuration contain? */ public Set> fragmentClasses() { return fragments.keySet(); } /** * Returns true if non-functional build stamps are enabled. */ public boolean stampBinaries() { return options.stampBinaries; } /** * Returns true if extended sanity checks should be enabled. */ public boolean extendedSanityChecks() { return options.extendedSanityChecks; } /** * Returns true if we are building runfiles symlinks for this configuration. */ public boolean buildRunfiles() { return options.buildRunfiles; } /** * Returns if we are building external runfiles symlinks using the old-style structure. */ public boolean legacyExternalRunfiles() { return options.legacyExternalRunfiles; } public boolean getCheckFilesetDependenciesRecursively() { return options.checkFilesetDependenciesRecursively; } public boolean getSkyframeNativeFileset() { return options.skyframeNativeFileset; } /** * Returns user-specified test environment variables and their values, as set by the --test_env * options. */ @Deprecated @SkylarkCallable( name = "test_env", structField = true, doc = "A dictionary containing user-specified test environment variables and their values, " + "as set by the --test_env options. DO NOT USE! This is not the complete environment!" ) public ImmutableMap getTestEnv() { return testEnv.getFixedEnv(); } /** * Returns user-specified test environment variables and their values, as set by the * {@code --test_env} options. It is incomplete in that it is not a superset of the * {@link #getActionEnvironment}, but both have to be applied, with this one being applied after * the other, such that {@code --test_env} settings can override {@code --action_env} settings. */ // TODO(ulfjack): Just return the merged action and test action environment here? public ActionEnvironment getTestActionEnvironment() { return testEnv; } public int getMinParamFileSize() { return options.minParamFileSize; } @SkylarkCallable(name = "coverage_enabled", structField = true, doc = "A boolean that tells whether code coverage is enabled for this run. Note that this " + "does not compute whether a specific rule should be instrumented for code coverage " + "data collection. For that, see the " + "ctx.coverage_instrumented function.") public boolean isCodeCoverageEnabled() { return options.collectCodeCoverage; } public boolean isLLVMCoverageMapFormatEnabled() { return options.useLLVMCoverageMapFormat; } /** If false, AnalysisEnvironment doesn't register any actions created by the ConfiguredTarget. */ public boolean isActionsEnabled() { return actionsEnabled; } public RunUnder getRunUnder() { return options.runUnder; } /** * Returns true if this is a host configuration. */ public boolean isHostConfiguration() { return options.isHost; } public boolean checkVisibility() { return options.checkVisibility; } public boolean checkLicenses() { return options.checkLicenses; } public boolean enforceConstraints() { return options.enforceConstraints; } public List

Be very careful using this method. Options classes are mutable - no caller * should ever call this method if there's any change the reference might be written to. * This method only exists because {@link #cloneOptions} can be expensive when applied to * every edge in a dependency graph, which becomes possible with dynamic configurations. * *

Do not use this method without careful review with other Bazel developers.. */ public BuildOptions getOptions() { return buildOptions; } public String getCpu() { return options.cpu; } @VisibleForTesting public String getHostCpu() { return options.hostCpu; } public boolean runfilesEnabled() { switch (options.enableRunfiles) { case YES: return true; case NO: return false; default: return OS.getCurrent() != OS.WINDOWS; } } public boolean buildPythonZip() { switch (options.buildPythonZip) { case YES: return true; case NO: return false; default: return OS.getCurrent() == OS.WINDOWS; } } public boolean enableWindowsExeLauncher() { return options.windowsExeLauncher; } /** * Collects executables defined by fragments. */ private PathFragment computeShellExecutable() { PathFragment result = null; for (Fragment fragment : fragments.values()) { if (fragment.getShellExecutable() != null) { Verify.verify(result == null); result = fragment.getShellExecutable(); } } return result; } /** * Returns the transition that produces the "artifact owner" for this configuration, or null * if this configuration is its own owner. */ @Nullable public PatchTransition getArtifactOwnerTransition() { PatchTransition ownerTransition = null; for (Fragment fragment : fragments.values()) { PatchTransition fragmentTransition = fragment.getArtifactOwnerTransition(); if (fragmentTransition != null) { if (ownerTransition != null) { Verify.verify(ownerTransition == fragmentTransition, String.format( "cannot determine owner transition: fragments returning both %s and %s", ownerTransition.toString(), fragmentTransition.toString())); } ownerTransition = fragmentTransition; } } return ownerTransition; } /** * @return the list of default features used for all packages. */ public List getDefaultFeatures() { return options.defaultFeatures; } /** * Returns the "top-level" environment space, i.e. the set of environments all top-level * targets must be compatible with. An empty value implies no restrictions. */ public List