예제 #1
0
 /**
  * Returns ids for dependent nodes of a given node, sorted by attribute. Note that some
  * dependencies do not have a corresponding attribute here, and we use the null attribute to
  * represent those edges. Visibility attributes are only visited if {@code visitVisibility} is
  * {@code true}.
  *
  * <p>If {@code aspect} is null, returns the dependent nodes of the configured target node
  * representing the given target and configuration, otherwise that of the aspect node accompanying
  * the aforementioned configured target node for the specified aspect.
  *
  * <p>The values are not simply labels because this also implements the first step of applying
  * configuration transitions, namely, split transitions. This needs to be done before the labels
  * are resolved because late bound attributes depend on the configuration. A good example for this
  * is @{code :cc_toolchain}.
  *
  * <p>The long-term goal is that most configuration transitions be applied here. However, in order
  * to do that, we first have to eliminate transitions that depend on the rule class of the
  * dependency.
  */
 public final ListMultimap<Attribute, Dependency> dependentNodeMap(
     TargetAndConfiguration node,
     BuildConfiguration hostConfig,
     AspectDefinition aspect,
     AspectParameters aspectParameters,
     Set<ConfigMatchingProvider> configConditions)
     throws EvalException, InterruptedException {
   Target target = node.getTarget();
   BuildConfiguration config = node.getConfiguration();
   ListMultimap<Attribute, Dependency> outgoingEdges = ArrayListMultimap.create();
   if (target instanceof OutputFile) {
     Preconditions.checkNotNull(config);
     visitTargetVisibility(node, outgoingEdges.get(null));
     Rule rule = ((OutputFile) target).getGeneratingRule();
     outgoingEdges.put(null, new Dependency(rule.getLabel(), config));
   } else if (target instanceof InputFile) {
     visitTargetVisibility(node, outgoingEdges.get(null));
   } else if (target instanceof EnvironmentGroup) {
     visitTargetVisibility(node, outgoingEdges.get(null));
   } else if (target instanceof Rule) {
     Preconditions.checkNotNull(config);
     visitTargetVisibility(node, outgoingEdges.get(null));
     Rule rule = (Rule) target;
     ListMultimap<Attribute, LabelAndConfiguration> labelMap =
         resolveAttributes(rule, aspect, config, hostConfig, configConditions);
     visitRule(rule, aspect, aspectParameters, labelMap, outgoingEdges);
   } else if (target instanceof PackageGroup) {
     visitPackageGroup(node, (PackageGroup) target, outgoingEdges.get(null));
   } else {
     throw new IllegalStateException(target.getLabel().toString());
   }
   return outgoingEdges;
 }
예제 #2
0
 private Set<Label> getAllowedDeps(Rule rule) {
   Set<Label> allowedLabels = new HashSet<>(rule.getTransitions(dependencyFilter).values());
   allowedLabels.addAll(rule.getVisibility().getDependencyLabels());
   // We should add deps from aspects, otherwise they are going to be filtered out.
   allowedLabels.addAll(rule.getAspectLabelsSuperset(dependencyFilter));
   return allowedLabels;
 }
 /**
  * Returns the configurable attribute conditions necessary to evaluate the given configured
  * target, or null if not all dependencies have yet been SkyFrame-evaluated.
  */
 @Nullable
 private Set<ConfigMatchingProvider> getConfigurableAttributeConditions(
     TargetAndConfiguration ctg, Environment env) {
   if (!(ctg.getTarget() instanceof Rule)) {
     return ImmutableSet.of();
   }
   Rule rule = (Rule) ctg.getTarget();
   RawAttributeMapper mapper = RawAttributeMapper.of(rule);
   Set<SkyKey> depKeys = new LinkedHashSet<>();
   for (Attribute attribute : rule.getAttributes()) {
     for (Label label : mapper.getConfigurabilityKeys(attribute.getName(), attribute.getType())) {
       if (!BuildType.Selector.isReservedLabel(label)) {
         depKeys.add(ConfiguredTargetValue.key(label, ctg.getConfiguration()));
       }
     }
   }
   Map<SkyKey, SkyValue> cts = env.getValues(depKeys);
   if (env.valuesMissing()) {
     return null;
   }
   ImmutableSet.Builder<ConfigMatchingProvider> conditions = ImmutableSet.builder();
   for (SkyValue ctValue : cts.values()) {
     ConfiguredTarget ct = ((ConfiguredTargetValue) ctValue).getConfiguredTarget();
     conditions.add(Preconditions.checkNotNull(ct.getProvider(ConfigMatchingProvider.class)));
   }
   return conditions.build();
 }
