123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322 |
- /* C++ modules. Experimental! -*- c++ -*-
- Copyright (C) 2017-2022 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>
- #include <memory>
- // C
- #include <cstring>
- // OS
- #include <fcntl.h>
- #include <unistd.h>
- #if 0 // 1 for testing no mmap
- #define MAPPED_READING 0
- #else
- #ifdef IN_GCC
- #if HAVE_MMAP_FILE && _POSIX_MAPPED_FILES > 0
- #define MAPPED_READING 1
- #else
- #define MAPPED_READING 0
- #endif
- #else
- #ifdef HAVE_SYS_MMAN_H
- #include <sys/mman.h>
- #define MAPPED_READING 1
- #else
- #define MAPPED_READING 0
- #endif
- #endif
- #endif
- #include <sys/types.h>
- #include <sys/stat.h>
- #if !defined (IN_GCC) && !MAPPED_READING
- #define xmalloc(X) malloc(X)
- #endif
- #if !HOST_HAS_O_CLOEXEC
- #define O_CLOEXEC 0
- #endif
- #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;
- void *buffer = nullptr;
- #if MAPPED_READING
- // Just map the file, we're gonna read all of it, so no need for
- // line buffering
- buffer = mmap (nullptr, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
- if (buffer == MAP_FAILED)
- return -errno;
- struct Deleter {
- void operator()(void* p) const { munmap(p, size); }
- size_t size;
- };
- std::unique_ptr<void, Deleter> guard(buffer, Deleter{(size_t)stat.st_size});
- #else
- buffer = xmalloc (stat.st_size);
- if (!buffer)
- return -errno;
- struct Deleter { void operator()(void* p) const { free(p); } };
- std::unique_ptr<void, Deleter> guard(buffer);
- if (read (fd, buffer, stat.st_size) != stat.st_size)
- return -errno;
- #endif
- 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);
- }
- }
- 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 = default_map ? GetCMIName (module) : std::string ();
- 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;
- }
- /* This handles a client notification to the server that a CMI has been
- produced for a module. For this simplified server, we just accept
- the transaction and respond with "OK". */
- int
- module_resolver::ModuleCompiledRequest (Cody::Server *s, Cody::Flags,
- std::string &)
- {
- s->OKResponse();
- return 0;
- }
|