private LicensesProvider initializeLicensesProvider() { if (!ruleContext.getConfiguration().checkLicenses()) { return LicensesProviderImpl.EMPTY; } NestedSetBuilder<TargetLicense> builder = NestedSetBuilder.linkOrder(); BuildConfiguration configuration = ruleContext.getConfiguration(); Rule rule = ruleContext.getRule(); License toolOutputLicense = rule.getToolOutputLicense(ruleContext.attributes()); if (configuration.isHostConfiguration() && toolOutputLicense != null) { if (toolOutputLicense != License.NO_LICENSE) { builder.add(new TargetLicense(rule.getLabel(), toolOutputLicense)); } } else { if (rule.getLicense() != License.NO_LICENSE) { builder.add(new TargetLicense(rule.getLabel(), rule.getLicense())); } for (TransitiveInfoCollection dep : ruleContext.getConfiguredTargetMap().values()) { LicensesProvider provider = dep.getProvider(LicensesProvider.class); if (provider != null) { builder.addTransitive(provider.getTransitiveLicenses()); } } } return new LicensesProviderImpl(builder.build()); }
TestTargetExecutionSettings( RuleContext ruleContext, RunfilesSupport runfiles, Artifact executable, Artifact instrumentedFileManifest, int shards) { Preconditions.checkArgument(TargetUtils.isTestRule(ruleContext.getRule())); Preconditions.checkArgument(shards >= 0); BuildConfiguration config = ruleContext.getConfiguration(); List<String> targetArgs = runfiles.getArgs(); testArguments = targetArgs.isEmpty() ? config.getTestArguments() : ImmutableList.copyOf(Iterables.concat(targetArgs, config.getTestArguments())); totalShards = shards; runUnder = config.getRunUnder(); runUnderExecutable = getRunUnderExecutable(ruleContext); this.testFilter = config.getTestFilter(); this.executable = executable; this.runfilesManifest = runfiles.getRunfilesManifest(); this.runfilesInputManifest = runfiles.getRunfilesInputManifest(); this.instrumentedFileManifest = instrumentedFileManifest; }
/** * Returns the host configuration trimmed to the same fragments as the input configuration. If the * input is null, returns the top-level host configuration. * * <p>For static configurations, this unconditionally returns the (sole) top-level configuration. * * <p>This may only be called after {@link #setTopLevelHostConfiguration} has set the correct host * configuration at the top-level. */ public BuildConfiguration getHostConfiguration(BuildConfiguration config) { if (config == null || !config.useDynamicConfigurations()) { return topLevelHostConfiguration; } // TODO(bazel-team): have the fragment classes be those required by the consuming target's // transitive closure. This isn't the same as the input configuration's fragment classes - // the latter may be a proper subset of the former. // // ConfigurationFactory.getConfiguration provides the reason why: if a declared required // fragment is evaluated and returns null, it never gets added to the configuration. So if we // use the configuration's fragments as the source of truth, that excludes required fragments // that never made it in. // // If we're just trimming an existing configuration, this is no big deal (if the original // configuration doesn't need the fragment, the trimmed one doesn't either). But this method // trims a host configuration to the same scope as a target configuration. Since their options // are different, the host instance may actually be able to produce the fragment. So it's // wrong and potentially dangerous to unilaterally exclude it. Set<Class<? extends BuildConfiguration.Fragment>> fragmentClasses = config.fragmentClasses(); BuildConfiguration hostConfig = hostConfigurationCache.get(fragmentClasses); if (hostConfig != null) { return hostConfig; } BuildConfiguration trimmedConfig = topLevelHostConfiguration.clone(fragmentClasses, ruleClassProvider); hostConfigurationCache.put(fragmentClasses, trimmedConfig); return trimmedConfig; }
@Nullable private static CppConfiguration getCppConfiguration(ConfiguredTarget base) { BuildConfiguration configuration = base.getConfiguration(); if (configuration != null) { return configuration.getFragment(CppConfiguration.class); } return null; }
private Artifact scopedArtifact(PathFragment scopeRelative, boolean inGenfiles) { Root root = inGenfiles ? buildConfiguration.getGenfilesDirectory(ruleContext.getRule().getRepository()) : buildConfiguration.getBinDirectory(ruleContext.getRule().getRepository()); // The path of this artifact will be RULE_PACKAGE/SCOPERELATIVE return ruleContext.getPackageRelativeArtifact(scopeRelative, root); }
private static Artifact derivedArtifact( ConfiguredTarget base, RuleContext ruleContext, String suffix) { BuildConfiguration configuration = ruleContext.getConfiguration(); assert configuration != null; Root genfilesDirectory = configuration.getGenfilesDirectory(); PathFragment derivedFilePath = getOutputFilePath(base, ruleContext, suffix); return ruleContext .getAnalysisEnvironment() .getDerivedArtifact(derivedFilePath, genfilesDirectory); }
/** * Takes a set of configured targets, and checks if the distribution methods declared for the * targets are compatible with the constraints imposed by their prerequisites' licenses. * * @param configuredTargets the targets to check * @param keepGoing if false, and a licensing error is encountered, both generates an error * message on the reporter, <em>and</em> throws an exception. If true, then just generates a * message on the reporter. * @throws ViewCreationFailedException if the license checking failed (and not --keep_going) */ private void validateLicensingForTargets( Iterable<ConfiguredTarget> configuredTargets, boolean keepGoing) throws ViewCreationFailedException { for (ConfiguredTarget configuredTarget : configuredTargets) { final Target target = configuredTarget.getTarget(); if (TargetUtils.isTestRule(target)) { continue; // Tests are exempt from license checking } final Set<DistributionType> distribs = target.getDistributions(); BuildConfiguration config = configuredTarget.getConfiguration(); boolean staticallyLinked = (config != null) && config.performsStaticLink(); staticallyLinked |= (config != null) && (target instanceof Rule) && ((Rule) target).getRuleClassObject().hasAttr("linkopts", Type.STRING_LIST) && ConfiguredAttributeMapper.of((RuleConfiguredTarget) configuredTarget) .get("linkopts", Type.STRING_LIST) .contains("-static"); LicensesProvider provider = configuredTarget.getProvider(LicensesProvider.class); if (provider != null) { NestedSet<TargetLicense> licenses = provider.getTransitiveLicenses(); for (TargetLicense targetLicense : licenses) { if (!targetLicense .getLicense() .checkCompatibility( distribs, target, targetLicense.getLabel(), getReporter(), staticallyLinked)) { if (!keepGoing) { throw new ViewCreationFailedException("Build aborted due to licensing error"); } } } } else if (configuredTarget.getTarget() instanceof InputFile) { // Input file targets do not provide licenses because they do not // depend on the rule where their license is taken from. This is usually // not a problem, because the transitive collection of licenses always // hits the rule they come from, except when the input file is a // top-level target. Thus, we need to handle that case specially here. // // See FileTarget#getLicense for more information about the handling of // license issues with File targets. License license = configuredTarget.getTarget().getLicense(); if (!license.checkCompatibility( distribs, target, configuredTarget.getLabel(), getReporter(), staticallyLinked)) { if (!keepGoing) { throw new ViewCreationFailedException("Build aborted due to licensing error"); } } } } }
/** * Returns the host configuration trimmed to the same fragments as the input configuration. If the * input is null, returns the top-level host configuration. * * <p>For static configurations, this unconditionally returns the (sole) top-level configuration. * * <p>This may only be called after {@link #setTopLevelHostConfiguration} has set the correct host * configuration at the top-level. */ public BuildConfiguration getHostConfiguration(BuildConfiguration config) { if (config == null || !config.useDynamicConfigurations()) { return topLevelHostConfiguration; } Set<Class<? extends BuildConfiguration.Fragment>> fragmentClasses = config.fragmentClasses(); BuildConfiguration hostConfig = hostConfigurationCache.get(fragmentClasses); if (hostConfig != null) { return hostConfig; } BuildConfiguration trimmedConfig = topLevelHostConfiguration.clone(fragmentClasses, ruleClassProvider); hostConfigurationCache.put(fragmentClasses, trimmedConfig); return trimmedConfig; }
public CppCompileActionBuilder setDotdFile(PathFragment outputName, String extension) { if (CppFileTypes.mustProduceDotdFile(outputName.toString())) { if (configuration.getFragment(CppConfiguration.class).getInmemoryDotdFiles()) { // Just set the path, no artifact is constructed PathFragment file = FileSystemUtils.replaceExtension(outputName, extension); Root root = configuration.getBinDirectory(); dotdFile = new DotdFile(root.getExecPath().getRelative(file)); } else { dotdFile = new DotdFile(ruleContext.getRelatedArtifact(outputName, extension)); } } else { dotdFile = null; } return this; }
/** Returns true iff code coverage is enabled for the given target. */ private boolean isCodeCoverageEnabled() { if (configuration.isCodeCoverageEnabled()) { // If rule is matched by the instrumentation filter, enable instrumentation if (InstrumentedFilesCollector.shouldIncludeLocalSources(ruleContext)) { return true; } // At this point the rule itself is not matched by the instrumentation filter. However, we // might still want to instrument C++ rules if one of the targets listed in "deps" is // instrumented and, therefore, can supply header files that we would want to collect code // coverage for. For example, think about cc_test rule that tests functionality defined in a // header file that is supplied by the cc_library. // // Note that we only check direct prerequisites and not the transitive closure. This is done // for two reasons: // a) It is a good practice to declare libraries which you directly rely on. Including headers // from a library hidden deep inside the transitive closure makes build dependencies less // readable and can lead to unexpected breakage. // b) Traversing the transitive closure for each C++ compile action would require more complex // implementation (with caching results of this method) to avoid O(N^2) slowdown. if (ruleContext.getRule().isAttrDefined("deps", BuildType.LABEL_LIST)) { for (TransitiveInfoCollection dep : ruleContext.getPrerequisites("deps", Mode.TARGET)) { if (dep.getProvider(CppCompilationContext.class) != null && InstrumentedFilesCollector.shouldIncludeLocalSources(configuration, dep)) { return true; } } } } return false; }
/** * Sets the host configuration consisting of all fragments that will be used by the top level * targets' transitive closures. * * <p>This is used to power {@link #getHostConfiguration} during analysis, which computes * fragment-trimmed host configurations from the top-level one. */ private void setTopLevelHostConfiguration(BuildConfiguration topLevelHostConfiguration) { if (topLevelHostConfiguration.equals(this.topLevelHostConfiguration)) { return; } hostConfigurationCache.clear(); this.topLevelHostConfiguration = topLevelHostConfiguration; }
@Override public Label getDefault(Rule rule, BuildConfiguration configuration) { // If --proguard_top is not specified, null is returned. AndroidSdk will take care of // using // android_sdk.proguard then. return configuration.getFragment(AndroidConfiguration.class).getProguardLabel(); }
/** * Scans {@code action_listeners} associated with this build to see if any {@code extra_actions} * should be added to this configured target. If any action_listeners are present, a partial visit * of the artifact/action graph is performed (for as long as actions found are owned by this * {@link ConfiguredTarget}). Any actions that match the {@code action_listener} get an {@code * extra_action} associated. The output artifacts of the extra_action are reported to the {@link * AnalysisEnvironment} for bookkeeping. */ private ExtraActionArtifactsProvider initializeExtraActions() { BuildConfiguration configuration = ruleContext.getConfiguration(); if (configuration.isHostConfiguration()) { return ExtraActionArtifactsProvider.EMPTY; } ImmutableList<Artifact> extraActionArtifacts = ImmutableList.of(); NestedSetBuilder<ExtraArtifactSet> builder = NestedSetBuilder.stableOrder(); List<Label> actionListenerLabels = configuration.getActionListeners(); if (!actionListenerLabels.isEmpty() && ruleContext.getRule().getAttributeDefinition(":action_listener") != null) { ExtraActionsVisitor visitor = new ExtraActionsVisitor(ruleContext, computeMnemonicsToExtraActionMap()); // The action list is modified within the body of the loop by the addExtraAction() call, // thus the copy for (Action action : ImmutableList.copyOf(ruleContext.getAnalysisEnvironment().getRegisteredActions())) { if (!actionsWithoutExtraAction.contains(action)) { visitor.addExtraAction(action); } } extraActionArtifacts = visitor.getAndResetExtraArtifacts(); if (!extraActionArtifacts.isEmpty()) { builder.add(ExtraArtifactSet.of(ruleContext.getLabel(), extraActionArtifacts)); } } // Add extra action artifacts from dependencies for (TransitiveInfoCollection dep : ruleContext.getConfiguredTargetMap().values()) { ExtraActionArtifactsProvider provider = dep.getProvider(ExtraActionArtifactsProvider.class); if (provider != null) { builder.addTransitive(provider.getTransitiveExtraActionArtifacts()); } } if (mandatoryStampFiles != null && !mandatoryStampFiles.isEmpty()) { builder.add(ExtraArtifactSet.of(ruleContext.getLabel(), mandatoryStampFiles)); } if (extraActionArtifacts.isEmpty() && builder.isEmpty()) { return ExtraActionArtifactsProvider.EMPTY; } return new ExtraActionArtifactsProvider(extraActionArtifacts, builder.build()); }
/** * Returns a derived artifact in the bin directory obtained by appending some extension to the * main label name; the result artifact is placed in a unique "entitlements" directory. For * example, if this artifact is for a target Foo with extension ".extension", the result artifact * will be located at {target_base_path}/entitlements/Foo.extension. */ public Artifact appendExtensionForEntitlementArtifact(String extension) { PathFragment entitlementsDirectory = ruleContext.getUniqueDirectory("entitlements"); Artifact artifact = ruleContext.getDerivedArtifact( entitlementsDirectory.replaceName( addOutputPrefix(entitlementsDirectory.getBaseName(), extension)), buildConfiguration.getBinDirectory(ruleContext.getRule().getRepository())); return artifact; }
/** * Checks that if this is an environment-restricted build, all top-level targets support the * expected environments. * * @param topLevelTargets the build's top-level targets * @throws ViewCreationFailedException if constraint enforcement is on, the build declares * environment-restricted top level configurations, and any top-level target doesn't support * the expected environments */ private void checkTargetEnvironmentRestrictions( Iterable<ConfiguredTarget> topLevelTargets, PackageManager packageManager) throws ViewCreationFailedException { for (ConfiguredTarget topLevelTarget : topLevelTargets) { BuildConfiguration config = topLevelTarget.getConfiguration(); if (config == null) { // TODO(bazel-team): support file targets (they should apply package-default constraints). continue; } else if (!config.enforceConstraints() || config.getTargetEnvironments().isEmpty()) { continue; } // Parse and collect this configuration's environments. EnvironmentCollection.Builder builder = new EnvironmentCollection.Builder(); for (Label envLabel : config.getTargetEnvironments()) { try { Target env = packageManager.getLoadedTarget(envLabel); builder.put(ConstraintSemantics.getEnvironmentGroup(env), envLabel); } catch (NoSuchPackageException | NoSuchTargetException | ConstraintSemantics.EnvironmentLookupException e) { throw new ViewCreationFailedException("invalid target environment", e); } } EnvironmentCollection expectedEnvironments = builder.build(); // Now check the target against those environments. SupportedEnvironmentsProvider provider = Verify.verifyNotNull(topLevelTarget.getProvider(SupportedEnvironmentsProvider.class)); Collection<Label> missingEnvironments = ConstraintSemantics.getUnsupportedEnvironments( provider.getEnvironments(), expectedEnvironments); if (!missingEnvironments.isEmpty()) { throw new ViewCreationFailedException( String.format( "This is a restricted-environment build. %s does not support" + " required environment%s %s", topLevelTarget.getLabel(), missingEnvironments.size() == 1 ? "" : "s", Joiner.on(", ").join(missingEnvironments))); } } }
/** * Adds the given targets as dependencies - this can include explicit dependencies on other rules * (like from a "deps" attribute) and also implicit dependencies on runtime libraries. */ public CcLibraryHelper addDeps(Iterable<? extends TransitiveInfoCollection> deps) { for (TransitiveInfoCollection dep : deps) { Preconditions.checkArgument( dep.getConfiguration() == null || configuration.equalsOrIsSupersetOf(dep.getConfiguration()), "dep " + dep.getLabel() + " has a different config than " + ruleContext.getLabel()); this.deps.add(dep); } return this; }
@Override public List<Label> resolve( Rule rule, AttributeMap attributes, BuildConfiguration configuration) { // action_listeners are special rules; they tell the build system to add extra_actions to // existing rules. As such they need an edge to every ConfiguredTarget with the limitation // that they only run on the target configuration and should not operate on // action_listeners // and extra_actions themselves (to avoid cycles). return configuration.getActionListeners(); }
@Override public boolean equals(Object other) { if (!(other instanceof Dependency)) { return false; } Dependency otherDep = (Dependency) other; return label.equals(otherDep.label) && (configuration == otherDep.configuration || (configuration != null && configuration.equals(otherDep.configuration))) && aspects.equals(otherDep.aspects); }
public void assertConfigurationsHaveUniqueOutputDirectories( BuildConfigurationCollection configCollection) throws Exception { Collection<BuildConfiguration> allConfigs = configCollection.getAllConfigurations(); Map<Root, BuildConfiguration> outputPaths = new HashMap<>(); for (BuildConfiguration config : allConfigs) { if (config.isActionsEnabled()) { BuildConfiguration otherConfig = outputPaths.get(config.getOutputDirectory()); if (otherConfig != null) { throw new IllegalStateException( "The output path '" + config.getOutputDirectory() + "' is the same for configurations '" + config + "' and '" + otherConfig + "'"); } else { outputPaths.put(config.getOutputDirectory(), config); } } } }
/** Returns null if any build-info values are not ready. */ @Nullable CachingAnalysisEnvironment createAnalysisEnvironment( ArtifactOwner owner, boolean isSystemEnv, EventHandler eventHandler, Environment env, BuildConfiguration config) { if (config != null && !getWorkspaceStatusValues(env, config)) { return null; } boolean extendedSanityChecks = config != null && config.extendedSanityChecks(); boolean allowRegisteringActions = config == null || config.isActionsEnabled(); return new CachingAnalysisEnvironment( artifactFactory, owner, isSystemEnv, extendedSanityChecks, eventHandler, env, allowRegisteringActions, binTools); }
/** * Checks that the implicit labels are reachable from the loaded labels. The loaded labels are * those returned from {@link BuildOptions#getAllLabels()}, and the implicit ones are those that * need to be available for late-bound attributes. */ private void sanityCheckImplicitLabels( Collection<Label> reachableLabels, BuildConfiguration config) throws InvalidConfigurationException { for (Map.Entry<String, Label> entry : config.getImplicitLabels().entries()) { if (!reachableLabels.contains(entry.getValue())) { throw new InvalidConfigurationException( "The required " + entry.getKey() + " target is not transitively reachable from a command-line option: '" + entry.getValue() + "'"); } } }
/** * Returns a raw link command for the given link invocation, including both command and arguments * (argv). After any further usage-specific processing, this can be passed to {@link * #finalizeWithLinkstampCommands} to give the final command line. * * @return raw link command line. */ public List<String> getRawLinkArgv() { List<String> argv = new ArrayList<>(); switch (linkTargetType) { case EXECUTABLE: argv.add(cppConfiguration.getCppExecutable().getPathString()); argv.addAll(featureConfiguration.getCommandLine(actionName, variables)); argv.addAll(noWholeArchiveFlags); addToolchainFlags(argv); break; case DYNAMIC_LIBRARY: if (interfaceOutput != null) { argv.add(configuration.getShExecutable().getPathString()); argv.add("-c"); argv.add( "build_iface_so=\"$0\"; impl=\"$1\"; iface=\"$2\"; cmd=\"$3\"; shift 3; " + "\"$cmd\" \"$@\" && \"$build_iface_so\" \"$impl\" \"$iface\""); argv.add(interfaceSoBuilder.getExecPathString()); argv.add(output.getExecPathString()); argv.add(interfaceOutput.getExecPathString()); } argv.add(cppConfiguration.getCppExecutable().getPathString()); argv.addAll(featureConfiguration.getCommandLine(actionName, variables)); argv.addAll(noWholeArchiveFlags); addToolchainFlags(argv); break; case STATIC_LIBRARY: case PIC_STATIC_LIBRARY: case ALWAYS_LINK_STATIC_LIBRARY: case ALWAYS_LINK_PIC_STATIC_LIBRARY: // The static library link command follows this template: // ar <cmd> <output_archive> <input_files...> argv.add(cppConfiguration.getArExecutable().getPathString()); argv.addAll( cppConfiguration.getArFlags(cppConfiguration.archiveType() == Link.ArchiveType.THIN)); argv.add(output.getExecPathString()); argv.addAll(featureConfiguration.getCommandLine(actionName, variables)); argv.addAll(noWholeArchiveFlags); break; default: throw new IllegalArgumentException(); } return argv; }
private void visitRule( Rule rule, AspectDefinition aspect, AspectParameters aspectParameters, ListMultimap<Attribute, LabelAndConfiguration> labelMap, ListMultimap<Attribute, Dependency> outgoingEdges) { Preconditions.checkNotNull(labelMap); for (Map.Entry<Attribute, Collection<LabelAndConfiguration>> entry : labelMap.asMap().entrySet()) { Attribute attribute = entry.getKey(); for (LabelAndConfiguration dep : entry.getValue()) { Label label = dep.getLabel(); BuildConfiguration config = dep.getConfiguration(); Target toTarget; try { toTarget = getTarget(label); } catch (NoSuchThingException e) { throw new IllegalStateException( "not found: " + label + " from " + rule + " in " + attribute.getName()); } if (toTarget == null) { continue; } BuildConfiguration.TransitionApplier transitionApplier = config.getTransitionApplier(); if (config.useDynamicConfigurations() && config.isHostConfiguration() && !BuildConfiguration.usesNullConfiguration(toTarget)) { // This condition is needed because resolveLateBoundAttributes may switch config to // the host configuration, which is the only case DependencyResolver applies a // configuration transition outside of this method. We need to reflect that // transition in the results of this method, but config.evaluateTransition is hard-set // to return a NONE transition when the input is a host config. Since the outside // caller originally passed the *original* value of config (before the possible // switch), it can mistakenly interpret the result as a NONE transition from the // original value of config. This condition fixes that. Another fix would be to have // config.evaluateTransition return a HOST transition when the input config is a host, // but since this blemish is specific to DependencyResolver it seems best to keep the // fix here. // TODO(bazel-team): eliminate this special case by passing transitionApplier to // resolveLateBoundAttributes, so that method uses the same interface for transitions. transitionApplier.applyTransition(Attribute.ConfigurationTransition.HOST); } else { config.evaluateTransition(rule, attribute, toTarget, transitionApplier); } for (Dependency dependency : transitionApplier.getDependencies( label, requiredAspects(aspect, aspectParameters, attribute, toTarget, rule))) { outgoingEdges.put(entry.getKey(), dependency); } } } }
/** * Creates a builder for an owner that is not required to be rule. * * <p>If errors are found when creating the {@code CppCompileAction}, builders constructed this * way will throw a runtime exception. */ @VisibleForTesting public CppCompileActionBuilder( ActionOwner owner, AnalysisEnvironment analysisEnvironment, Artifact sourceFile, Label sourceLabel, BuildConfiguration configuration) { this.owner = owner; this.actionContext = CppCompileActionContext.class; this.cppConfiguration = configuration.getFragment(CppConfiguration.class); this.analysisEnvironment = analysisEnvironment; this.sourceFile = sourceFile; this.sourceLabel = sourceLabel; this.configuration = configuration; this.mandatoryInputsBuilder = NestedSetBuilder.stableOrder(); this.pluginInputsBuilder = NestedSetBuilder.stableOrder(); this.lipoScannableMap = ImmutableMap.of(); }
@Override public List<Label> getDefault(Rule rule, BuildConfiguration configuration) { return ImmutableList.copyOf(configuration.getPlugins()); }
@Override public Label getDefault(Rule rule, BuildConfiguration configuration) { return configuration.getFragment(JavaConfiguration.class).getProguardBinary(); }
@Override public List<Label> getDefault(Rule rule, BuildConfiguration configuration) { return ImmutableList.copyOf( configuration.getFragment(JavaConfiguration.class).getExtraProguardSpecs()); }
@Override public Label resolve(Rule rule, AttributeMap attributes, BuildConfiguration configuration) { return configuration.getFragment(AndroidConfiguration.class).getSdk(); }
@Override public Label getDefault(Rule rule, BuildConfiguration configuration) { return configuration.getFragment(JavaConfiguration.class).getToolchainLabel(); }
private void resolveLateBoundAttributes( Rule rule, BuildConfiguration configuration, BuildConfiguration hostConfiguration, AttributeMap attributeMap, Iterable<Attribute> attributes, ImmutableSortedKeyListMultimap.Builder<Attribute, LabelAndConfiguration> builder) throws EvalException, InterruptedException { for (Attribute attribute : attributes) { if (!attribute.isLateBound() || !attribute.getCondition().apply(attributeMap)) { continue; } List<BuildConfiguration> actualConfigurations = ImmutableList.of(configuration); if (attribute.getConfigurationTransition() instanceof SplitTransition<?>) { Preconditions.checkState(attribute.getConfigurator() == null); // TODO(bazel-team): This ends up applying the split transition twice, both here and in the // visitRule method below - this is not currently a problem, because the configuration graph // never contains nested split transitions, so the second application is idempotent. actualConfigurations = configuration.getSplitConfigurations( (SplitTransition<?>) attribute.getConfigurationTransition()); } for (BuildConfiguration actualConfig : actualConfigurations) { @SuppressWarnings("unchecked") LateBoundDefault<BuildConfiguration> lateBoundDefault = (LateBoundDefault<BuildConfiguration>) attribute.getLateBoundDefault(); if (lateBoundDefault.useHostConfiguration()) { actualConfig = hostConfiguration; } // TODO(bazel-team): This might be too expensive - can we cache this somehow? if (!lateBoundDefault.getRequiredConfigurationFragments().isEmpty()) { if (!actualConfig.hasAllFragments(lateBoundDefault.getRequiredConfigurationFragments())) { continue; } } // TODO(bazel-team): We should check if the implementation tries to access an undeclared // fragment. Object actualValue = lateBoundDefault.getDefault(rule, actualConfig); if (EvalUtils.isNullOrNone(actualValue)) { continue; } try { if (attribute.getType() == BuildType.LABEL) { Label label = BuildType.LABEL.cast(actualValue); builder.put(attribute, LabelAndConfiguration.of(label, actualConfig)); } else if (attribute.getType() == BuildType.LABEL_LIST) { for (Label label : BuildType.LABEL_LIST.cast(actualValue)) { builder.put(attribute, LabelAndConfiguration.of(label, actualConfig)); } } else { throw new IllegalStateException( String.format( "Late bound attribute '%s' is not a label or a label list", attribute.getName())); } } catch (ClassCastException e) { throw new EvalException( rule.getLocation(), String.format( "When computing the default value of %s, expected '%s', got '%s'", attribute.getName(), attribute.getType(), EvalUtils.getDataTypeName(actualValue, true))); } } } }