예제 #4
0
  @Nullable
  @Override
  public SkyValue compute(SkyKey skyKey, SkyFunction.Environment env)
      throws RepositoryFunctionException {
    RepositoryName repositoryName = (RepositoryName) skyKey.argument();
    Rule rule = getRule(repositoryName, NewHttpArchiveRule.NAME, env);
    if (rule == null) {
      return null;
    }
    Path outputDirectory = getExternalRepositoryDirectory().getRelative(rule.getName());
    try {
      FileSystemUtils.createDirectoryAndParents(outputDirectory);
    } catch (IOException e) {
      throw new RepositoryFunctionException(
          new IOException(
              "Could not create directory for " + rule.getName() + ": " + e.getMessage()),
          Transience.TRANSIENT);
    }
    FileValue repositoryDirectory = getRepositoryDirectory(outputDirectory, env);
    if (repositoryDirectory == null) {
      return null;
    }

    // Download.
    HttpDownloadValue downloadedFileValue;
    try {
      downloadedFileValue =
          (HttpDownloadValue)
              env.getValueOrThrow(
                  HttpDownloadFunction.key(rule, outputDirectory), IOException.class);
    } catch (IOException e) {
      throw new RepositoryFunctionException(e, Transience.PERSISTENT);
    }
    if (downloadedFileValue == null) {
      return null;
    }

    // Decompress.
    DecompressorValue decompressed;
    try {
      decompressed =
          (DecompressorValue)
              env.getValueOrThrow(
                  DecompressorValue.key(
                      rule.getTargetKind(),
                      rule.getName(),
                      downloadedFileValue.getPath(),
                      outputDirectory),
                  IOException.class);
      if (decompressed == null) {
        return null;
      }
    } catch (IOException e) {
      throw new RepositoryFunctionException(new IOException(e.getMessage()), Transience.TRANSIENT);
    }

    // Add WORKSPACE and BUILD files.
    createWorkspaceFile(decompressed.getDirectory(), rule);
    return symlinkBuildFile(rule, getWorkspace(), repositoryDirectory, env);
  }
예제 #5
0
  private void collectTransitiveClosure(
      PackageProviderForConfigurations packageProvider, Set<Label> reachableLabels, Label from)
      throws NoSuchThingException {
    if (!reachableLabels.add(from)) {
      return;
    }
    Target fromTarget = packageProvider.getTarget(from);
    if (fromTarget instanceof Rule) {
      Rule rule = (Rule) fromTarget;
      if (rule.getRuleClassObject().hasAttr("srcs", BuildType.LABEL_LIST)) {
        // TODO(bazel-team): refine this. This visits "srcs" reachable under *any* configuration,
        // not necessarily the configuration actually applied to the rule. We should correlate the
        // two. However, doing so requires faithfully reflecting the configuration transitions that
        // might happen as we traverse the dependency chain.
        // TODO(bazel-team): Why don't we use AbstractAttributeMapper#visitLabels() here?
        for (List<Label> labelsForConfiguration :
            AggregatingAttributeMapper.of(rule).visitAttribute("srcs", BuildType.LABEL_LIST)) {
          for (Label label : labelsForConfiguration) {
            collectTransitiveClosure(
                packageProvider, reachableLabels, from.resolveRepositoryRelative(label));
          }
        }
      }

      if (rule.getRuleClass().equals("bind")) {
        Label actual = AggregatingAttributeMapper.of(rule).get("actual", BuildType.LABEL);
        if (actual != null) {
          collectTransitiveClosure(packageProvider, reachableLabels, actual);
        }
      }
    }
  }
 @Override
 public void setUp() throws Exception {
   super.setUp();
   rule =
       createRule(
           "x", "myrule", "cc_binary(name = 'myrule',", "          srcs = ['a', 'b', 'c'])");
   RuleClass ruleClass = rule.getRuleClassObject();
   mapper = new TestMapper(pkg, ruleClass, rule.getLabel(), rule.getAttributeContainer());
 }
 /** Adds the given rule to the stack trace of the exception (if there is one). */
 private static void addRuleToStackTrace(EvalException ex, Rule rule, BaseFunction ruleImpl) {
   if (ex instanceof EvalExceptionWithStackTrace) {
     ((EvalExceptionWithStackTrace) ex)
         .registerPhantomFuncall(
             String.format("%s(name = '%s')", rule.getRuleClass(), rule.getName()),
             rule.getLocation(),
             ruleImpl);
   }
 }
