Introduce --dep_roots flag to Stardoc to search alternate roots for bzl deps
This is necessary to handle generated bzl input files.
See https://github.com/bazelbuild/skydoc/issues/151 for details.
RELNOTES: None.
PiperOrigin-RevId: 233481475
diff --git a/src/main/java/com/google/devtools/build/skydoc/FilesystemFileAccessor.java b/src/main/java/com/google/devtools/build/skydoc/FilesystemFileAccessor.java
index e9ad5f3..2b1bd91 100644
--- a/src/main/java/com/google/devtools/build/skydoc/FilesystemFileAccessor.java
+++ b/src/main/java/com/google/devtools/build/skydoc/FilesystemFileAccessor.java
@@ -30,4 +30,9 @@
byte[] content = Files.readAllBytes(Paths.get(pathString));
return ParserInputSource.create(content, PathFragment.create(pathString));
}
+
+ @Override
+ public boolean fileExists(String pathString) {
+ return Files.exists(Paths.get(pathString));
+ }
}
diff --git a/src/main/java/com/google/devtools/build/skydoc/SkydocMain.java b/src/main/java/com/google/devtools/build/skydoc/SkydocMain.java
index fd256a7..20d4f52 100644
--- a/src/main/java/com/google/devtools/build/skydoc/SkydocMain.java
+++ b/src/main/java/com/google/devtools/build/skydoc/SkydocMain.java
@@ -142,9 +142,17 @@
private final LinkedHashSet<Path> pending = new LinkedHashSet<>();
private final Map<Path, Environment> loaded = new HashMap<>();
private final SkylarkFileAccessor fileAccessor;
+ private final List<String> depRoots;
- public SkydocMain(SkylarkFileAccessor fileAccessor) {
+ public SkydocMain(SkylarkFileAccessor fileAccessor, List<String> depRoots) {
this.fileAccessor = fileAccessor;
+ if (depRoots.isEmpty()) {
+ // For backwards compatibility, if no dep_roots are specified, use the current
+ // directory as the only root.
+ this.depRoots = ImmutableList.of(".");
+ } else {
+ this.depRoots = depRoots;
+ }
}
public static void main(String[] args)
@@ -158,6 +166,7 @@
String targetFileLabelString;
String outputPath;
ImmutableSet<String> symbolNames;
+ ImmutableList<String> depRoots;
// TODO(cparsons): Remove optional positional arg parsing.
List<String> residualArgs = parser.getResidue();
@@ -172,10 +181,12 @@
targetFileLabelString = residualArgs.get(0);
outputPath = residualArgs.get(1);
symbolNames = getSymbolNames(residualArgs);
+ depRoots = ImmutableList.of();
} else {
targetFileLabelString = skydocOptions.targetFileLabel;
outputPath = skydocOptions.outputFilePath;
symbolNames = ImmutableSet.copyOf(skydocOptions.symbolNames);
+ depRoots = ImmutableList.copyOf(skydocOptions.depRoots);
}
Label targetFileLabel =
@@ -186,7 +197,7 @@
ImmutableList.Builder<RuleInfo> unknownNamedRules = ImmutableList.builder();
ImmutableMap.Builder<String, UserDefinedFunction> userDefinedFunctions = ImmutableMap.builder();
- new SkydocMain(new FilesystemFileAccessor())
+ new SkydocMain(new FilesystemFileAccessor(), depRoots)
.eval(
semanticsOptions.toSkylarkSemantics(),
targetFileLabel,
@@ -387,7 +398,7 @@
}
pending.add(path);
- ParserInputSource parserInputSource = fileAccessor.inputSource(path.toString());
+ ParserInputSource parserInputSource = getInputSource(path.toString());
BuildFileAST buildFileAST = BuildFileAST.parseSkylarkFile(parserInputSource, eventHandler);
Map<String, Extension> imports = new HashMap<>();
@@ -403,8 +414,9 @@
imports.put(anImport.getImportString(), new Extension(importEnv));
} catch (NoSuchFileException noSuchFileException) {
throw new IllegalStateException(
- String.format("File %s imported '%s', yet %s was not found.",
- path, anImport.getImportString(), pathOfLabel(relativeLabel)));
+ String.format(
+ "File %s imported '%s', yet %s was not found, even at roots %s.",
+ path, anImport.getImportString(), pathOfLabel(relativeLabel), depRoots));
}
}
@@ -425,6 +437,17 @@
return Paths.get(workspacePrefix + label.toPathFragment());
}
+ private ParserInputSource getInputSource(String bzlWorkspacePath) throws IOException {
+ for (String rootPath : depRoots) {
+ if (fileAccessor.fileExists(rootPath + "/" + bzlWorkspacePath)) {
+ return fileAccessor.inputSource(rootPath + "/" + bzlWorkspacePath);
+ }
+ }
+
+ // All depRoots attempted and no valid file was found.
+ throw new NoSuchFileException(bzlWorkspacePath);
+ }
+
/** Evaluates the AST from a single skylark file, given the already-resolved imports. */
private Environment evalSkylarkBody(
SkylarkSemantics semantics,
diff --git a/src/main/java/com/google/devtools/build/skydoc/SkydocOptions.java b/src/main/java/com/google/devtools/build/skydoc/SkydocOptions.java
index aa561f5..1a45913 100644
--- a/src/main/java/com/google/devtools/build/skydoc/SkydocOptions.java
+++ b/src/main/java/com/google/devtools/build/skydoc/SkydocOptions.java
@@ -47,4 +47,13 @@
effectTags = OptionEffectTag.UNKNOWN,
help = "The path of the file to output documentation into")
public List<String> symbolNames;
+
+ @Option(
+ name = "dep_roots",
+ allowMultiple = true,
+ defaultValue = "",
+ documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
+ effectTags = OptionEffectTag.UNKNOWN,
+ help = "File path roots to search when resolving transitive bzl dependencies")
+ public List<String> depRoots;
}
diff --git a/src/main/java/com/google/devtools/build/skydoc/SkylarkFileAccessor.java b/src/main/java/com/google/devtools/build/skydoc/SkylarkFileAccessor.java
index 9abe532..d4039c4 100644
--- a/src/main/java/com/google/devtools/build/skydoc/SkylarkFileAccessor.java
+++ b/src/main/java/com/google/devtools/build/skydoc/SkylarkFileAccessor.java
@@ -28,4 +28,7 @@
* string.
*/
ParserInputSource inputSource(String pathString) throws IOException;
+
+ /** Returns true if a file exists at the current path. */
+ boolean fileExists(String pathString);
}
diff --git a/src/test/java/com/google/devtools/build/skydoc/SkydocTest.java b/src/test/java/com/google/devtools/build/skydoc/SkydocTest.java
index 5c51834..5ab2830 100644
--- a/src/test/java/com/google/devtools/build/skydoc/SkydocTest.java
+++ b/src/test/java/com/google/devtools/build/skydoc/SkydocTest.java
@@ -50,15 +50,23 @@
@Before
public void setUp() {
- skydocMain = new SkydocMain(new SkylarkFileAccessor() {
+ skydocMain =
+ new SkydocMain(
+ new SkylarkFileAccessor() {
- @Override
- public ParserInputSource inputSource(String pathString) throws IOException {
- Path path = fileSystem.getPath("/" + pathString);
- byte[] bytes = FileSystemUtils.asByteSource(path).read();
- return ParserInputSource.create(bytes, path.asFragment());
- }
- });
+ @Override
+ public ParserInputSource inputSource(String pathString) throws IOException {
+ Path path = fileSystem.getPath("/" + pathString);
+ byte[] bytes = FileSystemUtils.asByteSource(path).read();
+ return ParserInputSource.create(bytes, path.asFragment());
+ }
+
+ @Override
+ public boolean fileExists(String pathString) {
+ return fileSystem.exists(fileSystem.getPath("/" + pathString));
+ }
+ },
+ ImmutableList.of("/other_root", "."));
}
@Test
@@ -169,14 +177,12 @@
"def rule_impl(ctx):",
" return struct()");
- scratch.file(
- "/deps/foo/docstring.bzl",
- "doc_string = 'Dep rule'");
+ scratch.file("/other_root/deps/foo/other_root.bzl", "doc_string = 'Dep rule'");
scratch.file(
"/deps/foo/dep_rule.bzl",
"load('//lib:rule_impl.bzl', 'rule_impl')",
- "load(':docstring.bzl', 'doc_string')",
+ "load(':other_root.bzl', 'doc_string')",
"",
"_hidden_rule = rule(",
" doc = doc_string,",