// 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.Lists; 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.AspectCollection; import com.google.devtools.build.lib.analysis.BlazeDirectories; import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider; import com.google.devtools.build.lib.analysis.Dependency; import com.google.devtools.build.lib.analysis.RuleContext; import com.google.devtools.build.lib.analysis.actions.FileWriteAction; import com.google.devtools.build.lib.analysis.config.BuildConfigurationCollection.Transitions; 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.Attribute; import com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition; import com.google.devtools.build.lib.packages.Attribute.Configurator; import com.google.devtools.build.lib.packages.Attribute.SplitTransition; import com.google.devtools.build.lib.packages.Attribute.Transition; import com.google.devtools.build.lib.packages.InputFile; import com.google.devtools.build.lib.packages.PackageGroup; import com.google.devtools.build.lib.packages.Rule; import com.google.devtools.build.lib.packages.RuleClass; import com.google.devtools.build.lib.packages.RuleClassProvider; import com.google.devtools.build.lib.packages.RuleTransitionFactory; import com.google.devtools.build.lib.packages.Target; import com.google.devtools.build.lib.rules.test.TestActionBuilder; 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.Preconditions; 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.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Queue; 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 (see * {@link ConfigurationFactory#getConfiguration}. * *

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

This used to revert certain special cases to static configurations because dynamic * configuration didn't support them. But now all builds use dynamic configurations. This * value will be removed once we know no one is setting it. * * @deprecated use {@link #NOTRIM} instead */ @Deprecated NOTRIM_PARTIAL, /** * Same as NOTRIM. * *