예제 #8
0
 @Override
 public AspectParameters apply(Rule rule) {
   if (rule.isAttrDefined("baz", STRING)) {
     String value = rule.getAttributeContainer().getAttr("baz").toString();
     if (!value.equals("")) {
       return new AspectParameters.Builder().addAttribute("baz", value).build();
     }
   }
   return AspectParameters.EMPTY;
 }
예제 #9
0
  /**
   * Returns the rule class defaults specified for this rule, or null if there are no such defaults.
   */
  @Nullable
  private static EnvironmentCollector maybeGetRuleClassDefaults(RuleContext ruleContext) {
    Rule rule = ruleContext.getRule();
    String restrictionAttr = RuleClass.DEFAULT_RESTRICTED_ENVIRONMENT_ATTR;
    String compatibilityAttr = RuleClass.DEFAULT_COMPATIBLE_ENVIRONMENT_ATTR;

    if (rule.isAttrDefined(restrictionAttr, BuildType.LABEL_LIST)
        || rule.isAttrDefined(compatibilityAttr, BuildType.LABEL_LIST)) {
      return new EnvironmentCollector(
          ruleContext, restrictionAttr, compatibilityAttr, new GroupDefaultsProvider());
    } else {
      return null;
    }
  }
  @Override
  public SkyValue fetch(Rule rule, Path outputDirectory, Environment env)
      throws SkyFunctionException {
    prepareLocalRepositorySymlinkTree(rule, outputDirectory);
    PathFragment pathFragment = getTargetPath(rule, getWorkspace());

    if (!symlinkLocalRepositoryContents(
        outputDirectory, getOutputBase().getFileSystem().getPath(pathFragment))) {
      return null;
    }

    AttributeMap attributes = NonconfigurableAttributeMapper.of(rule);
    String buildToolsVersion = attributes.get("build_tools_version", Type.STRING);
    Integer apiLevel = attributes.get("api_level", Type.INTEGER);

    String template = getStringResource("android_sdk_repository_template.txt");

    String buildFile =
        template
            .replaceAll("%repository_name%", rule.getName())
            .replaceAll("%build_tools_version%", buildToolsVersion)
            .replaceAll("%api_level%", apiLevel.toString());

    writeBuildFile(outputDirectory, buildFile);
    return RepositoryDirectoryValue.create(outputDirectory);
  }
