/** 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); } }
/** * 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); }
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; }
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); }
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))); } } } }