--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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