c++: module mapper
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.
This commit is contained in:
parent
2efd8b1ffd
commit
b7dfc2074c
4 changed files with 448 additions and 1 deletions
|
@ -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 \
|
||||
|
|
356
gcc/cp/mapper-client.cc
Normal file
356
gcc/cp/mapper-client.cc
Normal file
|
@ -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;
|
||||
}
|
63
gcc/cp/mapper-client.h
Normal file
63
gcc/cp/mapper-client.h
Normal file
|
@ -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
|
27
gcc/cp/mapper-resolver.cc
Normal file
27
gcc/cp/mapper-resolver.cc
Normal file
|
@ -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"
|
Loading…
Add table
Reference in a new issue