예제 #11
0
 /**
  * Adds new dependencies to the given rule under the given attribute name
  *
  * @param result the builder for the attribute --> dependency labels map
  * @param rule the rule being evaluated
  * @param attrName the name of the attribute to add dependency labels to
  * @param labels the dependencies to add
  * @param configuration the configuration to apply to those dependencies
  */
 private void addExplicitDeps(
     ImmutableSortedKeyListMultimap.Builder<Attribute, LabelAndConfiguration> result,
     Rule rule,
     String attrName,
     Iterable<Label> labels,
     BuildConfiguration configuration) {
   if (!rule.isAttrDefined(attrName, BuildType.LABEL_LIST)
       && !rule.isAttrDefined(attrName, BuildType.NODEP_LABEL_LIST)) {
     return;
   }
   Attribute attribute = rule.getRuleClassObject().getAttributeByName(attrName);
   for (Label label : labels) {
     // The configuration must be the configuration after the first transition step (applying
     // split configurations). The proper configuration (null) for package groups will be set
     // later.
     result.put(attribute, LabelAndConfiguration.of(label, configuration));
   }
 }
예제 #12
0
  private void resolveExplicitAttributes(
      Rule rule,
      final BuildConfiguration configuration,
      AttributeMap attributes,
      final ImmutableSortedKeyListMultimap.Builder<Attribute, LabelAndConfiguration> builder) {
    attributes.visitLabels(
        new AttributeMap.AcceptsLabelAttribute() {
          @Override
          public void acceptLabelAttribute(Label label, Attribute attribute) {
            String attributeName = attribute.getName();
            if (attributeName.equals("abi_deps")) {
              // abi_deps is handled specially: we visit only the branch that
              // needs to be taken based on the configuration.
              return;
            }

            if (attribute.getType() == BuildType.NODEP_LABEL) {
              return;
            }

            if (attribute.isImplicit() || attribute.isLateBound()) {
              return;
            }

            builder.put(attribute, LabelAndConfiguration.of(label, configuration));
          }
        });

    // TODO(bazel-team): Remove this in favor of the new configurable attributes.
    if (attributes.getAttributeDefinition("abi_deps") != null) {
      Attribute depsAttribute = attributes.getAttributeDefinition("deps");
      MakeVariableExpander.Context context =
          new ConfigurationMakeVariableContext(rule.getPackage(), configuration);
      String abi = null;
      try {
        abi = MakeVariableExpander.expand(attributes.get("abi", Type.STRING), context);
      } catch (MakeVariableExpander.ExpansionException e) {
        // Ignore this. It will be handled during the analysis phase.
      }

      if (abi != null) {
        for (Map.Entry<String, List<Label>> entry :
            attributes.get("abi_deps", BuildType.LABEL_LIST_DICT).entrySet()) {
          try {
            if (Pattern.matches(entry.getKey(), abi)) {
              for (Label label : entry.getValue()) {
                builder.put(depsAttribute, LabelAndConfiguration.of(label, configuration));
              }
            }
          } catch (PatternSyntaxException e) {
            // Ignore this. It will be handled during the analysis phase.
          }
        }
      }
    }
  }
예제 #13
0
 protected void createDirectory(Path path, Rule rule) throws RepositoryFunctionException {
   try {
     FileSystemUtils.createDirectoryAndParents(path);
   } catch (IOException e) {
     throw new RepositoryFunctionException(
         new IOException(
             "Could not create directory for " + rule.getName() + ": " + e.getMessage()),
         Transience.TRANSIENT);
   }
 }
예제 #14
0
        @Nullable
        @Override
        public Map<String, Label> apply(Rule rule) {
          ImmutableMap.Builder<String, Label> result = ImmutableMap.builder();
          for (String tool : TOOLS) {
            result.put(
                "android/" + tool,
                Label.parseAbsoluteUnchecked("@" + rule.getName() + "//tools/android:" + tool));
          }

          return result.build();
        }
  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());
  }
