Allow workspace-relative imports in .bazelrc

RELNOTES[NEW]: .bazelrc allows workspace-relative imports as "import %workspace%/path/to/rcfile"

--
MOS_MIGRATED_REVID=109237460
diff --git a/src/main/cpp/blaze_startup_options.cc b/src/main/cpp/blaze_startup_options.cc
index efceba3..641d6cd 100644
--- a/src/main/cpp/blaze_startup_options.cc
+++ b/src/main/cpp/blaze_startup_options.cc
@@ -173,6 +173,16 @@
   candidates->push_back("tools/bazel.rc");
 }
 
+bool BlazeStartupOptions::WorkspaceRelativizeRcFilePath(const string &workspace,
+                                                        string *path_fragment) {
+  // Strip off the "%workspace%/" prefix and prepend the true workspace path.
+  // In theory this could use alternate search paths for blazerc files.
+  path_fragment->assign(
+      blaze_util::JoinPath(workspace,
+                           path_fragment->substr(WorkspacePrefixLength)));
+  return true;
+}
+
 string BlazeStartupOptions::SystemWideRcPath() {
   return "/etc/bazel.bazelrc";
 }
diff --git a/src/main/cpp/blaze_startup_options.h b/src/main/cpp/blaze_startup_options.h
index e61af4b..ca52c27 100644
--- a/src/main/cpp/blaze_startup_options.h
+++ b/src/main/cpp/blaze_startup_options.h
@@ -190,9 +190,20 @@
   // Returns the path for the system-wide rc file.
   static string SystemWideRcPath();
 
-  // Returns the search paths for the RC file in the workspace.
+  // Returns the candidate pathnames for the RC file in the workspace,
+  // the first readable one of which will be chosen.
+  // It is ok if no usable candidate exists.
   static void WorkspaceRcFileSearchPath(std::vector<string>* candidates);
 
+  // Turn a %workspace%-relative import into its true name in the filesystem.
+  // path_fragment is modified in place.
+  // Unlike WorkspaceRcFileSearchPath, it is an error if no import file exists.
+  static bool WorkspaceRelativizeRcFilePath(const string &workspace,
+                                            string *path_fragment);
+
+  static constexpr char WorkspacePrefix[] = "%workspace%/";
+  static const int WorkspacePrefixLength = sizeof WorkspacePrefix - 1;
+
   // Returns the GetHostJavabase. This should be called after parsing
   // the --host_javabase option.
   string GetHostJavabase();
