Skip to content
Snippets Groups Projects
Commit 35fc243f authored by Nathan Sidwell's avatar Nathan Sidwell
Browse files

Add c++tools

Part of our module implementation adds a sample mapper server, the
guts of which are used by the default in-process mapping of cc1plus.
Rather than add another executable to gcc/cp/, this creates a new
c++tools directory where this and any other c++ tools might live.
The toplevel changes are a subsequent commit, because ... git.

c++tools/ChangeLog:

	* Makefile.in: New.
	* config.h.in: New.
	* configure: New.
	* configure.ac: New.
	* resolver.cc: New.
	* resolver.h: New.
	* server.cc: New.
parent e831ad4d
No related branches found
No related tags found
No related merge requests found
# Makefile for c++tools
# Copyright 2020 Free Software Foundation, Inc.
#
# This file 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 of the License, or
# (at your option) any later version.
#
# This program 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 this program; see the file COPYING3. If not see
# <http://www.gnu.org/licenses/>.
srcdir := @srcdir@
prefix := @prefix@
bindir := @bindir@
libexecdir := @libexecdir@
target_noncanonical := @target_noncanonical@
version := $(shell cat $(srcdir)/../gcc/BASE-VER)
libexecsubdir := $(libexecdir)/gcc/$(target_noncanonical)/$(version)
INSTALL := @INSTALL@
AUTOCONF := @AUTOCONF@
AUTOHEADER := @AUTOHEADER@
CXX := @CXX@
CXXFLAGS := @CXXFLAGS@
CXXOPTS := $(CXXFLAGS) -fno-exceptions -fno-rtti
EXEEXT := @EXEEXT@
LIBIBERTY := ../libiberty/libiberty.a
VERSION.O := ../gcc/version.o
all::
mostlyclean::
rm -f $(MAPPER.O)
clean::
rm -f c++-mapper-server$(exeext)
distclean::
rm -f config.log config.status config.h
maintainer-clean::
install::
check:
installcheck:
dvi:
pdf:
html:
info:
install-info:
install-pdf:
install-man:
install-html:
vpath %.cc $(srcdir)
vpath %.in $(srcdir)
.SUFFIXES:
.SUFFIXES: .cc .o
# Per-source & per-directory compile flags (warning: recursive)
SRC_CXXFLAGS = $(CXXFLAGS$(patsubst $(srcdir)%,%,$1)) \
$(if $(filter-out $(srcdir)/,$1),\
$(call $0,$(dir $(patsubst %/,%,$1))))
%.o: %.cc
$(CXX) $(strip $(CXXOPTS) $(call SRC_CXXFLAGS,$<) $(CXXINC)) \
-MMD -MP -MF ${@:.o=.d} -c -o $@ $<
ifeq (@CXX_AUX_TOOLS@,yes)
all::g++-mapper-server$(exeext)
MAPPER.O := server.o resolver.o
CODYLIB = ../libcody/libcody.a
CXXINC += -I$(srcdir)/../libcody -I$(srcdir)/../include -I$(srcdir)/../gcc -I.
g++-mapper-server$(exeext): $(MAPPER.O) $(CODYLIB)
+$(CXX) $(LDFLAGS) -o $@ $^ $(VERSION.O) $(LIBIBERTY)
# copy to gcc dir so tests there can run
all::../gcc/g++-mapper-server$(exeext)
../gcc/g++-mapper-server$(exeext): g++-mapper-server$(exeext)
$(INSTALL) -p $< $@
install::
$(INSTALL) -p g++-mapper-server$(exeext) $(DESTDIR)$(libexecsubdir)
endif
ifneq ($(MAINTAINER),)
override MAINTAINER += $1
endif
ifeq (@MAINTAINER@,yes)
MAINTAINER = $2
else
MAINTAINER = \# --enable-maintainer-mode to rebuild $1, or make MAINTAINER=touch
endif
all:: Makefile
Makefile: $(srcdir)/Makefile.in config.status
$(SHELL) ./config.status Makefile
$(srcdir)/configure: $(srcdir)/configure.ac
$(call MAINTAINER,$@,cd $(@D) && $(AUTOCONF) -W all,error)
$(srcdir)/config.h.in: $(srcdir)/configure.ac
$(call MAINTAINER,$@,cd $(@D) && $(AUTOHEADER) -f -W all,error)
config.h: config.status config.h.in
./$< --header=$@
touch $@
config.status: $(srcdir)/configure $(srcdir)/config.h.in
if test -x $@; then ./$@ -recheck; else $< @configure_args@; fi
.PHONY: all check clean distclean maintainer-clean
-include $(MAPPER.O:.o=.d)
/* config.h.in. Generated from configure.ac by autoheader. */
/* Define if accept4 provided. */
#undef HAVE_ACCEPT4
/* Define if AF_INET6 supported. */
#undef HAVE_AF_INET6
/* Define if AF_UNIX supported. */
#undef HAVE_AF_UNIX
/* Define if epoll_create, epoll_ctl, epoll_pwait provided. */
#undef HAVE_EPOLL
/* Define if inet_ntop provided. */
#undef HAVE_INET_NTOP
/* Define if pselect provided. */
#undef HAVE_PSELECT
/* Define if select provided. */
#undef HAVE_SELECT
/* Define if O_CLOEXEC supported by fcntl. */
#undef HOST_HAS_O_CLOEXEC
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
/* Define to the full name of this package. */
#undef PACKAGE_NAME
/* Define to the full name and version of this package. */
#undef PACKAGE_STRING
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the home page for this package. */
#undef PACKAGE_URL
/* Define to the version of this package. */
#undef PACKAGE_VERSION
This diff is collapsed.
# Configure script for c++tools
# Copyright (C) 2020 Free Software Foundation, Inc.
# Written by Nathan Sidwell <nathan@acm.org> while at FaceBook
#
# This file 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 of the License, or
# (at your option) any later version.
#
# This program 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 this program; see the file COPYING3. If not see
# <http://www.gnu.org/licenses/>.
# C++ has grown a C++20 mapper server. This may be used to provide
# and/or learn and/or build required modules. This sample server
# shows how the protocol introduced by wg21.link/p1184 may be used.
# By default g++ uses an in-process mapper.
sinclude(../config/acx.m4)
AC_INIT(c++tools)
AC_CONFIG_SRCDIR([server.cc])
# Determine the noncanonical names used for directories.
ACX_NONCANONICAL_TARGET
AC_CANONICAL_SYSTEM
AC_PROG_INSTALL
AC_PROG_CXX
MISSING=`cd $ac_aux_dir && ${PWDCMD-pwd}`/missing
AC_CHECK_PROGS([AUTOCONF], [autoconf], [$MISSING autoconf])
AC_CHECK_PROGS([AUTOHEADER], [autoheader], [$MISSING autoheader])
dnl Enabled by default
AC_MSG_CHECKING([whether to build C++ tools])
AC_ARG_ENABLE(c++-tools,
[AS_HELP_STRING([--enable-c++-tools],
[build auxiliary c++ tools])],
cxx_aux_tools=$enableval,
cxx_aux_tools=yes)
AC_MSG_RESULT($cxx_aux_tools)
CXX_AUX_TOOLS="$cxx_aux_tools"
AC_SUBST(CXX_AUX_TOOLS)
AC_ARG_ENABLE([maintainer-mode],
AS_HELP_STRING([--enable-maintainer-mode],
[enable maintainer mode. Add rules to rebuild configurey bits]),,
[enable_maintainer_mode=no])
case "$enable_maintainer_mode" in
("yes") maintainer_mode=yes ;;
("no") maintainer=no ;;
(*) AC_MSG_ERROR([unknown maintainer mode $enable_maintainer_mode]) ;;
esac
AC_MSG_CHECKING([maintainer-mode])
AC_MSG_RESULT([$maintainer_mode])
test "$maintainer_mode" = yes && MAINTAINER=yes
AC_SUBST(MAINTAINER)
# Check if O_CLOEXEC is defined by fcntl
AC_CACHE_CHECK(for O_CLOEXEC, ac_cv_o_cloexec, [
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#include <fcntl.h>]], [[
return open ("/dev/null", O_RDONLY | O_CLOEXEC);]])],
[ac_cv_o_cloexec=yes],[ac_cv_o_cloexec=no])])
if test $ac_cv_o_cloexec = yes; then
AC_DEFINE(HOST_HAS_O_CLOEXEC, 1,
[Define if O_CLOEXEC supported by fcntl.])
fi
# C++ Modules would like some networking features to provide the mapping
# server. You can still use modules without them though.
# The following network-related checks could probably do with some
# Windows and other non-linux defenses and checking.
# Local socket connectivity wants AF_UNIX networking
# Check for AF_UNIX networking
AC_CACHE_CHECK(for AF_UNIX, ac_cv_af_unix, [
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>]],[[
sockaddr_un un;
un.sun_family = AF_UNSPEC;
int fd = socket (AF_UNIX, SOCK_STREAM, 0);
connect (fd, (sockaddr *)&un, sizeof (un));]])],
[ac_cv_af_unix=yes],
[ac_cv_af_unix=no])])
if test $ac_cv_af_unix = yes; then
AC_DEFINE(HAVE_AF_UNIX, 1,
[Define if AF_UNIX supported.])
fi
# Remote socket connectivity wants AF_INET6 networking
# Check for AF_INET6 networking
AC_CACHE_CHECK(for AF_INET6, ac_cv_af_inet6, [
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>]],[[
sockaddr_in6 in6;
in6.sin6_family = AF_UNSPEC;
struct addrinfo *addrs = 0;
struct addrinfo hints;
hints.ai_flags = 0;
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
hints.ai_canonname = 0;
hints.ai_addr = 0;
hints.ai_next = 0;
int e = getaddrinfo ("localhost", 0, &hints, &addrs);
const char *str = gai_strerror (e);
freeaddrinfo (addrs);
int fd = socket (AF_INET6, SOCK_STREAM, 0);
connect (fd, (sockaddr *)&in6, sizeof (in6));]])],
[ac_cv_af_inet6=yes],
[ac_cv_af_inet6=no])])
if test $ac_cv_af_inet6 = yes; then
AC_DEFINE(HAVE_AF_INET6, 1,
[Define if AF_INET6 supported.])
fi
# Efficient server response wants epoll
# Check for epoll_create, epoll_ctl, epoll_pwait
AC_CACHE_CHECK(for epoll, ac_cv_epoll, [
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#include <sys/epoll.h>]],[[
int fd = epoll_create (1);
epoll_event ev;
ev.events = EPOLLIN;
ev.data.fd = 0;
epoll_ctl (fd, EPOLL_CTL_ADD, 0, &ev);
epoll_pwait (fd, 0, 0, -1, 0);]])],
[ac_cv_epoll=yes],
[ac_cv_epoll=no])])
if test $ac_cv_epoll = yes; then
AC_DEFINE(HAVE_EPOLL, 1,
[Define if epoll_create, epoll_ctl, epoll_pwait provided.])
fi
# If we can't use epoll, try pselect.
# Check for pselect
AC_CACHE_CHECK(for pselect, ac_cv_pselect, [
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#include <sys/select.h>]],[[
pselect (0, 0, 0, 0, 0, 0);]])],
[ac_cv_pselect=yes],
[ac_cv_pselect=no])])
if test $ac_cv_pselect = yes; then
AC_DEFINE(HAVE_PSELECT, 1,
[Define if pselect provided.])
fi
# And failing that, use good old select.
# If we can't even use this, the server is serialized.
# Check for select
AC_CACHE_CHECK(for select, ac_cv_select, [
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#include <sys/select.h>]],[[
select (0, 0, 0, 0, 0);]])],
[ac_cv_select=yes],
[ac_cv_select=no])])
if test $ac_cv_select = yes; then
AC_DEFINE(HAVE_SELECT, 1,
[Define if select provided.])
fi
# Avoid some fnctl calls by using accept4, when available.
# Check for accept4
AC_CACHE_CHECK(for accept4, ac_cv_accept4, [
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#include <sys/socket.h>]],[[
int err = accept4 (1, 0, 0, SOCK_NONBLOCK);]])],
[ac_cv_accept4=yes],
[ac_cv_accept4=no])])
if test $ac_cv_accept4 = yes; then
AC_DEFINE(HAVE_ACCEPT4, 1,
[Define if accept4 provided.])
fi
# For better server messages, look for a way to stringize network addresses
# Check for inet_ntop
AC_CACHE_CHECK(for inet_ntop, ac_cv_inet_ntop, [
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#include <arpa/inet.h>
#include <netinet/in.h>]],[[
sockaddr_in6 in6;
char buf[INET6_ADDRSTRLEN];
const char *str = inet_ntop (AF_INET6, &in6, buf, sizeof (buf));]])],
[ac_cv_inet_ntop=yes],
[ac_cv_inet_ntop=no])])
if test $ac_cv_inet_ntop = yes; then
AC_DEFINE(HAVE_INET_NTOP, 1,
[Define if inet_ntop provided.])
fi
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
/* C++ modules. Experimental! -*- c++ -*-
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 "resolver.h"
// C++
#include <algorithm>
// C
#include <cstring>
// OS
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifndef DIR_SEPARATOR
#define DIR_SEPARATOR '/'
#endif
module_resolver::module_resolver (bool map, bool xlate)
: default_map (map), default_translate (xlate)
{
}
module_resolver::~module_resolver ()
{
if (fd_repo >= 0)
close (fd_repo);
}
bool
module_resolver::set_repo (std::string &&r, bool force)
{
if (force || repo.empty ())
{
repo = std::move (r);
force = true;
}
return force;
}
bool
module_resolver::add_mapping (std::string &&module, std::string &&file,
bool force)
{
auto res = map.emplace (std::move (module), std::move (file));
if (res.second)
force = true;
else if (force)
res.first->second = std::move (file);
return force;
}
int
module_resolver::read_tuple_file (int fd, char const *prefix, bool force)
{
struct stat stat;
if (fstat (fd, &stat) < 0)
return -errno;
if (!stat.st_size)
return 0;
// Just map the file, we're gonna read all of it, so no need for
// line buffering
void *buffer = mmap (nullptr, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (buffer == MAP_FAILED)
return -errno;
size_t prefix_len = prefix ? strlen (prefix) : 0;
unsigned lineno = 0;
for (char const *begin = reinterpret_cast <char const *> (buffer),
*end = begin + stat.st_size, *eol;
begin != end; begin = eol + 1)
{
lineno++;
eol = std::find (begin, end, '\n');
if (eol == end)
// last line has no \n, ignore the line, you lose
break;
auto *pos = begin;
bool pfx_search = prefix_len != 0;
pfx_search:
while (*pos == ' ' || *pos == '\t')
pos++;
auto *space = pos;
while (*space != '\n' && *space != ' ' && *space != '\t')
space++;
if (pos == space)
// at end of line, nothing here
continue;
if (pfx_search)
{
if (size_t (space - pos) == prefix_len
&& std::equal (pos, space, prefix))
pfx_search = false;
pos = space;
goto pfx_search;
}
std::string module (pos, space);
while (*space == ' ' || *space == '\t')
space++;
std::string file (space, eol);
if (module[0] == '$')
{
if (module == "$root")
set_repo (std::move (file));
else
return lineno;
}
else
{
if (file.empty ())
file = GetCMIName (module);
add_mapping (std::move (module), std::move (file), force);
}
}
munmap (buffer, stat.st_size);
return 0;
}
char const *
module_resolver::GetCMISuffix ()
{
return "gcm";
}
module_resolver *
module_resolver::ConnectRequest (Cody::Server *s, unsigned version,
std::string &a, std::string &i)
{
if (!version || version > Cody::Version)
s->ErrorResponse ("version mismatch");
else if (a != "GCC")
// Refuse anything but GCC
ErrorResponse (s, std::string ("only GCC supported"));
else if (!ident.empty () && ident != i)
// Failed ident check
ErrorResponse (s, std::string ("bad ident"));
else
// Success!
s->ConnectResponse ("gcc");
return this;
}
int
module_resolver::ModuleRepoRequest (Cody::Server *s)
{
s->PathnameResponse (repo);
return 0;
}
int
module_resolver::cmi_response (Cody::Server *s, std::string &module)
{
auto iter = map.find (module);
if (iter == map.end ())
{
std::string file;
if (default_map)
file = std::move (GetCMIName (module));
auto res = map.emplace (module, file);
iter = res.first;
}
if (iter->second.empty ())
s->ErrorResponse ("no such module");
else
s->PathnameResponse (iter->second);
return 0;
}
int
module_resolver::ModuleExportRequest (Cody::Server *s, Cody::Flags,
std::string &module)
{
return cmi_response (s, module);
}
int
module_resolver::ModuleImportRequest (Cody::Server *s, Cody::Flags,
std::string &module)
{
return cmi_response (s, module);
}
int
module_resolver::IncludeTranslateRequest (Cody::Server *s, Cody::Flags,
std::string &include)
{
auto iter = map.find (include);
if (iter == map.end () && default_translate)
{
// Not found, look for it
auto file = GetCMIName (include);
struct stat statbuf;
bool ok = true;
#if HAVE_FSTATAT
int fd_dir = AT_FDCWD;
if (!repo.empty ())
{
if (fd_repo == -1)
{
fd_repo = open (repo.c_str (),
O_RDONLY | O_CLOEXEC | O_DIRECTORY);
if (fd_repo < 0)
fd_repo = -2;
}
fd_dir = fd_repo;
}
if (!repo.empty () && fd_repo < 0)
ok = false;
else if (fstatat (fd_dir, file.c_str (), &statbuf, 0) < 0
|| !S_ISREG (statbuf.st_mode))
ok = false;
#else
auto append = repo;
append.push_back (DIR_SEPARATOR);
append.append (file);
if (stat (append.c_str (), &statbuf) < 0
|| !S_ISREG (statbuf.st_mode))
ok = false;
#endif
if (!ok)
// Mark as not present
file.clear ();
auto res = map.emplace (include, file);
iter = res.first;
}
if (iter == map.end () || iter->second.empty ())
s->BoolResponse (false);
else
s->PathnameResponse (iter->second);
return 0;
}
/* C++ modules. Experimental! -*- c++ -*-
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/>. */
#ifndef GXX_RESOLVER_H
#define GXX_RESOLVER_H 1
// Mapper interface for client and server bits
#include "cody.hh"
// C++
#include <string>
#include <map>
// This is a GCC class, so GCC coding conventions on new bits.
class module_resolver : public Cody::Resolver
{
public:
using parent = Cody::Resolver;
using module_map = std::map<std::string, std::string>;
private:
std::string repo;
std::string ident;
module_map map;
int fd_repo = -1;
bool default_map = true;
bool default_translate = true;
public:
module_resolver (bool map = true, bool xlate = false);
virtual ~module_resolver () override;
public:
void set_default_map (bool d)
{
default_map = d;
}
void set_default_translate (bool d)
{
default_translate = d;
}
void set_ident (char const *i)
{
ident = i;
}
bool set_repo (std::string &&repo, bool force = false);
bool add_mapping (std::string &&module, std::string &&file,
bool force = false);
// Return +ve line number of error, or -ve errno
int read_tuple_file (int fd, char const *prefix, bool force = false);
int read_tuple_file (int fd, std::string const &prefix,
bool force = false)
{
return read_tuple_file (fd, prefix.empty () ? nullptr : prefix.c_str (),
force);
}
public:
// Virtual overriders, names are controlled by Cody::Resolver
using parent::ConnectRequest;
virtual module_resolver *ConnectRequest (Cody::Server *, unsigned version,
std::string &agent,
std::string &ident)
override;
using parent::ModuleRepoRequest;
virtual int ModuleRepoRequest (Cody::Server *) override;
using parent::ModuleExportRequest;
virtual int ModuleExportRequest (Cody::Server *s, Cody::Flags,
std::string &module)
override;
using parent::ModuleImportRequest;
virtual int ModuleImportRequest (Cody::Server *s, Cody::Flags,
std::string &module)
override;
using parent::IncludeTranslateRequest;
virtual int IncludeTranslateRequest (Cody::Server *s, Cody::Flags,
std::string &include)
override;
private:
using parent::GetCMISuffix;
virtual char const *GetCMISuffix () override;
private:
int cmi_response (Cody::Server *s, std::string &module);
};
#endif
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment