diff mbox series

c++: module mapper

Message ID 1c91ef3b-2365-d955-3a7b-5a398f5ffb1b@acm.org
State New
Headers show
Series c++: module mapper | expand

Commit Message

Nathan Sidwell Dec. 15, 2020, 3:47 p.m. UTC
To avoid always requiring an active mapper to connect to, we provide a 
default in-process mapper with similar functionality to the sample 
server.  This is that code.  Also included is the client-side connection 
mechanism, which determines what server to use and how to
     connect to it.

             gcc/cp/
             * Make-lang.in (CXX_AND_OBJCXX_OBJS): Add mapper-client &
             mapper-resolver.
             * mapper-client.h: New.
             * mapper-client.cc: New.
             * mapper-resolver.cc: New.

pushing to trunk
diff mbox series

Patch

diff --git c/gcc/cp/Make-lang.in w/gcc/cp/Make-lang.in
index 52116652900..49272464409 100644
--- c/gcc/cp/Make-lang.in
+++ w/gcc/cp/Make-lang.in
@@ -94,7 +94,8 @@  CXX_AND_OBJCXX_OBJS = \
 	cp/error.o cp/except.o cp/expr.o \
 	cp/friend.o cp/init.o \
 	cp/lambda.o cp/lex.o cp/logic.o \
-	cp/mangle.o cp/method.o cp/module.o \
+	cp/mangle.o cp/mapper-client.o cp/mapper-resolver.o \
+	cp/method.o cp/module.o \
 	cp/name-lookup.o cp/optimize.o \
 	cp/parser.o cp/pt.o cp/ptree.o \
 	cp/rtti.o \
