diff options
author | Greg Estren <gregce@google.com> | 2015-05-26 22:37:44 +0000 |
---|---|---|
committer | Laurent Le Brun <laurentlb@google.com> | 2015-05-27 16:46:09 +0000 |
commit | 531fc042bba338613978cf65d86a6299859f5e1e (patch) | |
tree | dd383164063fde81bf1e6a6135c32f5cf406385e /src/main/java/com/google/devtools/build | |
parent | b46c6351321c2950badb4c0e4071e3f20d49e338 (diff) |
Add an entry to TransitiveTargetValue that tracks the
configuration fragments needed by a rule's transitive
closure.
Also add a Skyframe BuildConfiguration node.
Memory and performance profiling shows no noticeable
performance hit in loading or analysis and a 0.35%
memory increase for moderately sized (by Google
standards) build graphs when these are depended
upon in ConfiguredTargetFunction.
--
MOS_MIGRATED_REVID=94517099
Diffstat (limited to 'src/main/java/com/google/devtools/build')
7 files changed, 329 insertions, 14 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java b/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java index 0c53af0e15..267b8d32de 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java @@ -993,7 +993,7 @@ public final class BuildConfiguration implements Serializable { return builder.build(); } - BuildConfiguration(BlazeDirectories directories, + public BuildConfiguration(BlazeDirectories directories, Map<Class<? extends Fragment>, Fragment> fragmentsMap, BuildOptions buildOptions, boolean actionsDisabled) { diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/BuildConfigurationFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/BuildConfigurationFunction.java new file mode 100644 index 0000000000..3de1bc5c09 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/skyframe/BuildConfigurationFunction.java @@ -0,0 +1,102 @@ +// Copyright 2015 Google Inc. 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.skyframe; + +import static com.google.devtools.build.lib.analysis.config.BuildConfiguration.Fragment; + +import com.google.common.collect.ClassToInstanceMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.MutableClassToInstanceMap; +import com.google.devtools.build.lib.analysis.BlazeDirectories; +import com.google.devtools.build.lib.analysis.config.BuildConfiguration; +import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException; +import com.google.devtools.build.skyframe.SkyFunction; +import com.google.devtools.build.skyframe.SkyFunctionException; +import com.google.devtools.build.skyframe.SkyKey; +import com.google.devtools.build.skyframe.SkyValue; +import com.google.devtools.build.skyframe.ValueOrException; + +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +/** + * A builder for {@link BuildConfigurationValue} instances. + */ +public class BuildConfigurationFunction implements SkyFunction { + + private final BlazeDirectories directories; + + public BuildConfigurationFunction(BlazeDirectories directories) { + this.directories = directories; + } + + @Override + public SkyValue compute(SkyKey skyKey, Environment env) throws InterruptedException, + SkyFunctionException { + BuildConfigurationValue.Key key = (BuildConfigurationValue.Key) skyKey.argument(); + Set<Fragment> fragments; + try { + fragments = getConfigurationFragments(key, env); + } catch (InvalidConfigurationException e) { + throw new BuildConfigurationFunctionException(e); + } + if (fragments == null) { + return null; + } + + ClassToInstanceMap<Fragment> fragmentsMap = MutableClassToInstanceMap.create(); + for (Fragment fragment : fragments) { + fragmentsMap.put(fragment.getClass(), fragment); + } + + return new BuildConfigurationValue( + new BuildConfiguration(directories, fragmentsMap, key.getBuildOptions(), + !key.actionsEnabled())); + } + + private Set<Fragment> getConfigurationFragments(BuildConfigurationValue.Key key, Environment env) + throws InvalidConfigurationException { + // Get SkyKeys for the fragments we need to load. + Set<SkyKey> fragmentKeys = new LinkedHashSet<>(); + for (Class<? extends BuildConfiguration.Fragment> fragmentClass : key.getFragments()) { + fragmentKeys.add(ConfigurationFragmentValue.key(key.getBuildOptions(), fragmentClass)); + } + + // Load them as Skyframe deps. + Map<SkyKey, ValueOrException<InvalidConfigurationException>> fragmentDeps = + env.getValuesOrThrow(fragmentKeys, InvalidConfigurationException.class); + if (env.valuesMissing()) { + return null; + } + + // Collect and return the results. + ImmutableSet.Builder<Fragment> fragments = ImmutableSet.builder(); + for (ValueOrException<InvalidConfigurationException> value : fragmentDeps.values()) { + fragments.add(((ConfigurationFragmentValue) value.get()).getFragment()); + } + return fragments.build(); + } + + @Override + public String extractTag(SkyKey skyKey) { + return null; + } + + private static final class BuildConfigurationFunctionException extends SkyFunctionException { + public BuildConfigurationFunctionException(InvalidConfigurationException e) { + super(e, Transience.PERSISTENT); + } + } +} diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/BuildConfigurationValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/BuildConfigurationValue.java new file mode 100644 index 0000000000..10f5d30776 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/skyframe/BuildConfigurationValue.java @@ -0,0 +1,113 @@ +// Copyright 2015 Google Inc. 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.skyframe; + +import com.google.common.base.Preconditions; +import com.google.devtools.build.lib.analysis.config.BuildConfiguration; +import com.google.devtools.build.lib.analysis.config.BuildOptions; +import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe; +import com.google.devtools.build.skyframe.SkyKey; +import com.google.devtools.build.skyframe.SkyValue; + +import java.io.Serializable; +import java.util.Objects; +import java.util.Set; + +/** + * A Skyframe value representing a {@link BuildConfiguration}. + */ +// TODO(bazel-team): mark this immutable when BuildConfiguration is immutable. +// @Immutable +@ThreadSafe +public class BuildConfigurationValue implements SkyValue { + + private final BuildConfiguration configuration; + + BuildConfigurationValue(BuildConfiguration configuration) { + this.configuration = configuration; + } + + public BuildConfiguration getConfiguration() { + return configuration; + } + + /** + * Returns the key for a requested configuration. + * + * @param fragments the fragments the configuration should contain + * @param buildOptions the build options the fragments should be built from + */ + @ThreadSafe + public static SkyKey key(Set<Class<? extends BuildConfiguration.Fragment>> fragments, + BuildOptions buildOptions) { + return new SkyKey(SkyFunctions.BUILD_CONFIGURATION, + new Key(fragments, buildOptions, true)); + } + + /** + * Returns the key for a requested action-disabled configuration (actions generated by rules + * under the configuration are ignored). + * + * @param fragments the fragments the configuration should contain + * @param buildOptions the build options the fragments should be built from + */ + @ThreadSafe + public static SkyKey disabledActionsKey( + Set<Class<? extends BuildConfiguration.Fragment>> fragments, + BuildOptions buildOptions) { + return new SkyKey(SkyFunctions.BUILD_CONFIGURATION, + new Key(fragments, buildOptions, false)); + } + + static final class Key implements Serializable { + private final Set<Class<? extends BuildConfiguration.Fragment>> fragments; + private final BuildOptions buildOptions; + private final boolean enableActions; + + Key(Set<Class<? extends BuildConfiguration.Fragment>> fragments, + BuildOptions buildOptions, boolean enableActions) { + this.fragments = fragments; + this.buildOptions = Preconditions.checkNotNull(buildOptions); + this.enableActions = enableActions; + } + + Set<Class<? extends BuildConfiguration.Fragment>> getFragments() { + return fragments; + } + + BuildOptions getBuildOptions() { + return buildOptions; + } + + boolean actionsEnabled() { + return enableActions; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Key)) { + return false; + } + Key otherConfig = (Key) o; + return Objects.equals(fragments, otherConfig.fragments) + && Objects.equals(buildOptions, otherConfig.buildOptions) + && otherConfig.actionsEnabled() == enableActions; + } + + @Override + public int hashCode() { + return Objects.hash(fragments, buildOptions, enableActions); + } + } +} diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctions.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctions.java index ba0ea07e84..633e95a591 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctions.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctions.java @@ -54,6 +54,8 @@ public final class SkyFunctions { SkyFunctionName.computed("TARGET_COMPLETION"); public static final SkyFunctionName TEST_COMPLETION = SkyFunctionName.computed("TEST_COMPLETION"); + public static final SkyFunctionName BUILD_CONFIGURATION = + SkyFunctionName.computed("BUILD_CONFIGURATION"); public static final SkyFunctionName CONFIGURATION_FRAGMENT = SkyFunctionName.computed("CONFIGURATION_FRAGMENT"); public static final SkyFunctionName CONFIGURATION_COLLECTION = diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java index d445175daa..afb7a50898 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java @@ -69,6 +69,7 @@ import com.google.devtools.build.lib.packages.Package; import com.google.devtools.build.lib.packages.PackageFactory; import com.google.devtools.build.lib.packages.PackageIdentifier; import com.google.devtools.build.lib.packages.Preprocessor; +import com.google.devtools.build.lib.packages.RuleClassProvider; import com.google.devtools.build.lib.packages.RuleVisibility; import com.google.devtools.build.lib.packages.Target; import com.google.devtools.build.lib.pkgcache.FilteringPolicy; @@ -282,6 +283,7 @@ public abstract class SkyframeExecutor implements WalkableGraphFactory { Predicate<PathFragment> allowedMissingInputs) { ExternalFilesHelper externalFilesHelper = new ExternalFilesHelper(pkgLocator, immutableDirectories, errorOnExternalFiles); + RuleClassProvider ruleClassProvider = pkgFactory.getRuleClassProvider(); // We use an immutable map builder for the nice side effect that it throws if a duplicate key // is inserted. ImmutableMap.Builder<SkyFunctionName, SkyFunction> map = ImmutableMap.builder(); @@ -296,9 +298,9 @@ public abstract class SkyframeExecutor implements WalkableGraphFactory { map.put(SkyFunctions.PACKAGE_LOOKUP, new PackageLookupFunction(deletedPackages)); map.put(SkyFunctions.CONTAINING_PACKAGE_LOOKUP, new ContainingPackageLookupFunction()); map.put(SkyFunctions.AST_FILE_LOOKUP, new ASTFileLookupFunction( - pkgLocator, packageManager, pkgFactory.getRuleClassProvider())); + pkgLocator, packageManager, ruleClassProvider)); map.put(SkyFunctions.SKYLARK_IMPORTS_LOOKUP, new SkylarkImportLookupFunction( - pkgFactory.getRuleClassProvider(), pkgFactory)); + ruleClassProvider, pkgFactory)); map.put(SkyFunctions.GLOB, new GlobFunction()); map.put(SkyFunctions.TARGET_PATTERN, new TargetPatternFunction(pkgLocator)); map.put(SkyFunctions.PREPARE_DEPS_OF_PATTERNS, new PrepareDepsOfPatternsFunction()); @@ -307,12 +309,13 @@ public abstract class SkyframeExecutor implements WalkableGraphFactory { reporter, pkgFactory, packageManager, showLoadingProgress, packageFunctionCache, numPackagesLoaded)); map.put(SkyFunctions.TARGET_MARKER, new TargetMarkerFunction()); - map.put(SkyFunctions.TRANSITIVE_TARGET, new TransitiveTargetFunction()); + map.put(SkyFunctions.TRANSITIVE_TARGET, new TransitiveTargetFunction(ruleClassProvider)); map.put(SkyFunctions.CONFIGURED_TARGET, new ConfiguredTargetFunction(new BuildViewProvider())); map.put(SkyFunctions.ASPECT, new AspectFunction(new BuildViewProvider())); map.put(SkyFunctions.POST_CONFIGURED_TARGET, new PostConfiguredTargetFunction(new BuildViewProvider())); + map.put(SkyFunctions.BUILD_CONFIGURATION, new BuildConfigurationFunction(directories)); map.put(SkyFunctions.CONFIGURATION_COLLECTION, new ConfigurationCollectionFunction( configurationFactory, configurationPackages)); map.put(SkyFunctions.CONFIGURATION_FRAGMENT, new ConfigurationFragmentFunction( diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/TransitiveTargetFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/TransitiveTargetFunction.java index 1e02ef38c5..ae5ca36550 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/TransitiveTargetFunction.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/TransitiveTargetFunction.java @@ -14,8 +14,12 @@ package com.google.devtools.build.lib.skyframe; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; +import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider; +import com.google.devtools.build.lib.analysis.config.BuildConfiguration; +import com.google.devtools.build.lib.analysis.config.ConfigurationFragmentFactory; import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.events.Event; @@ -31,6 +35,7 @@ import com.google.devtools.build.lib.packages.Package; import com.google.devtools.build.lib.packages.PackageGroup; import com.google.devtools.build.lib.packages.PackageIdentifier; import com.google.devtools.build.lib.packages.Rule; +import com.google.devtools.build.lib.packages.RuleClassProvider; import com.google.devtools.build.lib.packages.Target; import com.google.devtools.build.lib.packages.TargetUtils; import com.google.devtools.build.lib.syntax.Label; @@ -42,6 +47,7 @@ import com.google.devtools.build.skyframe.ValueOrException; import java.util.Collection; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map.Entry; import java.util.Set; @@ -52,6 +58,12 @@ import java.util.Set; */ public class TransitiveTargetFunction implements SkyFunction { + private final ConfiguredRuleClassProvider ruleClassProvider; + + TransitiveTargetFunction(RuleClassProvider ruleClassProvider) { + this.ruleClassProvider = (ConfiguredRuleClassProvider) ruleClassProvider; + } + @Override public SkyValue compute(SkyKey key, Environment env) throws TransitiveTargetFunctionException { Label label = (Label) key.argument(); @@ -119,6 +131,11 @@ public class TransitiveTargetFunction implements SkyFunction { NestedSetBuilder<PackageIdentifier> transitiveSuccessfulPkgs = NestedSetBuilder.stableOrder(); NestedSetBuilder<PackageIdentifier> transitiveUnsuccessfulPkgs = NestedSetBuilder.stableOrder(); NestedSetBuilder<Label> transitiveTargets = NestedSetBuilder.stableOrder(); + NestedSetBuilder<Class<? extends BuildConfiguration.Fragment>> transitiveConfigFragments = + NestedSetBuilder.stableOrder(); + // No need to store directly required fragments that are also required by deps. + Set<Class<? extends BuildConfiguration.Fragment>> configFragmentsFromDeps = + new LinkedHashSet<>(); PackageIdentifier packageId = target.getPackage().getPackageIdentifier(); if (packageLoadedSuccessfully) { @@ -131,36 +148,67 @@ public class TransitiveTargetFunction implements SkyFunction { // Process deps from attributes of current target. Iterable<SkyKey> depKeys = getLabelDepKeys(target); successfulTransitiveLoading &= processDeps(env, target, transitiveRootCauses, - transitiveSuccessfulPkgs, transitiveUnsuccessfulPkgs, transitiveTargets, depKeys); + transitiveSuccessfulPkgs, transitiveUnsuccessfulPkgs, transitiveTargets, depKeys, + transitiveConfigFragments, configFragmentsFromDeps); if (env.valuesMissing()) { return null; } // Process deps from aspects. depKeys = getLabelAspectKeys(target, env); successfulTransitiveLoading &= processDeps(env, target, transitiveRootCauses, - transitiveSuccessfulPkgs, transitiveUnsuccessfulPkgs, transitiveTargets, depKeys); + transitiveSuccessfulPkgs, transitiveUnsuccessfulPkgs, transitiveTargets, depKeys, + transitiveConfigFragments, configFragmentsFromDeps); if (env.valuesMissing()) { return null; } + // Get configuration fragments directly required by this target. + if (target instanceof Rule) { + Set<Class<?>> configFragments = + target.getAssociatedRule().getRuleClassObject().getRequiredConfigurationFragments(); + // An empty result means this rule requires all fragments (which practically means + // the rule isn't yet declaring its actually needed fragments). So load everything. + configFragments = configFragments.isEmpty() ? getAllFragments() : configFragments; + for (Class<?> fragment : configFragments) { + if (!configFragmentsFromDeps.contains(fragment)) { + transitiveConfigFragments.add((Class<? extends BuildConfiguration.Fragment>) fragment); + } + } + } + NestedSet<PackageIdentifier> successfullyLoadedPackages = transitiveSuccessfulPkgs.build(); NestedSet<PackageIdentifier> unsuccessfullyLoadedPackages = transitiveUnsuccessfulPkgs.build(); NestedSet<Label> loadedTargets = transitiveTargets.build(); if (successfulTransitiveLoading) { return TransitiveTargetValue.successfulTransitiveLoading(successfullyLoadedPackages, - unsuccessfullyLoadedPackages, loadedTargets); + unsuccessfullyLoadedPackages, loadedTargets, transitiveConfigFragments.build()); } else { NestedSet<Label> rootCauses = transitiveRootCauses.build(); return TransitiveTargetValue.unsuccessfulTransitiveLoading(successfullyLoadedPackages, - unsuccessfullyLoadedPackages, loadedTargets, rootCauses, errorLoadingTarget); + unsuccessfullyLoadedPackages, loadedTargets, rootCauses, errorLoadingTarget, + transitiveConfigFragments.build()); } } + /** + * Returns every configuration fragment known to the system. + */ + private Set<Class<?>> getAllFragments() { + ImmutableSet.Builder<Class<?>> builder = + ImmutableSet.builder(); + for (ConfigurationFragmentFactory factory : ruleClassProvider.getConfigurationFragments()) { + builder.add(factory.creates()); + } + return builder.build(); + } + private boolean processDeps(Environment env, Target target, NestedSetBuilder<Label> transitiveRootCauses, NestedSetBuilder<PackageIdentifier> transitiveSuccessfulPkgs, NestedSetBuilder<PackageIdentifier> transitiveUnsuccessfulPkgs, - NestedSetBuilder<Label> transitiveTargets, Iterable<SkyKey> depKeys) { + NestedSetBuilder<Label> transitiveTargets, Iterable<SkyKey> depKeys, + NestedSetBuilder<Class<? extends BuildConfiguration.Fragment>> transitiveConfigFragments, + Set<Class<? extends BuildConfiguration.Fragment>> addedConfigFragments) { boolean successfulTransitiveLoading = true; for (Entry<SkyKey, ValueOrException<NoSuchThingException>> entry : env.getValuesOrThrow(depKeys, NoSuchThingException.class).entrySet()) { @@ -193,6 +241,21 @@ public class TransitiveTargetFunction implements SkyFunction { transitiveTargetValue.getErrorLoadingTarget(), env.getListener()); } } + + NestedSet<Class<? extends BuildConfiguration.Fragment>> depFragments = + transitiveTargetValue.getTransitiveConfigFragments(); + Collection<Class<? extends BuildConfiguration.Fragment>> depFragmentsAsCollection = + depFragments.toCollection(); + // The simplest collection technique would be to unconditionally add all deps' nested + // sets to the current target's nested set. But when there's large overlap between their + // fragment needs, this produces unnecessarily bloated nested sets and a lot of references + // that don't contribute anything unique to the required fragment set. So we optimize here + // by completely skipping sets that don't offer anything new. More fine-tuned optimization + // is possible, but this offers a good balance between simplicity and practical efficiency. + if (!addedConfigFragments.containsAll(depFragmentsAsCollection)) { + transitiveConfigFragments.addTransitive(depFragments); + addedConfigFragments.addAll(depFragmentsAsCollection); + } } return successfulTransitiveLoading; } diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/TransitiveTargetValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/TransitiveTargetValue.java index 69b9638c6f..e668d5786e 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/TransitiveTargetValue.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/TransitiveTargetValue.java @@ -13,6 +13,7 @@ // limitations under the License. package com.google.devtools.build.lib.skyframe; +import com.google.devtools.build.lib.analysis.config.BuildConfiguration; import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.collect.nestedset.Order; @@ -49,16 +50,19 @@ public class TransitiveTargetValue implements SkyValue { private NestedSet<Label> transitiveTargets; @Nullable private NestedSet<Label> transitiveRootCauses; @Nullable private NoSuchTargetException errorLoadingTarget; + private NestedSet<Class<? extends BuildConfiguration.Fragment>> transitiveConfigFragments; private TransitiveTargetValue(NestedSet<PackageIdentifier> transitiveSuccessfulPkgs, NestedSet<PackageIdentifier> transitiveUnsuccessfulPkgs, NestedSet<Label> transitiveTargets, @Nullable NestedSet<Label> transitiveRootCauses, - @Nullable NoSuchTargetException errorLoadingTarget) { + @Nullable NoSuchTargetException errorLoadingTarget, + NestedSet<Class<? extends BuildConfiguration.Fragment>> transitiveConfigFragments) { this.transitiveSuccessfulPkgs = transitiveSuccessfulPkgs; this.transitiveUnsuccessfulPkgs = transitiveUnsuccessfulPkgs; this.transitiveTargets = transitiveTargets; this.transitiveRootCauses = transitiveRootCauses; this.errorLoadingTarget = errorLoadingTarget; + this.transitiveConfigFragments = transitiveConfigFragments; } private void writeObject(ObjectOutputStream out) throws IOException { @@ -75,6 +79,7 @@ public class TransitiveTargetValue implements SkyValue { // TODO(bazel-team): Deal with this properly once we have efficient serialization of NestedSets. out.writeObject(transitiveRootCauses); out.writeObject(errorLoadingTarget); + out.writeObject(transitiveConfigFragments); } @SuppressWarnings("unchecked") @@ -90,22 +95,26 @@ public class TransitiveTargetValue implements SkyValue { transitiveTargets = NestedSetBuilder.emptySet(Order.STABLE_ORDER); transitiveRootCauses = (NestedSet<Label>) in.readObject(); errorLoadingTarget = (NoSuchTargetException) in.readObject(); + transitiveConfigFragments = + (NestedSet<Class<? extends BuildConfiguration.Fragment>>) in.readObject(); } static TransitiveTargetValue unsuccessfulTransitiveLoading( NestedSet<PackageIdentifier> transitiveSuccessfulPkgs, NestedSet<PackageIdentifier> transitiveUnsuccessfulPkgs, NestedSet<Label> transitiveTargets, - NestedSet<Label> rootCauses, @Nullable NoSuchTargetException errorLoadingTarget) { + NestedSet<Label> rootCauses, @Nullable NoSuchTargetException errorLoadingTarget, + NestedSet<Class<? extends BuildConfiguration.Fragment>> transitiveConfigFragments) { return new TransitiveTargetValue(transitiveSuccessfulPkgs, transitiveUnsuccessfulPkgs, - transitiveTargets, rootCauses, errorLoadingTarget); + transitiveTargets, rootCauses, errorLoadingTarget, transitiveConfigFragments); } static TransitiveTargetValue successfulTransitiveLoading( NestedSet<PackageIdentifier> transitiveSuccessfulPkgs, NestedSet<PackageIdentifier> transitiveUnsuccessfulPkgs, - NestedSet<Label> transitiveTargets) { + NestedSet<Label> transitiveTargets, + NestedSet<Class<? extends BuildConfiguration.Fragment>> transitiveConfigFragments) { return new TransitiveTargetValue(transitiveSuccessfulPkgs, transitiveUnsuccessfulPkgs, - transitiveTargets, null, null); + transitiveTargets, null, null, transitiveConfigFragments); } /** Returns the error, if any, from loading the target. */ @@ -135,6 +144,29 @@ public class TransitiveTargetValue implements SkyValue { return transitiveRootCauses; } + /** + * Returns the set of {@link BuildConfiguration.Fragment} classes required to configure a + * rule's transitive closure. These are used to instantiate the right + * {@link ConfigurationFragmentValue} instances for a rule's {@link BuildConfigurationValue}. + * + * <p>This provides the basis for rule-scoped configurations. For example, Java-related build + * flags have nothing to do with C++. So changing a Java flag shouldn't invalidate a C++ rule + * (unless it has transitive dependencies on other Java rules). Likewise, a C++ rule shouldn't + * fail because the Java configuration doesn't recognize the chosen architecture. + * + * <p>The general principle is that a rule can be influenced by the configuration parameters it + * directly uses and the configuration parameters its transitive dependencies use (since it + * reads its dependencies as part of analysis). So we need to 1) determine which configuration + * fragments provide these parameters, 2) load those fragments, then 3) create a configuration + * from them to feed the rule's configured target. This provides the first step. + * + * <p>See + * {@link com.google.devtools.build.lib.packages.RuleClass.Builder#requiredConfigurationFragments} + */ + NestedSet<Class<? extends BuildConfiguration.Fragment>> getTransitiveConfigFragments() { + return transitiveConfigFragments; + } + @ThreadSafe public static SkyKey key(Label label) { return new SkyKey(SkyFunctions.TRANSITIVE_TARGET, label); |