Skip to content

Commit

Permalink
implement import_prefix and strip_import_prefix
Browse files Browse the repository at this point in the history
  • Loading branch information
lberki committed Nov 26, 2018
1 parent af36058 commit 0b36536
Show file tree
Hide file tree
Showing 6 changed files with 278 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ public class BazelProtoLibrary implements RuleConfiguredTargetFactory {
@Override
public ConfiguredTarget create(RuleContext ruleContext) throws ActionConflictException {
ProtoInfo protoInfo = ProtoCommon.createProtoInfo(ruleContext);
if (ruleContext.hasErrors()) {
return null;
}

ProtoCompileActionBuilder.writeDescriptorSet(ruleContext, protoInfo, Services.ALLOW);

Runfiles dataRunfiles =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ the source root will be the workspace directory (default).
source.
<!-- #END_BLAZE_RULE.ATTRIBUTE --> */
.add(attr("exports", LABEL_LIST).allowedRuleClasses("proto_library").allowedFileTypes())
.add(attr("strip_import_prefix", STRING))
.add(attr("import_prefix", STRING))
.advertiseProvider(ProtoInfo.class)
.build();
}
Expand Down
149 changes: 137 additions & 12 deletions src/main/java/com/google/devtools/build/lib/rules/proto/ProtoCommon.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import static com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode.TARGET;
import static com.google.devtools.build.lib.collect.nestedset.Order.STABLE_ORDER;
import static com.google.devtools.build.lib.syntax.Type.STRING;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
Expand All @@ -24,11 +25,13 @@
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.Runfiles;
import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
import com.google.devtools.build.lib.analysis.actions.SymlinkAction;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
import com.google.devtools.build.lib.packages.BuildType;
import com.google.devtools.build.lib.syntax.Type;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
Expand Down Expand Up @@ -115,17 +118,37 @@ private static NestedSet<String> computeTransitiveProtoSourceRoots(
return protoPath.build();
}


final static class Library {
private final ImmutableList<Artifact> sources;
private final String sourceRoot;

Library(ImmutableList<Artifact> sources, String sourceRoot) {
this.sources = sources;
this.sourceRoot = sourceRoot;
}

public ImmutableList<Artifact> getSources() {
return sources;
}

public String getSourceRoot() {
return sourceRoot;
}
}

/**
* Returns the source root of the currentl {@code proto_library} or "." if it's the source root.
* Returns the source root of the current {@code proto_library} or "." if it's the source root.
*
* <p>Build will fail if the {@code proto_source_root} of the current library is neither the
* package name nor the source root.
*/
// TODO(lberki): This should really be a PathFragment. Unfortunately, it's on the Starlark API of
// ProtoInfo so it's not an easy change :(
private static String getProtoSourceRoot(RuleContext ruleContext) {
private static Library getProtoSourceRoot(
RuleContext ruleContext, ImmutableList<Artifact> directSources) {
String protoSourceRoot =
ruleContext.attributes().get("proto_source_root", Type.STRING);
ruleContext.attributes().get("proto_source_root", STRING);
if (!protoSourceRoot.isEmpty()) {
checkProtoSourceRootIsTheSameAsPackage(protoSourceRoot, ruleContext);
}
Expand All @@ -140,7 +163,98 @@ private static String getProtoSourceRoot(RuleContext ruleContext) {
.getPathUnderExecRoot()
.getRelative(protoSourceRoot)
.getPathString();
return result.isEmpty() ? "." : result;
return new Library(directSources, result.isEmpty() ? "." : result);
}

private static PathFragment getPathFragmentAttribute(
RuleContext ruleContext, String attributeName) {
if (!ruleContext.attributes().has(attributeName)) {
return null;
}

if (!ruleContext.attributes().isAttributeValueExplicitlySpecified(attributeName)) {
return null;
}

PathFragment pathFragment = PathFragment.create(
ruleContext.attributes().get(attributeName, STRING));

if (pathFragment.containsUplevelReferences()) {
ruleContext.attributeError(attributeName, "should not contain uplevel references");
}

return pathFragment;
}

private static Library createVirtualImportDirectoryMaybe(
RuleContext ruleContext, ImmutableList<Artifact> protoSources) {
PathFragment importPrefix = getPathFragmentAttribute(ruleContext, "import_prefix");
PathFragment stripImportPrefix = getPathFragmentAttribute(ruleContext, "strip_import_prefix");

if (importPrefix == null && stripImportPrefix == null) {
// Simple case, no magic required.
return null;
}

if (!ruleContext.attributes().get("proto_source_root", STRING).isEmpty()) {
ruleContext.ruleError("the 'proto_source_root' attribute is incompatible with "
+ "'strip_import_prefix' and 'import_prefix");
return null;
}

if (importPrefix.isAbsolute()) {
ruleContext.attributeError("import_prefix", "should be a relative path");
}

if (ruleContext.hasErrors()) {
return null;
}

if (stripImportPrefix == null) {
stripImportPrefix = PathFragment.EMPTY_FRAGMENT;
} else if (stripImportPrefix.isAbsolute()) {
stripImportPrefix = ruleContext.getLabel()
.getPackageIdentifier()
.getRepository()
.getSourceRoot()
.getRelative(stripImportPrefix.toRelative());
} else {
stripImportPrefix = ruleContext.getPackageDirectory().getRelative(stripImportPrefix);
}

if (importPrefix == null) {
importPrefix = PathFragment.EMPTY_FRAGMENT;
}

ImmutableList.Builder<Artifact> symlinks = ImmutableList.builder();
PathFragment sourceRootPath = ruleContext.getUniqueDirectory("_virtual_imports");

for (Artifact realProtoSource : protoSources) {
if (!realProtoSource.getRootRelativePath().startsWith(stripImportPrefix)) {
ruleContext.ruleError(String.format(
".proto file '%s' is not under the specific strip prefix '%s'",
realProtoSource.getExecPathString(), stripImportPrefix.getPathString()));
continue;
}

PathFragment importPath = importPrefix.getRelative(
realProtoSource.getRootRelativePath().relativeTo(stripImportPrefix));

Artifact virtualProtoSource = ruleContext.getDerivedArtifact(
sourceRootPath.getRelative(importPath), ruleContext.getBinOrGenfilesDirectory());

ruleContext.registerAction(SymlinkAction.toArtifact(
ruleContext.getActionOwner(), realProtoSource, virtualProtoSource,
"Symlinking virtual .proto sources for " + ruleContext.getLabel()));

symlinks.add(virtualProtoSource);
}

String sourceRoot = ruleContext.getBinOrGenfilesDirectory()
.getExecPath()
.getRelative(sourceRootPath)
.getPathString();
return new Library(symlinks.build(), sourceRoot);
}

/**
Expand Down Expand Up @@ -224,22 +338,33 @@ public static ProtoInfo createProtoInfo(RuleContext ruleContext) {
checkSourceFilesAreInSamePackage(ruleContext);
ImmutableList<Artifact> directProtoSources =
ruleContext.getPrerequisiteArtifacts("srcs", Mode.TARGET).list();
String protoSourceRoot = ProtoCommon.getProtoSourceRoot(ruleContext);
Library library = createVirtualImportDirectoryMaybe(ruleContext, directProtoSources);
if (ruleContext.hasErrors()) {
return null;
}

if (library == null ){
library = getProtoSourceRoot(ruleContext, directProtoSources);
}

if (ruleContext.hasErrors()) {
return null;
}

NestedSet<Artifact> transitiveProtoSources =
computeTransitiveProtoSources(ruleContext, directProtoSources);
computeTransitiveProtoSources(ruleContext, library.getSources());
NestedSet<String> transitiveProtoSourceRoots =
computeTransitiveProtoSourceRoots(ruleContext, protoSourceRoot);
computeTransitiveProtoSourceRoots(ruleContext, library.getSourceRoot());

NestedSet<Artifact> strictImportableProtosForDependents =
computeStrictImportableProtosForDependents(ruleContext, directProtoSources);
computeStrictImportableProtosForDependents(ruleContext, library.getSources());
NestedSet<Artifact> strictImportableProtos = computeStrictImportableProtos(ruleContext);
NestedSet<String> strictImportableProtoSourceRoots =
computeStrictImportableProtoSourceRoots(ruleContext, protoSourceRoot);
computeStrictImportableProtoSourceRoots(ruleContext, library.getSourceRoot());

NestedSet<Artifact> exportedProtos = ProtoCommon.computeExportedProtos(ruleContext);
NestedSet<String> exportedProtoSourceRoots =
computeExportedProtoSourceRoots(ruleContext, protoSourceRoot);
computeExportedProtoSourceRoots(ruleContext, library.getSourceRoot());

Artifact directDescriptorSet =
ruleContext.getGenfilesArtifact(
Expand All @@ -251,8 +376,8 @@ public static ProtoInfo createProtoInfo(RuleContext ruleContext) {

ProtoInfo protoInfo =
ProtoInfo.create(
directProtoSources,
protoSourceRoot,
library.getSources(),
library.getSourceRoot(),
transitiveProtoSources,
transitiveProtoSourceRoots,
strictImportableProtosForDependents,
Expand Down
Loading

0 comments on commit 0b36536

Please sign in to comment.