예제 #16
0
  /**
   * Symlinks a BUILD file from the local filesystem into the external repository's root.
   *
   * @param rule the rule that declares the build_file path.
   * @param workspaceDirectory the workspace root for the build.
   * @param directoryValue the FileValue corresponding to the external repository's root directory.
   * @param env the Skyframe environment.
   * @return the file value of the symlink created.
   * @throws
   *     com.google.devtools.build.lib.bazel.repository.RepositoryFunction.RepositoryFunctionException
   *     if the BUILD file specified does not exist or cannot be linked.
   */
  protected RepositoryValue symlinkBuildFile(
      Rule rule, Path workspaceDirectory, FileValue directoryValue, Environment env)
      throws RepositoryFunctionException {
    AggregatingAttributeMapper mapper = AggregatingAttributeMapper.of(rule);
    PathFragment buildFile = new PathFragment(mapper.get("build_file", Type.STRING));
    Path buildFileTarget = workspaceDirectory.getRelative(buildFile);
    if (!buildFileTarget.exists()) {
      throw new RepositoryFunctionException(
          new EvalException(
              rule.getLocation(),
              String.format(
                  "In %s the 'build_file' attribute does not specify an existing file "
                      + "(%s does not exist)",
                  rule, buildFileTarget)),
          Transience.PERSISTENT);
    }

    RootedPath rootedBuild;
    if (buildFile.isAbsolute()) {
      rootedBuild =
          RootedPath.toRootedPath(
              buildFileTarget.getParentDirectory(),
              new PathFragment(buildFileTarget.getBaseName()));
    } else {
      rootedBuild = RootedPath.toRootedPath(workspaceDirectory, buildFile);
    }
    SkyKey buildFileKey = FileValue.key(rootedBuild);
    FileValue buildFileValue;
    try {
      buildFileValue =
          (FileValue)
              env.getValueOrThrow(
                  buildFileKey,
                  IOException.class,
                  FileSymlinkException.class,
                  InconsistentFilesystemException.class);
      if (buildFileValue == null) {
        return null;
      }
    } catch (IOException | FileSymlinkException | InconsistentFilesystemException e) {
      throw new RepositoryFunctionException(
          new IOException("Cannot lookup " + buildFile + ": " + e.getMessage()),
          Transience.TRANSIENT);
    }

    Path buildFilePath = directoryValue.realRootedPath().asPath().getRelative("BUILD");
    if (createSymbolicLink(buildFilePath, buildFileTarget, env) == null) {
      return null;
    }
    return RepositoryValue.createNew(directoryValue, buildFileValue);
  }
예제 #17
0
 protected SkyValue compute(Environment env, Rule rule) throws RepositoryFunctionException {
   // The output directory is always under .external-repository (to stay out of the way of
   // artifacts from this repository) and uses the rule's name to avoid conflicts with other
   // remote repository rules. For example, suppose you had the following WORKSPACE file:
   //
   // http_archive(name = "png", url = "http://example.com/downloads/png.tar.gz", sha256 = "...")
   //
   // This would download png.tar.gz to .external-repository/png/png.tar.gz.
   Path outputDirectory = getExternalRepositoryDirectory().getRelative(rule.getName());
   FileValue directoryValue = createDirectory(outputDirectory, env);
   if (directoryValue == null) {
     return null;
   }
   AggregatingAttributeMapper mapper = AggregatingAttributeMapper.of(rule);
   URL url = null;
   try {
     url = new URL(mapper.get("url", Type.STRING));
   } catch (MalformedURLException e) {
     throw new RepositoryFunctionException(
         new EvalException(rule.getLocation(), "Error parsing URL: " + e.getMessage()),
         Transience.PERSISTENT);
   }
   String sha256 = mapper.get("sha256", Type.STRING);
   HttpDownloader downloader = new HttpDownloader(url, sha256, outputDirectory);
   try {
     Path archiveFile = downloader.download();
     outputDirectory =
         DecompressorFactory.create(
                 rule.getTargetKind(), rule.getName(), archiveFile, outputDirectory)
             .decompress();
   } catch (IOException e) {
     // Assumes all IO errors transient.
     throw new RepositoryFunctionException(e, Transience.TRANSIENT);
   } catch (DecompressorException e) {
     throw new RepositoryFunctionException(new IOException(e.getMessage()), Transience.TRANSIENT);
   }
   return new RepositoryValue(outputDirectory, directoryValue);
 }
