blob: 1da1ce99ce431e75883dac037c41fcc73fad78d6 [file] [log] [blame]
/* C++ modules. Experimental! -*- c++-mode -*-
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 "mapper.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>
#include "collect-utils.h"
module_resolver::module_resolver (bool def)
: provide_default (def)
{
}
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;
// 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 (provide_default)
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, std::string &module)
{
return cmi_response (s, module);
}
int
module_resolver::ModuleImportRequest (Cody::Server *s, std::string &module)
{
return cmi_response (s, module);
}
int
module_resolver::IncludeTranslateRequest (Cody::Server *s,
std::string &include)
{
auto iter = map.find (include);
if (iter == map.end ())
{
// Not found, look for it
if (fd_repo == -1 && !repo.empty ())
{
fd_repo = open (repo.c_str (), O_RDONLY | O_CLOEXEC | O_DIRECTORY);
if (fd_repo < 0)
fd_repo = -2;
}
if (fd_repo >= 0 || repo.empty ())
{
auto file = GetCMIName (include);
struct stat statbuf;
if (fstatat (repo.empty () ? AT_FDCWD : fd_repo,
file.c_str (), &statbuf, 0) < 0
|| !S_ISREG (statbuf.st_mode))
// 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;
}
int module_resolver::InvokeSubProcessRequest (Cody::Server *s, std::vector<std::string> &args) {
char **new_argv = (char **)malloc((args.size()) * sizeof(char *));
new_argv[args.size()-1] = NULL;
int i = 0;
for (std::vector<std::string>::iterator arg = args.begin() ; arg != args.end(); ++arg, ++i) {
// ignore "INVOKE"
if(i == 0) continue;
//new_argv[i++] = (char *)malloc(args[i].length() * sizeof(char));
// TODO: type safety
new_argv[i-1] = (char *)(*arg).c_str();
//fprintf(stderr, "*arg[?] = %s\n", (*arg).c_str());
//fprintf(stderr, "*new_argv[%d] = %s\n", i-1, new_argv[i-1]);
}
//fprintf(stderr, "\tcalling fork_execute\n");
fork_execute (new_argv[0], new_argv, true);
// TODO: send back a compile status response
s->OKResponse();
return 0;
}