diff --git a/src/main/cpp/option_processor.cc b/src/main/cpp/option_processor.cc
index f08f4fe..209c9a0 100644
--- a/src/main/cpp/option_processor.cc
+++ b/src/main/cpp/option_processor.cc
@@ -36,6 +36,8 @@
 
 namespace blaze {
 
+constexpr char BlazeStartupOptions::WorkspacePrefix[];
+
 OptionProcessor::RcOption::RcOption(int rcfile_index, const string& option)
     : rcfile_index_(rcfile_index), option_(option) {
 }
@@ -45,16 +47,19 @@
 }
 
 blaze_exit_code::ExitCode OptionProcessor::RcFile::Parse(
+    const string& workspace,
     vector<RcFile*>* rcfiles,
     map<string, vector<RcOption> >* rcoptions,
     string* error) {
   list<string> initial_import_stack;
   initial_import_stack.push_back(filename_);
   return Parse(
-      filename_, index_, rcfiles, rcoptions, &initial_import_stack, error);
+      workspace, filename_, index_, rcfiles, rcoptions, &initial_import_stack,
+      error);
 }
 
 blaze_exit_code::ExitCode OptionProcessor::RcFile::Parse(
+    const string& workspace,
     const string& filename_ref,
     const int index,
     vector<RcFile*>* rcfiles,
@@ -101,13 +106,16 @@
     string command = words[0];
 
     if (command == "import") {
-      if (words.size() != 2) {
+      if (words.size() != 2
+          || (words[1].compare(0, BlazeStartupOptions::WorkspacePrefixLength,
+                               BlazeStartupOptions::WorkspacePrefix) == 0
+              && !BlazeStartupOptions::WorkspaceRelativizeRcFilePath(
+                  workspace, &words[1]))) {
         blaze_util::StringPrintf(error,
             "Invalid import declaration in .blazerc file '%s': '%s'",
             filename.c_str(), lines[line].c_str());
         return blaze_exit_code::BAD_ARGV;
       }
-
       if (std::find(import_stack->begin(), import_stack->end(), words[1]) !=
           import_stack->end()) {
         string loop;
@@ -123,8 +131,9 @@
       rcfiles->push_back(new RcFile(words[1], rcfiles->size()));
       import_stack->push_back(words[1]);
       blaze_exit_code::ExitCode parse_exit_code =
-        RcFile::Parse(rcfiles->back()->Filename(), rcfiles->back()->Index(),
-          rcfiles, rcoptions, import_stack, error);
+        RcFile::Parse(workspace, rcfiles->back()->Filename(),
+                      rcfiles->back()->Index(),
+                      rcfiles, rcoptions, import_stack, error);
       if (parse_exit_code != blaze_exit_code::SUCCESS) {
         return parse_exit_code;
       }
@@ -276,7 +285,7 @@
     if (!depot_blazerc_path.empty()) {
       blazercs_.push_back(new RcFile(depot_blazerc_path, blazercs_.size()));
       blaze_exit_code::ExitCode parse_exit_code =
-          blazercs_.back()->Parse(&blazercs_, &rcoptions_, error);
+          blazercs_.back()->Parse(workspace, &blazercs_, &rcoptions_, error);
       if (parse_exit_code != blaze_exit_code::SUCCESS) {
         return parse_exit_code;
       }
@@ -286,7 +295,7 @@
       blazercs_.push_back(new RcFile(alongside_binary_blazerc,
           blazercs_.size()));
       blaze_exit_code::ExitCode parse_exit_code =
-          blazercs_.back()->Parse(&blazercs_, &rcoptions_, error);
+          blazercs_.back()->Parse(workspace, &blazercs_, &rcoptions_, error);
       if (parse_exit_code != blaze_exit_code::SUCCESS) {
         return parse_exit_code;
       }
@@ -295,7 +304,7 @@
     if (!system_wide_blazerc.empty()) {
       blazercs_.push_back(new RcFile(system_wide_blazerc, blazercs_.size()));
       blaze_exit_code::ExitCode parse_exit_code =
-          blazercs_.back()->Parse(&blazercs_, &rcoptions_, error);
+          blazercs_.back()->Parse(workspace, &blazercs_, &rcoptions_, error);
       if (parse_exit_code != blaze_exit_code::SUCCESS) {
         return parse_exit_code;
       }
@@ -312,7 +321,7 @@
   if (!user_blazerc_path.empty()) {
     blazercs_.push_back(new RcFile(user_blazerc_path, blazercs_.size()));
     blaze_exit_code::ExitCode parse_exit_code =
-        blazercs_.back()->Parse(&blazercs_, &rcoptions_, error);
+        blazercs_.back()->Parse(workspace, &blazercs_, &rcoptions_, error);
     if (parse_exit_code != blaze_exit_code::SUCCESS) {
       return parse_exit_code;
     }
diff --git a/src/main/cpp/option_processor.h b/src/main/cpp/option_processor.h
index 7efea67..0888c8e 100644
--- a/src/main/cpp/option_processor.h
+++ b/src/main/cpp/option_processor.h
@@ -88,6 +88,7 @@
    public:
     RcFile(const string& filename, int index);
     blaze_exit_code::ExitCode Parse(
+        const string& workspace,
         std::vector<RcFile*>* rcfiles,
         std::map<string, std::vector<RcOption> >* rcoptions,
         string* error);
@@ -95,7 +96,8 @@
     const int Index() const { return index_; }
 
    private:
-    static blaze_exit_code::ExitCode Parse(const string& filename,
+    static blaze_exit_code::ExitCode Parse(const string& workspace,
+                                           const string& filename,
                                            const int index,
                                            std::vector<RcFile*>* rcfiles,
                                            std::map<string,