The gc Go compiler has a -importcfg option that takes a file that
provides a mapping from import paths to the files that satisfy those
imports.  This is used by the go build tool to let the compiler read
imported packages directly out of the build cache.  Without this
option the go build tool has to construct a tree of files to provide
the same mapping in the file system.

This patch to the Go frontend adds a -fgo-importcfg option that does
the same thing.  The go build tool already uses this option if it is
supported; with this patch, it is supported.

Bootstrapped and ran Go testsuite on x86_64-pc-linux-gnu.  Committed
to mainline.

Ian

* lang.opt (fgo-importcfg): New option.
* go-c.h (struct go_create_gogo_args): Add importcfg field.
* go-lang.cc (go_importcfg): New static variable.
(go_langhook_init): Set args.importcfg.
(go_langhook_handle_option): Handle -fgo-importcfg.
* gccgo.texi (Invoking gccgo): Document -fgo-importcfg.
cd4f91ed9786caf207d6d68bf2e64f986ed19735
diff --git a/gcc/go/gccgo.texi b/gcc/go/gccgo.texi
index 4ab1a76818f..90651af8384 100644
--- a/gcc/go/gccgo.texi
+++ b/gcc/go/gccgo.texi
@@ -271,6 +271,14 @@ pattern to a list of file names, and @code{Files} maps 
each file name
 to a full path to the file.  This option is intended for use by the
 @command{go} command to implement @code{//go:embed}.
 
+@cindex @option{-fgo-importcfg}
+@item -fgo-importcfg=@var{file}
+Identify a file that provides mappings for import package paths found
+in the Go source files.  The file can contain two commands:
+@code{importpath} to rename import paths for vendoring and
+@code{packagefile} to map from package path to files containing export
+data.  This option is intended for use by the @command{go} command.
+
 @cindex @option{-g for gccgo}
 @item -g
 This is the standard @command{gcc} option (@pxref{Debugging Options, ,
diff --git a/gcc/go/go-c.h b/gcc/go/go-c.h
index c6050382aa8..6a2b57b3b44 100644
--- a/gcc/go/go-c.h
+++ b/gcc/go/go-c.h
@@ -41,6 +41,7 @@ struct go_create_gogo_args
   const char* prefix;
   const char* relative_import_path;
   const char* c_header;
+  const char* importcfg;
   const char* embedcfg;
   Backend* backend;
   Linemap* linemap;
diff --git a/gcc/go/go-lang.cc b/gcc/go/go-lang.cc
index c6c147b20a5..e85a4bfe949 100644
--- a/gcc/go/go-lang.cc
+++ b/gcc/go/go-lang.cc
@@ -90,6 +90,7 @@ static const char *go_prefix = NULL;
 static const char *go_relative_import_path = NULL;
 static const char *go_c_header = NULL;
 static const char *go_embedcfg = NULL;
+static const char *go_importcfg = NULL;
 
 /* Language hooks.  */
 
@@ -111,6 +112,7 @@ go_langhook_init (void)
   args.relative_import_path = go_relative_import_path;
   args.c_header = go_c_header;
   args.embedcfg = go_embedcfg;
+  args.importcfg = go_importcfg;
   args.check_divide_by_zero = go_check_divide_zero;
   args.check_divide_overflow = go_check_divide_overflow;
   args.compiling_runtime = go_compiling_runtime;
@@ -286,6 +288,10 @@ go_langhook_handle_option (
       go_embedcfg = arg;
       break;
 
+    case OPT_fgo_importcfg_:
+      go_importcfg = arg;
+      break;
+
     default:
       /* Just return 1 to indicate that the option is valid.  */
       break;
diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE
index ff07b1a1fa6..c44cdc2baac 100644
--- a/gcc/go/gofrontend/MERGE
+++ b/gcc/go/gofrontend/MERGE
@@ -1,4 +1,4 @@
-68a756b6aadc901534cfddddad2b1e73fae9e34f
+92152c88ea8e2dd9e8c67e91bf4ae5e3edf1b506
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
diff --git a/gcc/go/gofrontend/embed.cc b/gcc/go/gofrontend/embed.cc
index 0584f707ce6..6dada5efc2a 100644
--- a/gcc/go/gofrontend/embed.cc
+++ b/gcc/go/gofrontend/embed.cc
@@ -19,8 +19,8 @@
 
 // Read a file into *DATA.  Returns false on error.
 
-static bool
-read_file(const char* filename, Location loc, std::string* data)
+bool
+Gogo::read_file(const char* filename, Location loc, std::string* data)
 {
   int fd = open(filename, O_RDONLY | O_BINARY);
   if (fd < 0)
@@ -346,7 +346,8 @@ Gogo::read_embedcfg(const char *filename)
 bool
 Embedcfg_reader::initialize_from_file()
 {
-  if (!read_file(this->filename_, Linemap::unknown_location(), &this->data_))
+  if (!Gogo::read_file(this->filename_, Linemap::unknown_location(),
+                      &this->data_))
     return false;
   if (this->data_.empty())
     {
@@ -849,7 +850,7 @@ Gogo::initializer_for_embeds(Type* type,
        }
 
       std::string data;
-      if (!read_file(this->embed_files_[paths[0]].c_str(), loc, &data))
+      if (!Gogo::read_file(this->embed_files_[paths[0]].c_str(), loc, &data))
        return Expression::make_error(loc);
 
       Expression* e = Expression::make_string(data, loc);
@@ -909,7 +910,7 @@ Gogo::initializer_for_embeds(Type* type,
       std::string data;
       if ((*pp)[pp->size() - 1] != '/')
        {
-         if (!read_file(this->embed_files_[*pp].c_str(), loc, &data))
+         if (!Gogo::read_file(this->embed_files_[*pp].c_str(), loc, &data))
            return Expression::make_error(loc);
        }
 
diff --git a/gcc/go/gofrontend/go.cc b/gcc/go/gofrontend/go.cc
index 1512770af29..66d481681f3 100644
--- a/gcc/go/gofrontend/go.cc
+++ b/gcc/go/gofrontend/go.cc
@@ -40,6 +40,8 @@ go_create_gogo(const struct go_create_gogo_args* args)
     ::gogo->set_compiling_runtime(args->compiling_runtime);
   if (args->c_header != NULL)
     ::gogo->set_c_header(args->c_header);
+  if (args->importcfg != NULL)
+    ::gogo->read_importcfg(args->importcfg);
   if (args->embedcfg != NULL)
     ::gogo->read_embedcfg(args->embedcfg);
   ::gogo->set_debug_escape_level(args->debug_escape_level);
diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc
index 980db1ea07e..fa3cd6ebe9f 100644
--- a/gcc/go/gofrontend/gogo.cc
+++ b/gcc/go/gofrontend/gogo.cc
@@ -52,6 +52,10 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int 
pointer_size)
     prefix_from_option_(false),
     relative_import_path_(),
     c_header_(),
+    import_map_(),
+    package_file_(),
+    embed_patterns_(),
+    embed_files_(),
     check_divide_by_zero_(true),
     check_divide_overflow_(true),
     compiling_runtime_(false),
@@ -517,7 +521,20 @@ Gogo::import_package(const std::string& filename,
       return;
     }
 
-  Import::Stream* stream = Import::open_package(filename, location,
+  // If we are using an importcfg file we have to check two mappings.
+  // IMPORT_MAP_ is a mapping from package path to real package path,
+  // for vendoring.  PACKAGE_FILE_ is a mapping from package path to
+  // file name, to find the file in the build cache.
+  std::string path = filename;
+  Unordered_map(std::string, std::string)::const_iterator pi;
+  pi = this->import_map_.find(filename);
+  if (pi != this->import_map_.end())
+    path = pi->second;
+  pi = this->package_file_.find(path);
+  if (pi != this->package_file_.end())
+    path = pi->second;
+
+  Import::Stream* stream = Import::open_package(path, location,
                                                this->relative_import_path_);
   if (stream == NULL)
     {
diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h
index c08a16b74c2..4fd45bfa040 100644
--- a/gcc/go/gofrontend/gogo.h
+++ b/gcc/go/gofrontend/gogo.h
@@ -393,6 +393,10 @@ class Gogo
   set_c_header(const std::string& s)
   { this->c_header_ = s; }
 
+  // Read an importcfg file.
+  void
+  read_importcfg(const char* filename);
+
   // Read an embedcfg file.
   void
   read_embedcfg(const char* filename);
@@ -1126,6 +1130,10 @@ class Gogo
   static size_t
   special_name_pos(const std::string& name);
 
+  // Read a file into memory.
+  static bool
+  read_file(const char* filename, Location loc, std::string* data);
+
  private:
   // During parsing, we keep a stack of functions.  Each function on
   // the stack is one that we are currently parsing.  For each
@@ -1295,6 +1303,10 @@ class Gogo
   std::string relative_import_path_;
   // The C header file to write, from the -fgo-c-header option.
   std::string c_header_;
+  // Mapping from imports in the source file to the real import paths.
+  Unordered_map(std::string, std::string) import_map_;
+  // Mapping from import paths to files to read.
+  Unordered_map(std::string, std::string) package_file_;
   // Patterns from an embedcfg file.
   Embed_patterns embed_patterns_;
   // Mapping from file to full path from an embedcfg file.
diff --git a/gcc/go/gofrontend/import.cc b/gcc/go/gofrontend/import.cc
index 6a5491be949..21691fa5ff4 100644
--- a/gcc/go/gofrontend/import.cc
+++ b/gcc/go/gofrontend/import.cc
@@ -34,6 +34,130 @@ go_add_search_path(const char* path)
   search_path.push_back(std::string(path));
 }
 
+// Read an importcfg file.
+
+void
+Gogo::read_importcfg(const char* filename)
+{
+  std::string data;
+  if (!Gogo::read_file(filename, Linemap::unknown_location(), &data))
+    return;
+  const char* p = data.data();
+  const char* pend = p + data.length();
+  int lineno = 0;
+  const char *pnext = NULL;
+  for (; p < pend; p = pnext)
+    {
+      // Line numbers start at 1.
+      lineno++;
+
+      // Find end of line.
+      const char* pnl = static_cast<const char*>(memchr(p, '\n', pend - p));
+      if (pnl != NULL)
+       pnext = pnl + 1;
+      else
+       {
+         pnl = pend;
+         pnext = pnl;
+       }
+
+      // Trim leading spaces.
+      while (p < pnl)
+       {
+         unsigned int rune;
+         int rune_len = Lex::fetch_char(p, &rune);
+         if (rune_len == 0)
+           {
+             go_error_at(Linemap::unknown_location(),
+                         "%s:%d: invalid character in importcfg file",
+                         filename, lineno);
+             return;
+           }
+         if (!Lex::is_unicode_space(rune))
+           break;
+         p += rune_len;
+       }
+
+      // Trim trailing spaces.
+      while (pnl > p)
+       {
+         size_t start = pnl - p - 1;
+         unsigned int rune = (unsigned char)p[start];
+         int rune_len = 1;
+         if (rune > 0x7f)
+           {
+             for (start--; start > 0; start--)
+               {
+                 unsigned char c = p[start];
+                 if ((c & 0xc0) != 0x80)
+                   break;
+               }
+             rune_len = Lex::fetch_char(p + start, &rune);
+             if (static_cast<size_t>(rune_len) != (pnl - p) - start)
+               {
+                 go_error_at(Linemap::unknown_location(),
+                             "%s:%d: invalid character in importcfg file",
+                             filename, lineno);
+                 return;
+               }
+           }
+         if (!Lex::is_unicode_space(rune))
+           break;
+         pnl -= rune_len;
+       }
+
+      // Skip empty lines and comment lines.
+      if (p == pnl || *p == '#')
+       continue;
+
+      size_t verb_len;
+      const char* psp = static_cast<const char*>(memchr(p, ' ', pnl - p));
+      if (psp == NULL)
+       verb_len = pnl - p;
+      else
+       verb_len = psp - p;
+
+      bool importmap = false;
+      bool packagefile = false;
+      if (strncmp(p, "importmap", verb_len) == 0)
+       importmap = true;
+      else if (strncmp(p, "packagefile", verb_len) == 0)
+       packagefile = true;
+      else
+       {
+         go_error_at(Linemap::unknown_location(),
+                     "%s:%d: unknown directive in importcfg file",
+                     filename, lineno);
+         return;
+       }
+
+      const char* peq;
+      if (psp == NULL)
+       peq = NULL;
+      else
+       {
+         psp++;
+         peq = static_cast<const char*>(memchr(psp, '=', pnl - psp));
+       }
+      if (peq == NULL || peq + 1 == pnl)
+       {
+         go_error_at(Linemap::unknown_location(),
+                     "%s:%d: invalid syntax in importcfg file",
+                     filename, lineno);
+         return;
+       }
+
+      std::string first(psp, peq - psp);
+      std::string second(peq + 1, pnl - (peq + 1));
+      if (importmap)
+       this->import_map_[first] = second;
+      else if (packagefile)
+       this->package_file_[first] = second;
+      else
+       go_unreachable();
+    }
+}
+
 // Find import data.  This searches the file system for FILENAME and
 // returns a pointer to a Stream object to read the data that it
 // exports.  If the file is not found, it returns NULL.
diff --git a/gcc/go/lang.opt b/gcc/go/lang.opt
index 4ca989c62e0..0d658fcbfbf 100644
--- a/gcc/go/lang.opt
+++ b/gcc/go/lang.opt
@@ -61,6 +61,10 @@ fgo-embedcfg=
 Go Joined RejectNegative
 -fgo-embedcfg=<file>   List embedded files via go:embed.
 
+fgo-importcfg=
+Go Joined RejectNegative
+-fgo-importcfg=<file>  Provide file that tells where to find imports.
+
 fgo-optimize-
 Go Joined
 -fgo-optimize-<type>   Turn on optimization passes in the frontend.

Reply via email to