Fix skydoc following of nontrivial relative labels.

Previously, only trivial relative paths (within the same package) were handled correctly. Now paths such as ":foo/bar.bzl" are handled appropriately.

RELNOTES: None.
PiperOrigin-RevId: 206237161
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 01fff20..2a0bf3d 100644
--- a/src/main/java/com/google/devtools/build/skydoc/SkydocMain.java
+++ b/src/main/java/com/google/devtools/build/skydoc/SkydocMain.java
@@ -18,6 +18,8 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
 import com.google.devtools.build.lib.events.EventHandler;
 import com.google.devtools.build.lib.skylarkbuildapi.TopLevelBootstrap;
 import com.google.devtools.build.lib.skylarkbuildapi.android.AndroidBootstrap;
@@ -38,7 +40,6 @@
 import com.google.devtools.build.lib.syntax.ParserInputSource;
 import com.google.devtools.build.lib.syntax.Runtime;
 import com.google.devtools.build.lib.syntax.SkylarkImport;
-import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.devtools.build.skydoc.fakebuildapi.FakeActionsInfoProvider;
 import com.google.devtools.build.skydoc.fakebuildapi.FakeBuildApiGlobals;
 import com.google.devtools.build.skydoc.fakebuildapi.FakeDefaultInfoProvider;
@@ -67,6 +68,7 @@
 import com.google.devtools.build.skydoc.rendering.RuleInfo;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.nio.file.NoSuchFileException;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
@@ -88,7 +90,7 @@
  *
  * <p>Usage:</p>
  * <pre>
- *   skydoc {target_skylark_file} {output_file} [symbol_name]...
+ *   skydoc {target_skylark_file_label} {output_file} [symbol_name]...
  * </pre>
  * <p>
  *   Generates documentation for all exported symbols of the target skylark file that are
@@ -107,22 +109,25 @@
     this.fileAccessor = fileAccessor;
   }
 
-  public static void main(String[] args) throws IOException, InterruptedException {
+  public static void main(String[] args)
+      throws IOException, InterruptedException, LabelSyntaxException {
     if (args.length < 2) {
       throw new IllegalArgumentException("Expected two or more arguments. Usage:\n"
-          + "{skydoc_bin} {target_skylark_file} {output_file} [symbol_names]...");
+          + "{skydoc_bin} {target_skylark_file_label} {output_file} [symbol_names]...");
     }
 
-    String bzlPath = args[0];
+    String targetFileLabelString = args[0];
     String outputPath = args[1];
-    ImmutableSet<String> symbolNames = getSymbolNames(args);
 
-    Path path = Paths.get(bzlPath);
+    Label targetFileLabel =
+        Label.parseAbsolute(targetFileLabelString, ImmutableMap.of());
+    ImmutableSet<String> symbolNames = getSymbolNames(args);
 
     ImmutableMap.Builder<String, RuleInfo> ruleInfoMap = ImmutableMap.builder();
     ImmutableList.Builder<RuleInfo> unknownNamedRules = ImmutableList.builder();
 
-    new SkydocMain(new FilesystemFileAccessor()).eval(path, ruleInfoMap, unknownNamedRules);
+    new SkydocMain(new FilesystemFileAccessor())
+        .eval(targetFileLabel, ruleInfoMap, unknownNamedRules);
 
     MarkdownRenderer renderer = new MarkdownRenderer();
 
@@ -175,7 +180,7 @@
    * using a fake build API and collects information about all rule definitions made in the
    * root skylark file.
    *
-   * @param path the path of the skylark file to evaluate
+   * @param label the label of the skylark file to evaluate
    * @param ruleInfoMap a map builder to be populated with rule definition information for
    *     named rules. Keys are exported names of rules, and values are their {@link RuleInfo}
    *     rule descriptions. For example, 'my_rule = rule(...)' has key 'my_rule'
@@ -184,16 +189,13 @@
    * @throws InterruptedException if evaluation is interrupted
    */
   public Environment eval(
-      Path path,
+      Label label,
       ImmutableMap.Builder<String, RuleInfo> ruleInfoMap,
       ImmutableList.Builder<RuleInfo> unknownNamedRules)
-      throws InterruptedException, IOException {
-    if (path.isAbsolute()) {
-      path = Paths.get(PathFragment.create(path.toString()).toRelative().getPathString());
-    }
+      throws InterruptedException, IOException, LabelSyntaxException {
 
     List<RuleInfo> ruleInfoList = new ArrayList<>();
-    Environment env = recursiveEval(path, ruleInfoList);
+    Environment env = recursiveEval(label, ruleInfoList);
 
     Map<BaseFunction, RuleInfo> ruleFunctions = ruleInfoList.stream()
         .collect(Collectors.toMap(
@@ -220,14 +222,15 @@
    * dependencies using a fake build API and collects information about all rule definitions made
    * in those files.
    *
-   * @param path the path of the skylark file to evaluate
+   * @param label the label of the skylark file to evaluate
    * @param ruleInfoList a collection of all rule definitions made so far (using rule()); this
    *     method will add to this list as it evaluates additional files
    * @throws InterruptedException if evaluation is interrupted
    */
   private Environment recursiveEval(
-      Path path, List<RuleInfo> ruleInfoList)
-      throws InterruptedException, IOException {
+      Label label, List<RuleInfo> ruleInfoList)
+      throws InterruptedException, IOException, LabelSyntaxException {
+    Path path = Paths.get(label.toPathFragment().toString());
     if (pending.contains(path)) {
       throw new IllegalStateException("cycle with " + path);
     } else if (loaded.containsKey(path)) {
@@ -240,11 +243,17 @@
 
     Map<String, Extension> imports = new HashMap<>();
     for (SkylarkImport anImport : buildFileAST.getImports()) {
-      Path importPath = fromPathFragment(path, anImport.asPathFragment());
+      Label relativeLabel = label.getRelative(anImport.getImportString());
+      Path importPath = Paths.get(relativeLabel.toPathFragment().toString());
 
-      Environment importEnv = recursiveEval(importPath, ruleInfoList);
-
-      imports.put(anImport.getImportString(), new Extension(importEnv));
+      try {
+        Environment importEnv = recursiveEval(relativeLabel, ruleInfoList);
+        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(), importPath));
+      }
     }
 
     Environment env = evalSkylarkBody(buildFileAST, imports, ruleInfoList);
@@ -255,12 +264,6 @@
     return env;
   }
 
-  private static Path fromPathFragment(Path fromPath, PathFragment pathFragment) {
-    return pathFragment.isAbsolute()
-        ? Paths.get(pathFragment.toRelative().getPathString())
-        : fromPath.resolveSibling(pathFragment.getPathString());
-  }
-
   /**
    * Evaluates the AST from a single skylark file, given the already-resolved imports.
    */