diff --git c/gcc/cp/mapper-client.cc w/gcc/cp/mapper-client.cc
new file mode 100644
index 00000000000..acec591296a
--- /dev/null
+++ w/gcc/cp/mapper-client.cc
@@ -0,0 +1,356 @@ 
+/* C++ modules.  Experimental!
+   Copyright (C) 2017-2020 Free Software Foundation, Inc.
+   Written by Nathan Sidwell <nathan@acm.org> while at FaceBook
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   GCC is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+
+#include "line-map.h"
+#include "diagnostic-core.h"
+#include "mapper-client.h"
+#include "intl.h"
+
+#include "../../c++tools/resolver.h"
+
+module_client::module_client (pex_obj *p, int fd_from, int fd_to)
+  : Client (fd_from, fd_to), pex (p)
+{
+}
+
+static module_client *
+spawn_mapper_program (char const **errmsg, std::string &name,
+		      char const *full_program_name)
+{
+  /* Split writable at white-space.  No space-containing args for
+     you!  */
+  // At most every other char could be an argument
+  char **argv = new char *[name.size () / 2 + 2];
+  unsigned arg_no = 0;
+  char *str = new char[name.size ()];
+  memcpy (str, name.c_str () + 1, name.size ());
+
+  for (auto ptr = str; ; ++ptr)
+    {
+      while (*ptr == ' ')
+	ptr++;
+      if (!*ptr)
+	break;
+
+      if (!arg_no)
+	{
+	  /* @name means look in the compiler's install dir.  */
+	  if (ptr[0] == '@')
+	    ptr++;
+	  else
+	    full_program_name = nullptr;
+	}
+
+      argv[arg_no++] = ptr;
+      while (*ptr && *ptr != ' ')
+	ptr++;
+      if (!*ptr)
+	break;
+      *ptr = 0;
+    }
+  argv[arg_no] = nullptr;
+
+  auto *pex = pex_init (PEX_USE_PIPES, progname, NULL);
+  FILE *to = pex_input_pipe (pex, false);
+  name = argv[0];
+  if (!to)
+    *errmsg = "connecting input";
+  else
+    {
+      int flags = PEX_SEARCH;
+
+      if (full_program_name)
+	{
+	  /* Prepend the invoking path, if the mapper is a simple
+	     file name.  */
+	  size_t dir_len = progname - full_program_name;
+	  std::string argv0;
+	  argv0.reserve (dir_len + name.size ());
+	  argv0.append (full_program_name, dir_len).append (name);
+	  name = std::move (argv0);
+	  argv[0] = const_cast <char *> (name.c_str ());
+	  flags = 0;
+	}
+      int err;
+      *errmsg = pex_run (pex, flags, argv[0], argv, NULL, NULL, &err);
+    }
+  delete[] str;
+  delete[] argv;
+
+  int fd_from = -1, fd_to = -1;
+  if (!*errmsg)
+    {
+      FILE *from = pex_read_output (pex, false);
+      if (from && (fd_to = dup (fileno (to))) >= 0)
+	fd_from = fileno (from);
+      else
+	*errmsg = "connecting output";
+      fclose (to);
+    }
+
+  if (*errmsg)
+    {
+      pex_free (pex);
+      return nullptr;
+    }
+
+  return new module_client (pex, fd_from, fd_to);
+}
+
+module_client *
+module_client::open_module_client (location_t loc, const char *o,
+				   void (*set_repo) (const char *),
+				   char const *full_program_name)
+{
+  module_client *c = nullptr;
+  std::string ident;
+  std::string name;
+  char const *errmsg = nullptr;
+  unsigned line = 0;
+
+  if (o && o[0])
+    {
+      /* Maybe a local or ipv6 address.  */
+      name = o;
+      auto last = name.find_last_of ('?');
+      if (last != name.npos)
+	{
+	  ident = name.substr (last + 1);
+	  name.erase (last);
+	}
+
+      if (name.size ())
+	{
+	  switch (name[0])
+	    {
+	    case '<':
+	      // <from>to or <>fromto, or <>
+	      {
+		size_t pos = name.find ('>', 1);
+		if (pos == std::string::npos)
+		  pos = name.size ();
+		std::string from (name, 1, pos - 1);
+		std::string to;
+		if (pos != name.size ())
+		  to.append (name, pos + 1, std::string::npos);
+
+		int fd_from = -1, fd_to = -1;
+		if (from.empty () && to.empty ())
+		  {
+		    fd_from = fileno (stdin);
+		    fd_to = fileno (stdout);
+		  }
+		else
+		  {
+		    if (!from.empty ())
+		      {
+			fd_from = std::stoul (from, &pos, 10);
+			if (pos != from.size ())
+			  {
+			    int dir = to.empty ()
+			      ? O_RDWR | O_CLOEXEC : O_RDONLY | O_CLOEXEC;
+			    fd_from = open (from.c_str (), dir);
+			  }
+			if (to.empty ())
+			  fd_to = fd_from;
+		      }
+
+		    if (!from.empty () && fd_from < 0)
+		      ;
+		    else if (to.empty ())
+		      ;
+		    else
+		      {
+			fd_to = std::stoul (to, &pos, 10);
+			if (pos != to.size ())
+			  {
+			    int dir = from.empty ()
+			      ? O_RDWR | O_CLOEXEC : O_WRONLY | O_CLOEXEC;
+			    fd_to = open (to.c_str (), dir);
+			    if (fd_to < 0)
+			      close (fd_from);
+			  }
+			if (from.empty ())
+			  fd_from = fd_to;
+		      }
+		  }
+
+		if (fd_from < 0 || fd_to < 0)
+		  errmsg = "opening";
+		else
+		  c = new module_client (fd_from, fd_to);
+	      }
+	      break;
+
+	    case '=':
+	      // =localsocket
+	      {
+		int fd = -1;
+#if CODY_NETWORKING
+		fd = Cody::OpenLocal (&errmsg, name.c_str () + 1);
+#endif
+		if (fd >= 0)
+		  c = new module_client (fd, fd);
+	      }
+	      break;
+
+	    case '|':
+	      // |program and args
+	      c = spawn_mapper_program (&errmsg, name, full_program_name);
+	      break;
+
+	    default:
+	      // file or hostname:port
+	      {
+		auto colon = name.find_last_of (':');
+		if (colon != name.npos)
+		  {
+		    char const *cptr = name.c_str () + colon;
+		    char *endp;
+		    unsigned port = strtoul (cptr + 1, &endp, 10);
+
+		    if (port && endp != cptr + 1 && !*endp)
+		      {
+			name[colon] = 0;
+			int fd = 01;
+#if CODY_NETWORKING
+			fd = Cody::OpenInet6 (&errmsg, name.c_str (), port);
+#endif
+			name[colon] = ':';
+
+			if (fd >= 0)
+			  c = new module_client (fd, fd);
+		      }
+		  }
+		
+	      }
+	      break;
+	    }
+	}
+    }
+
+  if (!c)
+    {
+      // Make a default in-process client
+      bool file = !errmsg && !name.empty ();
+      auto r = new module_resolver (!file, true);
+
+      if (file)
+	{
+	int fd = open (name.c_str (), O_RDONLY | O_CLOEXEC);
+	if (fd < 0)
+	  errmsg = "opening";
+	else
+	  {
+	    if (int l = r->read_tuple_file (fd, ident, false))
+	      {
+		if (l > 0)
+		  line = l;
+		errmsg = "reading";
+	      }
+	      
+	    close (fd);
+	  }
+	}
+      else
+	r->set_repo ("gcm.cache");
+
+      auto *s = new Cody::Server (r);
+      c = new module_client (s);
+    }
+
+#ifdef SIGPIPE
+  if (!c->IsDirect ())
+    /* We need to ignore sig pipe for a while.  */
+    c->sigpipe = signal (SIGPIPE, SIG_IGN);
+#endif
+
+  if (errmsg)
+    error_at (loc, line ? G_("failed %s mapper %qs line %u")
+	      : G_("failed %s mapper %qs"), errmsg, name.c_str (), line);
+
+  // now wave hello!
+  c->Cork ();
+  c->Connect (std::string ("GCC"), ident);
+  c->ModuleRepo ();
+  auto packets = c->Uncork ();
+
+  auto &connect = packets[0];
+  if (connect.GetCode () == Cody::Client::PC_CONNECT)
+    c->flags = Cody::Flags (connect.GetInteger ());
+  else if (connect.GetCode () == Cody::Client::PC_ERROR)
+    error_at (loc, "failed mapper handshake %s", connect.GetString ().c_str ());
+
+  auto &repo = packets[1];
+  if (repo.GetCode () == Cody::Client::PC_PATHNAME)
+    set_repo (repo.GetString ().c_str ());
+
+  return c;
+}
+
+void
+module_client::close_module_client (location_t loc, module_client *mapper)
+{
+  if (mapper->IsDirect ())
+    {
+      auto *s = mapper->GetServer ();
+      auto *r = s->GetResolver ();
+      delete s;
+      delete r;
+    }
+  else
+    {
+      if (mapper->pex)
+	{
+	  int fd_write = mapper->GetFDWrite ();
+	  if (fd_write >= 0)
+	    close (fd_write);
+
+	  int status;
+	  pex_get_status (mapper->pex, 1, &status);
+
+	  pex_free (mapper->pex);
+	  mapper->pex = NULL;
+
+	  if (WIFSIGNALED (status))
+	    error_at (loc, "mapper died by signal %s",
+		      strsignal (WTERMSIG (status)));
+	  else if (WIFEXITED (status) && WEXITSTATUS (status) != 0)
+	    error_at (loc, "mapper exit status %d",
+		      WEXITSTATUS (status));
+	}
+      else
+	{
+	  int fd_read = mapper->GetFDRead ();
+	  close (fd_read);
+	}
+
+#ifdef SIGPIPE
+      // Restore sigpipe
+      if (mapper->sigpipe != SIG_IGN)
+	signal (SIGPIPE, mapper->sigpipe);
+#endif
+    }
+
+  delete mapper;
+}
diff --git c/gcc/cp/mapper-client.h w/gcc/cp/mapper-client.h
new file mode 100644
index 00000000000..ca1a0aa5509
--- /dev/null
+++ w/gcc/cp/mapper-client.h
@@ -0,0 +1,63 @@ 
+/* C++ modules.  Experimental!	-*- c++ -*-
+   Copyright (C) 2020 Free Software Foundation, Inc.
+   Written by Nathan Sidwell <nathan@acm.org> while at FaceBook
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   GCC is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* Forward to the header in c++tools.  */
+
+#ifndef MAPPER_CLIENT_H
+#define MAPPER_CLIENT_H 1
+
+#include "cody.hh"
+
+#ifndef HAVE_SIGHANDLER_T
+typedef void (*sighandler_t) (int);
+#endif
+
+class module_client : public Cody::Client
+{
+  pex_obj *pex = nullptr;
+  sighandler_t sigpipe = SIG_IGN;
+  Cody::Flags flags = Cody::Flags::None;
+
+public:
+  module_client (Cody::Server *s)
+    : Client (s)
+  {
+  }
+  module_client (pex_obj *pex, int fd_from, int fd_to);
+
+  module_client (int fd_from, int fd_to)
+    : Client (fd_from, fd_to)
+  {
+  }
+
+public:
+  Cody::Flags get_flags () const
+  {
+    return flags;
+  }
+
+public:
+  static module_client *open_module_client (location_t loc, const char *option,
+					    void (*set_repo) (const char *),
+					    char const *);
+  static void close_module_client (location_t loc, module_client *);
+};
+
+#endif
diff --git c/gcc/cp/mapper-resolver.cc w/gcc/cp/mapper-resolver.cc
new file mode 100644
index 00000000000..02ec48c61ea
--- /dev/null
+++ w/gcc/cp/mapper-resolver.cc
@@ -0,0 +1,27 @@ 
+/* C++ modules.  Experimental!	-*- c++ -*-
+   Copyright (C) 2020 Free Software Foundation, Inc.
+   Written by Nathan Sidwell <nathan@acm.org> while at FaceBook
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   GCC is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* Forward to the resolver in c++tools.  */
+
+#include "config.h"
+#define INCLUDE_ALGORITHM
+#include "system.h"
+
+#include "../../c++tools/resolver.cc"