예제 #18
0
  protected static PathFragment getTargetPath(Rule rule) throws RepositoryFunctionException {
    AggregatingAttributeMapper mapper = AggregatingAttributeMapper.of(rule);
    String path = mapper.get("path", Type.STRING);
    PathFragment pathFragment = new PathFragment(path);
    if (!pathFragment.isAbsolute()) {
      throw new RepositoryFunctionException(
          new EvalException(
              rule.getLocation(),
              "In " + rule + " the 'path' attribute must specify an absolute path"),
          Transience.PERSISTENT);
    }

    return pathFragment;
  }
예제 #19
0
 /**
  * Uses a remote repository name to fetch the corresponding Rule describing how to get it. This
  * should be called from {@link SkyFunction#compute} functions, which should return null if this
  * returns null. If {@code ruleClassName} is set, the rule found must have a matching rule class
  * name.
  */
 @Nullable
 public static Rule getRule(
     RepositoryName repositoryName, @Nullable String ruleClassName, Environment env)
     throws RepositoryFunctionException {
   ExternalPackage externalPackage = getExternalPackage(env);
   if (externalPackage == null) {
     return null;
   }
   Rule rule = externalPackage.getRepositoryInfo(repositoryName);
   if (rule == null) {
     throw new RepositoryFunctionException(
         new BuildFileContainsErrorsException(
             ExternalPackage.PACKAGE_IDENTIFIER,
             "The repository named '" + repositoryName + "' could not be resolved"),
         Transience.PERSISTENT);
   }
   Preconditions.checkState(
       ruleClassName == null || rule.getRuleClass().equals(ruleClassName),
       "Got %s, was expecting a %s",
       rule,
       ruleClassName);
   return rule;
 }
 @Deprecated
 private RuleIdeInfo.Kind getRuleKind(Rule rule, ConfiguredTarget base) {
   switch (rule.getRuleClassObject().getName()) {
     case "java_library":
       return Kind.JAVA_LIBRARY;
     case "java_import":
       return Kind.JAVA_IMPORT;
     case "java_test":
       return Kind.JAVA_TEST;
     case "java_binary":
       return Kind.JAVA_BINARY;
     case "android_library":
       return Kind.ANDROID_LIBRARY;
     case "android_binary":
       return Kind.ANDROID_BINARY;
     case "android_test":
       return Kind.ANDROID_TEST;
     case "android_robolectric_test":
       return Kind.ANDROID_ROBOELECTRIC_TEST;
     case "proto_library":
       return Kind.PROTO_LIBRARY;
     case "java_plugin":
       return Kind.JAVA_PLUGIN;
     case "android_resources":
       return Kind.ANDROID_RESOURCES;
     case "cc_library":
       return Kind.CC_LIBRARY;
     case "cc_binary":
       return Kind.CC_BINARY;
     case "cc_test":
       return Kind.CC_TEST;
     case "cc_inc_library":
       return Kind.CC_INC_LIBRARY;
     case "cc_toolchain":
       return Kind.CC_TOOLCHAIN;
     case "java_wrap_cc":
       return Kind.JAVA_WRAP_CC;
     default:
       {
         if (base.getProvider(AndroidSdkProvider.class) != null) {
           return RuleIdeInfo.Kind.ANDROID_SDK;
         } else {
           return RuleIdeInfo.Kind.UNRECOGNIZED;
         }
       }
   }
 }
예제 #21
0
  protected FileValue prepareLocalRepositorySymlinkTree(Rule rule, Environment env)
      throws RepositoryFunctionException {
    Path repositoryDirectory = getExternalRepositoryDirectory().getRelative(rule.getName());
    try {
      FileSystemUtils.deleteTree(repositoryDirectory);
      FileSystemUtils.createDirectoryAndParents(repositoryDirectory);
    } catch (IOException e) {
      throw new RepositoryFunctionException(e, Transience.TRANSIENT);
    }
    FileValue directoryValue = getRepositoryDirectory(repositoryDirectory, env);

    if (directoryValue == null) {
      return null;
    }

    // Add x/WORKSPACE.
    createWorkspaceFile(repositoryDirectory, rule);
    return directoryValue;
  }
 public void testRuleProperties() throws Exception {
   assertEquals(rule.getName(), mapper.getName());
   assertEquals(rule.getLabel(), mapper.getLabel());
 }