This used to disable dynamic configurations (while the feature was still being * developed). But now all builds use dynamic configurations. This value will be removed * once we know no one is setting it. * * @deprecated use {@link #NOTRIM} instead */ @Deprecated OFF } /** * Converter for --experimental_dynamic_configs. */ public static class DynamicConfigsConverter extends EnumConverter { public DynamicConfigsConverter() { super(DynamicConfigsMode.class, "dynamic configurations mode"); } @Override public DynamicConfigsMode convert(String input) throws OptionsParsingException { DynamicConfigsMode userSetValue = super.convert(input); if (userSetValue == DynamicConfigsMode.OFF || userSetValue == DynamicConfigsMode.NOTRIM_PARTIAL) { return DynamicConfigsMode.NOTRIM; } else { return userSetValue; } } } @Option( name = "experimental_dynamic_configs", defaultValue = "notrim", converter = DynamicConfigsConverter.class, documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, effectTags = {OptionEffectTag.UNKNOWN}, help = "Dynamically instantiates build configurations instead of using the default " + "static globally defined ones" ) public DynamicConfigsMode useDynamicConfigurations; @Option( name = "experimental_enable_runfiles", defaultValue = "auto", documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, effectTags = {OptionEffectTag.UNKNOWN}, help = "Enable runfiles; off on Windows, on on other platforms" ) public TriState enableRunfiles; @Option( name = "build_python_zip", defaultValue = "auto", documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, effectTags = {OptionEffectTag.UNKNOWN}, help = "Build python executable zip; on on Windows, off on other platforms" ) public TriState buildPythonZip; @Override public FragmentOptions getHost(boolean fallback) { Options host = (Options) getDefault(); host.outputDirectoryName = "host"; host.compilationMode = CompilationMode.OPT; host.isHost = true; host.useDynamicConfigurations = useDynamicConfigurations; host.commandLineBuildVariables = commandLineBuildVariables; host.enforceConstraints = enforceConstraints; host.separateGenfilesDirectory = separateGenfilesDirectory; if (fallback) { // In the fallback case, we have already tried the target options and they didn't work, so // now we try the default options; the hostCpu field has the default value, because we use // getDefault() above. host.cpu = host.hostCpu; } else { host.cpu = hostCpu; } // === Runfiles === // Ideally we could force this the other way, and skip runfiles construction // for host tools which are never run locally, but that's probably a very // small optimization. host.buildRunfiles = true; // === Linkstamping === // Disable all link stamping for the host configuration, to improve action // cache hit rates for tools. host.stampBinaries = false; // === Visibility === host.checkVisibility = checkVisibility; // === Licenses === host.checkLicenses = checkLicenses; // === Fileset === host.skyframeNativeFileset = skyframeNativeFileset; // === Allow runtime_deps to depend on neverlink Java libraries. host.allowRuntimeDepsOnNeverLink = allowRuntimeDepsOnNeverLink; // === Pass on C++ compiler features. host.defaultFeatures = ImmutableList.copyOf(defaultFeatures); return host; } @Override public Map> getDefaultsLabels(BuildConfiguration.Options commonOptions) { return ImmutableMap.>of( "coverage_support", ImmutableSet.of(coverageSupport), "coverage_report_generator", ImmutableSet.of(coverageReportGenerator)); } } private final String checksum; private Transitions transitions; private Set allReachableConfigurations; private final ImmutableMap, Fragment> fragments; private final ImmutableMap> skylarkVisibleFragments; private final RepositoryName mainRepositoryName; private final DynamicTransitionMapper dynamicTransitionMapper; private final ImmutableSet reservedActionMnemonics; /** * Directories in the output tree. * *

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 (!useDynamicConfigurations()) { // Static configurations aren't safe for value equality because they include transition // references to other configurations (see setConfigurationTransitions). For example, imagine // in one build target config A has a reference to data config B. Now imagine a second build // where target config C has a reference to data config D. If A and B are value-equal, that // means a call to ConfiguredTargetKey("//foo", C) might return the SkyKey for ("//foo", A). // This is not just possible but *likely* due to SkyKey interning (see // SkyKey.SKY_KEY_INTERNER). This means a data transition on that config could incorrectly // return B, which is not safe because B is not necessarily value-equal to D. // // This becomes safe with dynamic configurations: transitions are completely triggered by // external logic and configs have no awareness of them at all. return false; } 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() { if (!useDynamicConfigurations()) { return BuildConfiguration.super.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")); } if (options.testShardingStrategy == TestActionBuilder.TestShardingStrategy.EXPERIMENTAL_HEURISTIC) { reporter.handle(Event.warn( "Heuristic sharding is intended as a one-off experimentation tool for determing the " + "benefit from sharding certain tests. Please don't keep this option in your " + ".blazerc or continuous build")); } } /** * @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, @Nullable DynamicTransitionMapper dynamicTransitionMapper) { 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); this.dynamicTransitionMapper = dynamicTransitionMapper; // 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(), dynamicTransitionMapper); 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(); } /** * Set the outgoing configuration transitions. During the lifetime of a given build configuration, * this must happen exactly once, shortly after the configuration is created. */ public void setConfigurationTransitions(Transitions transitions) { // TODO(bazel-team): This method makes the object mutable - get rid of it. Dynamic // configurations should eventually make this obsolete. Preconditions.checkNotNull(transitions); Preconditions.checkState(this.transitions == null); this.transitions = transitions; } public Transitions getTransitions() { return transitions; } /** * For static configurations, returns all configurations that can be reached from this one through * any kind of configuration transition. * *

For dynamic configurations, returns the current configuration (since configurations aren't * reached through other configurations). */ public synchronized Collection getAllReachableConfigurations() { if (allReachableConfigurations == null) { // This is needed for every configured target in skyframe m2, so we cache it. // We could alternatively make the corresponding dependencies into a skyframe node. this.allReachableConfigurations = computeAllReachableConfigurations(); } return allReachableConfigurations; } private Set computeAllReachableConfigurations() { if (useDynamicConfigurations()) { return ImmutableSet.of(this); } Set result = new LinkedHashSet<>(); Queue queue = new LinkedList<>(); queue.add(this); while (!queue.isEmpty()) { BuildConfiguration config = queue.remove(); if (!result.add(config)) { continue; } config.getTransitions().addDirectlyReachableConfigurations(queue); } return result; } /** * Returns the new configuration after traversing a dependency edge with a given configuration * transition. * * @param transition the configuration transition * @return the new configuration * @throws IllegalArgumentException if the transition is a {@link SplitTransition} * * TODO(bazel-team): remove this as part of the static -> dynamic configuration migration */ public BuildConfiguration getConfiguration(Transition transition) { Preconditions.checkArgument(!(transition instanceof SplitTransition)); // The below call precondition-checks we're indeed using static configurations. return transitions.getStaticConfiguration(transition); } /** * Returns the new configurations after traversing a dependency edge with a given split * transition. * * @param transition the split configuration transition * @return the new configurations */ public List getSplitConfigurations(SplitTransition transition) { return transitions.getSplitConfigurations(transition); } /** * A common interface for static vs. dynamic configuration implementations that allows * common configuration and transition-selection logic to seamlessly work with either. * *

The basic role of this interface is to "accept" a desired transition and produce * an actual configuration change from it in an implementation-appropriate way. */ public interface TransitionApplier { /** * Accepts the given configuration transition. The implementation decides how to turn * this into an actual configuration. This may be called multiple times (representing a * request for a sequence of transitions). */ void applyTransition(Transition transition); /** * Accepts the given split transition. The implementation decides how to turn this into * actual configurations. */ void split(SplitTransition splitTransition); /** * Returns whether or not all configuration(s) represented by the current state of this * instance are null. */ boolean isNull(); /** * Applies the given attribute configurator to the current configuration(s). */ void applyAttributeConfigurator(Configurator configurator); /** * Calls {@link Transitions#configurationHook} on the current configuration(s) represent by * this instance. */ void applyConfigurationHook(Rule fromRule, Attribute attribute, Target toTarget); /** * Populates a {@link com.google.devtools.build.lib.analysis.Dependency} * for each configuration represented by this instance. * TODO(bazel-team): this is a really ugly reverse dependency: factor this away. */ Iterable getDependencies(Label label, AspectCollection aspects); } /** * Transition applier for static configurations. This implementation populates * {@link com.google.devtools.build.lib.analysis.Dependency} objects with * actual configurations. * * TODO(bazel-team): remove this when dynamic configurations are fully production-ready. */ private static class StaticTransitionApplier implements TransitionApplier { // The configuration(s) this applier applies to dep rules. Plural because of split transitions. // May change multiple times: the ultimate transition might be a sequence of intermediate // transitions. List toConfigurations; private StaticTransitionApplier(BuildConfiguration originalConfiguration) { this.toConfigurations = ImmutableList.of(originalConfiguration); } @Override public void applyTransition(Transition transition) { if (transition == Attribute.ConfigurationTransition.NULL) { toConfigurations = Lists.asList(null, new BuildConfiguration[0]); } else { ImmutableList.Builder newConfigs = ImmutableList.builder(); for (BuildConfiguration currentConfig : toConfigurations) { newConfigs.add(currentConfig.getTransitions().getStaticConfiguration(transition)); } toConfigurations = newConfigs.build(); } } @Override public void split(SplitTransition splitTransition) { // Split transitions can't be nested, so if we're splitting we must be doing it over // a single config. toConfigurations = Iterables.getOnlyElement(toConfigurations).getSplitConfigurations(splitTransition); } @Override public boolean isNull() { return toConfigurations.size() == 1 ? Iterables.getOnlyElement(toConfigurations) == null : false; } @Override public void applyAttributeConfigurator(Configurator configurator) { // There should only be one output configuration at this point: splits don't occur down // attributes with attribute configurators. We can lift this restriction later if desired. BuildOptions toOptions = Iterables.getOnlyElement(toConfigurations).getOptions(); applyTransition(configurator.apply(toOptions)); } @Override public void applyConfigurationHook(Rule fromRule, Attribute attribute, Target toTarget) { ImmutableList.Builder toConfigs = ImmutableList.builder(); for (BuildConfiguration currentConfig : toConfigurations) { // BuildConfigurationCollection.configurationHook can apply further transitions. We want // those transitions to only affect currentConfig (not everything in toConfigurations), so // we use a delegate bound to only that config. StaticTransitionApplier delegate = new StaticTransitionApplier(currentConfig); currentConfig.getTransitions().configurationHook(fromRule, attribute, toTarget, delegate); currentConfig = Iterables.getOnlyElement(delegate.toConfigurations); // Allow rule classes to override their own configurations. Rule associatedRule = toTarget.getAssociatedRule(); if (associatedRule != null) { @SuppressWarnings("unchecked") RuleClass.Configurator func = associatedRule.getRuleClassObject().getConfigurator(); currentConfig = func.apply(associatedRule, currentConfig); } toConfigs.add(currentConfig); } toConfigurations = toConfigs.build(); } @Override public Iterable getDependencies( Label label, AspectCollection aspects) { ImmutableList.Builder deps = ImmutableList.builder(); for (BuildConfiguration config : toConfigurations) { deps.add(config != null ? Dependency.withConfigurationAndAspects(label, config, aspects) : Dependency.withNullConfiguration(label)); } return deps.build(); } } /** * Transition applier for dynamic configurations. This implementation populates * {@link com.google.devtools.build.lib.analysis.Dependency} objects with * transitions that the caller subsequently creates configurations from. */ private static class DynamicTransitionApplier implements TransitionApplier { private final DynamicTransitionMapper dynamicTransitionMapper; private boolean splitApplied = false; // The transition this applier applies to dep rules. When multiple transitions are requested, // this is a ComposingSplitTransition, which encapsulates the sequence into a single instance // so calling code doesn't need special logic to support combinations. private Transition currentTransition = Attribute.ConfigurationTransition.NONE; private DynamicTransitionApplier(DynamicTransitionMapper dynamicTransitionMapper) { this.dynamicTransitionMapper = dynamicTransitionMapper; } /** * Returns true if the given transition should not be modifiable by subsequent ones, i.e. * once this transition is applied it's the final word on the output configuration. */ private static boolean isFinal(Transition transition) { return (transition == Attribute.ConfigurationTransition.NULL || transition == HostTransition.INSTANCE); } @Override public void applyTransition(Transition transitionToApply) { currentTransition = composeTransitions(currentTransition, transitionToApply); } /** * Composes two transitions together efficiently. */ private Transition composeTransitions(Transition transition1, Transition transition2) { if (isFinal(transition1)) { return transition1; } else if (transition2 == Attribute.ConfigurationTransition.NONE) { return transition1; } else if (transition2 == Attribute.ConfigurationTransition.NULL) { // A NULL transition can just replace earlier transitions: no need to cfpose them. return Attribute.ConfigurationTransition.NULL; } else if (transition2 == Attribute.ConfigurationTransition.HOST) { // A HOST transition can just replace earlier transitions: no need to compose them. // But it also improves performance: host transitions are common, and // ConfiguredTargetFunction has special optimized logic to handle them. If they were buried // in the last segment of a ComposingSplitTransition, those optimizations wouldn't trigger. return HostTransition.INSTANCE; } // TODO(gregce): remove this dynamic transition mapping when static configs are removed. Transition dynamicTransition = dynamicTransitionMapper.map(transition2); return transition1 == Attribute.ConfigurationTransition.NONE ? dynamicTransition : new ComposingSplitTransition(transition1, dynamicTransition); } @Override // TODO(gregce): fold this into applyTransition during the static config code removal cleanup public void split(SplitTransition splitTransition) { // This "single split" check doesn't come from any design restriction. Its purpose is to // protect against runaway graph explosion, e.g. applying split[1,2,3] -> split[4,5,6] -> ... // and getting 3^n versions of a dep. So it's fine to loosen or lift this restriction // for a principled use case. Preconditions.checkState(!splitApplied, "dependency edges may apply at most one split transition"); Preconditions.checkState(currentTransition != Attribute.ConfigurationTransition.NULL, "cannot apply splits after null transitions (null transitions are expected to be final)"); Preconditions.checkState(currentTransition != HostTransition.INSTANCE, "cannot apply splits after host transitions (host transitions are expected to be final)"); currentTransition = currentTransition == Attribute.ConfigurationTransition.NONE ? splitTransition : new ComposingSplitTransition(currentTransition, splitTransition); splitApplied = true; } @Override public boolean isNull() { return currentTransition == Attribute.ConfigurationTransition.NULL; } /** * A {@link PatchTransition} that applies an attribute configurator over some input options * to determine which transition to use, then applies that transition over those options * for the final output. */ private static final class AttributeConfiguratorTransition implements PatchTransition { private final Configurator configurator; AttributeConfiguratorTransition(Configurator configurator) { this.configurator = configurator; } @Override public BuildOptions apply(BuildOptions options) { return Iterables.getOnlyElement( ComposingSplitTransition.apply(options, configurator.apply(options))); } @Override public boolean defaultsToSelf() { return false; } } /** * Unlike the static config version, this one can be composed with arbitrary transitions * (including splits). */ @Override public void applyAttributeConfigurator(Configurator configurator) { if (isFinal(currentTransition)) { return; } currentTransition = composeTransitions(currentTransition, new AttributeConfiguratorTransition(configurator)); } @Override public void applyConfigurationHook(Rule fromRule, Attribute attribute, Target toTarget) { if (isFinal(currentTransition)) { return; } Rule associatedRule = toTarget.getAssociatedRule(); RuleTransitionFactory transitionFactory = associatedRule.getRuleClassObject().getTransitionFactory(); if (transitionFactory != null) { // dynamicTransitionMapper is only needed because of Attribute.ConfigurationTransition.DATA: // this is C++-specific but non-C++ rules declare it. So they can't directly provide the // C++-specific patch transition that implements it. PatchTransition ruleClassTransition = (PatchTransition) dynamicTransitionMapper.map(transitionFactory.buildTransitionFor(associatedRule)); if (ruleClassTransition != null) { if (currentTransition == ConfigurationTransition.NONE) { currentTransition = ruleClassTransition; } else { currentTransition = new ComposingSplitTransition(currentTransition, ruleClassTransition); } } } /** * Dynamic configurations don't support rule class configurators (which may need intermediate * configurations to apply). The only current use of that is LIPO, which dynamic * configurations have a different code path for: * {@link com.google.devtools.build.lib.rules.cpp.CppRuleClasses.LIPO_ON_DEMAND}. * * So just check that if there is a configurator, it's for LIPO, in which case we can ignore * it. */ if (associatedRule != null) { @SuppressWarnings("unchecked") RuleClass.Configurator func = associatedRule.getRuleClassObject().getConfigurator(); Verify.verify(func == RuleClass.NO_CHANGE || func.getCategory().equals("lipo")); } } @Override public Iterable getDependencies( Label label, AspectCollection aspects) { return ImmutableList.of( isNull() // We can trivially set the final value for null-configured targets now. This saves // us from having to recreate a new Dependency object for the final value later. Since // there are lots of null-configured targets (e.g. all source files), this can add up // over the course of a build. ? Dependency.withNullConfiguration(label) : Dependency.withTransitionAndAspects(label, currentTransition, aspects)); } } /** * Returns the {@link TransitionApplier} that should be passed to {#evaluateTransition} calls. */ public TransitionApplier getTransitionApplier() { return useDynamicConfigurations() ? new DynamicTransitionApplier(dynamicTransitionMapper) : new StaticTransitionApplier(this); } /** * Returns true if the given target uses a null configuration, false otherwise. Consider * this method the "source of truth" for determining this. */ public static boolean usesNullConfiguration(Target target) { return target instanceof InputFile || target instanceof PackageGroup; } /** * Calculates the configurations of a direct dependency. If a rule in some BUILD file refers * to a target (like another rule or a source file) using a label attribute, that target needs * to have a configuration, too. This method figures out the proper configuration for the * dependency. * * @param fromRule the rule that's depending on some target * @param attribute the attribute using which the rule depends on that target (eg. "srcs") * @param toTarget the target that's dependeded on * @param transitionApplier the transition applier to accept transitions requests */ public void evaluateTransition(final Rule fromRule, final Attribute attribute, final Target toTarget, TransitionApplier transitionApplier) { // Fantastic configurations and where to find them: // I. Input files and package groups have no configurations. We don't want to duplicate them. if (usesNullConfiguration(toTarget)) { transitionApplier.applyTransition(Attribute.ConfigurationTransition.NULL); return; } // II. Host configurations never switch to another. All prerequisites of host targets have the // same host configuration. if (isHostConfiguration()) { transitionApplier.applyTransition(Attribute.ConfigurationTransition.NONE); return; } // Make sure config_setting dependencies are resolved in the referencing rule's configuration, // unconditionally. For example, given: // // genrule( // name = 'myrule', // tools = select({ '//a:condition': [':sometool'] }) // // all labels in "tools" get resolved in the host configuration (since the "tools" attribute // declares a host configuration transition). We want to explicitly exclude configuration labels // from these transitions, since their *purpose* is to do computation on the owning // rule's configuration. // TODO(bazel-team): don't require special casing here. This is far too hackish. if (toTarget instanceof Rule && ((Rule) toTarget).getRuleClassObject().isConfigMatcher()) { transitionApplier.applyTransition(Attribute.ConfigurationTransition.NONE); // Unnecessary. return; } // TODO(gregce): make the below transitions composable (i.e. take away the "else" clauses) once // the static config code path is removed. They can be mixed freely with dynamic configurations. if (attribute.hasSplitConfigurationTransition()) { Preconditions.checkState(attribute.getConfigurator() == null); transitionApplier.split( (SplitTransition) attribute.getSplitTransition(fromRule)); } else { // III. Attributes determine configurations. The configuration of a prerequisite is determined // by the attribute. @SuppressWarnings("unchecked") Configurator configurator = (Configurator) attribute.getConfigurator(); if (configurator != null) { // TODO(gregce): remove this branch when static config logic is removed. Attribute // configurators can just be implemented as standard attribute transitions, via // applyTransition. transitionApplier.applyAttributeConfigurator(configurator); } else { transitionApplier.applyTransition(attribute.getConfigurationTransition()); } } transitionApplier.applyConfigurationHook(fromRule, attribute, toTarget); } /** * 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; } public List getTestArguments() { return options.testArguments; } public String getTestFilter() { return options.testFilter; } /** * 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 TriState cacheTestResults() { return options.cacheTestResults; } 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 TestActionBuilder.TestShardingStrategy testShardingStrategy() { return options.testShardingStrategy; } /** * @return number of times the given test should run. * If the test doesn't match any of the filters, runs it once. */ public int getRunsPerTestForLabel(Label label) { for (PerLabelOptions perLabelRuns : options.runsPerTest) { if (perLabelRuns.isIncluded(label)) { return Integer.parseInt(Iterables.getOnlyElement(perLabelRuns.getOptions())); } } return 1; } 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; } } /** * 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. * *

This is the dynamic configuration version of {@link #getArtifactOwnerConfiguration}. */ @Nullable public PatchTransition getArtifactOwnerTransition() { Preconditions.checkState(useDynamicConfigurations()); 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; } /** * See {@code BuildConfigurationCollection.Transitions.getArtifactOwnerConfiguration()}. * *

This is the static configuration version of {@link #getArtifactOwnerTransition}. */ public BuildConfiguration getArtifactOwnerConfiguration() { Preconditions.checkState(!useDynamicConfigurations()); return transitions.getArtifactOwnerConfiguration(); } /** * @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