예제 #23
0
  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)));
        }
      }
    }
  }
예제 #24
0
  private ListMultimap<Attribute, LabelAndConfiguration> resolveAttributes(
      Rule rule,
      AspectDefinition aspect,
      BuildConfiguration configuration,
      BuildConfiguration hostConfiguration,
      Set<ConfigMatchingProvider> configConditions)
      throws EvalException, InterruptedException {
    ConfiguredAttributeMapper attributeMap = ConfiguredAttributeMapper.of(rule, configConditions);
    attributeMap.validateAttributes();
    List<Attribute> attributes;
    if (aspect == null) {
      attributes = rule.getRuleClassObject().getAttributes();
    } else {
      attributes = new ArrayList<>();
      attributes.addAll(rule.getRuleClassObject().getAttributes());
      if (aspect != null) {
        attributes.addAll(aspect.getAttributes().values());
      }
    }

    ImmutableSortedKeyListMultimap.Builder<Attribute, LabelAndConfiguration> result =
        ImmutableSortedKeyListMultimap.builder();

    resolveExplicitAttributes(rule, configuration, attributeMap, result);
    resolveImplicitAttributes(rule, configuration, attributeMap, attributes, result);
    resolveLateBoundAttributes(
        rule, configuration, hostConfiguration, attributeMap, attributes, result);

    // Add the rule's visibility labels (which may come from the rule or from package defaults).
    addExplicitDeps(
        result, rule, "visibility", rule.getVisibility().getDependencyLabels(), configuration);

    // Add package default constraints when the rule doesn't explicitly declare them.
    //
    // Note that this can have subtle implications for constraint semantics. For example: say that
    // package defaults declare compatibility with ':foo' and rule R declares compatibility with
    // ':bar'. Does that mean that R is compatible with [':foo', ':bar'] or just [':bar']? In other
    // words, did R's author intend to add additional compatibility to the package defaults or to
    // override them? More severely, what if package defaults "restrict" support to just [':baz']?
    // Should R's declaration signify [':baz'] + ['bar'], [ORIGINAL_DEFAULTS] + ['bar'], or
    // something else?
    //
    // Rather than try to answer these questions with possibly confusing logic, we take the
    // simple approach of assigning the rule's "restriction" attribute to the rule-declared value if
    // it exists, else the package defaults value (and likewise for "compatibility"). This may not
    // always provide what users want, but it makes it easy for them to understand how rule
    // declarations and package defaults intermix (and how to refactor them to get what they want).
    //
    // An alternative model would be to apply the "rule declaration" / "rule class defaults"
    // relationship, i.e. the rule class' "compatibility" and "restriction" declarations are merged
    // to generate a set of default environments, then the rule's declarations are independently
    // processed on top of that. This protects against obscure coupling behavior between
    // declarations from wildly different places (e.g. it offers clear answers to the examples posed
    // above). But within the scope of a single package it seems better to keep the model simple and
    // make the user responsible for resolving ambiguities.
    if (!rule.isAttributeValueExplicitlySpecified(RuleClass.COMPATIBLE_ENVIRONMENT_ATTR)) {
      addExplicitDeps(
          result,
          rule,
          RuleClass.COMPATIBLE_ENVIRONMENT_ATTR,
          rule.getPackage().getDefaultCompatibleWith(),
          configuration);
    }
    if (!rule.isAttributeValueExplicitlySpecified(RuleClass.RESTRICTED_ENVIRONMENT_ATTR)) {
      addExplicitDeps(
          result,
          rule,
          RuleClass.RESTRICTED_ENVIRONMENT_ATTR,
          rule.getPackage().getDefaultRestrictedTo(),
          configuration);
    }

    return result.build();
  }