resolver.cc 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. /* C++ modules. Experimental! -*- c++ -*-
  2. Copyright (C) 2017-2022 Free Software Foundation, Inc.
  3. Written by Nathan Sidwell <nathan@acm.org> while at FaceBook
  4. This file is part of GCC.
  5. GCC is free software; you can redistribute it and/or modify it
  6. under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 3, or (at your option)
  8. any later version.
  9. GCC is distributed in the hope that it will be useful, but
  10. WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with GCC; see the file COPYING3. If not see
  15. <http://www.gnu.org/licenses/>. */
  16. #include "config.h"
  17. #include "resolver.h"
  18. // C++
  19. #include <algorithm>
  20. #include <memory>
  21. // C
  22. #include <cstring>
  23. // OS
  24. #include <fcntl.h>
  25. #include <unistd.h>
  26. #if 0 // 1 for testing no mmap
  27. #define MAPPED_READING 0
  28. #else
  29. #ifdef IN_GCC
  30. #if HAVE_MMAP_FILE && _POSIX_MAPPED_FILES > 0
  31. #define MAPPED_READING 1
  32. #else
  33. #define MAPPED_READING 0
  34. #endif
  35. #else
  36. #ifdef HAVE_SYS_MMAN_H
  37. #include <sys/mman.h>
  38. #define MAPPED_READING 1
  39. #else
  40. #define MAPPED_READING 0
  41. #endif
  42. #endif
  43. #endif
  44. #include <sys/types.h>
  45. #include <sys/stat.h>
  46. #if !defined (IN_GCC) && !MAPPED_READING
  47. #define xmalloc(X) malloc(X)
  48. #endif
  49. #if !HOST_HAS_O_CLOEXEC
  50. #define O_CLOEXEC 0
  51. #endif
  52. #ifndef DIR_SEPARATOR
  53. #define DIR_SEPARATOR '/'
  54. #endif
  55. module_resolver::module_resolver (bool map, bool xlate)
  56. : default_map (map), default_translate (xlate)
  57. {
  58. }
  59. module_resolver::~module_resolver ()
  60. {
  61. if (fd_repo >= 0)
  62. close (fd_repo);
  63. }
  64. bool
  65. module_resolver::set_repo (std::string &&r, bool force)
  66. {
  67. if (force || repo.empty ())
  68. {
  69. repo = std::move (r);
  70. force = true;
  71. }
  72. return force;
  73. }
  74. bool
  75. module_resolver::add_mapping (std::string &&module, std::string &&file,
  76. bool force)
  77. {
  78. auto res = map.emplace (std::move (module), std::move (file));
  79. if (res.second)
  80. force = true;
  81. else if (force)
  82. res.first->second = std::move (file);
  83. return force;
  84. }
  85. int
  86. module_resolver::read_tuple_file (int fd, char const *prefix, bool force)
  87. {
  88. struct stat stat;
  89. if (fstat (fd, &stat) < 0)
  90. return -errno;
  91. if (!stat.st_size)
  92. return 0;
  93. void *buffer = nullptr;
  94. #if MAPPED_READING
  95. // Just map the file, we're gonna read all of it, so no need for
  96. // line buffering
  97. buffer = mmap (nullptr, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
  98. if (buffer == MAP_FAILED)
  99. return -errno;
  100. struct Deleter {
  101. void operator()(void* p) const { munmap(p, size); }
  102. size_t size;
  103. };
  104. std::unique_ptr<void, Deleter> guard(buffer, Deleter{(size_t)stat.st_size});
  105. #else
  106. buffer = xmalloc (stat.st_size);
  107. if (!buffer)
  108. return -errno;
  109. struct Deleter { void operator()(void* p) const { free(p); } };
  110. std::unique_ptr<void, Deleter> guard(buffer);
  111. if (read (fd, buffer, stat.st_size) != stat.st_size)
  112. return -errno;
  113. #endif
  114. size_t prefix_len = prefix ? strlen (prefix) : 0;
  115. unsigned lineno = 0;
  116. for (char const *begin = reinterpret_cast <char const *> (buffer),
  117. *end = begin + stat.st_size, *eol;
  118. begin != end; begin = eol + 1)
  119. {
  120. lineno++;
  121. eol = std::find (begin, end, '\n');
  122. if (eol == end)
  123. // last line has no \n, ignore the line, you lose
  124. break;
  125. auto *pos = begin;
  126. bool pfx_search = prefix_len != 0;
  127. pfx_search:
  128. while (*pos == ' ' || *pos == '\t')
  129. pos++;
  130. auto *space = pos;
  131. while (*space != '\n' && *space != ' ' && *space != '\t')
  132. space++;
  133. if (pos == space)
  134. // at end of line, nothing here
  135. continue;
  136. if (pfx_search)
  137. {
  138. if (size_t (space - pos) == prefix_len
  139. && std::equal (pos, space, prefix))
  140. pfx_search = false;
  141. pos = space;
  142. goto pfx_search;
  143. }
  144. std::string module (pos, space);
  145. while (*space == ' ' || *space == '\t')
  146. space++;
  147. std::string file (space, eol);
  148. if (module[0] == '$')
  149. {
  150. if (module == "$root")
  151. set_repo (std::move (file));
  152. else
  153. return lineno;
  154. }
  155. else
  156. {
  157. if (file.empty ())
  158. file = GetCMIName (module);
  159. add_mapping (std::move (module), std::move (file), force);
  160. }
  161. }
  162. return 0;
  163. }
  164. char const *
  165. module_resolver::GetCMISuffix ()
  166. {
  167. return "gcm";
  168. }
  169. module_resolver *
  170. module_resolver::ConnectRequest (Cody::Server *s, unsigned version,
  171. std::string &a, std::string &i)
  172. {
  173. if (!version || version > Cody::Version)
  174. s->ErrorResponse ("version mismatch");
  175. else if (a != "GCC")
  176. // Refuse anything but GCC
  177. ErrorResponse (s, std::string ("only GCC supported"));
  178. else if (!ident.empty () && ident != i)
  179. // Failed ident check
  180. ErrorResponse (s, std::string ("bad ident"));
  181. else
  182. // Success!
  183. s->ConnectResponse ("gcc");
  184. return this;
  185. }
  186. int
  187. module_resolver::ModuleRepoRequest (Cody::Server *s)
  188. {
  189. s->PathnameResponse (repo);
  190. return 0;
  191. }
  192. int
  193. module_resolver::cmi_response (Cody::Server *s, std::string &module)
  194. {
  195. auto iter = map.find (module);
  196. if (iter == map.end ())
  197. {
  198. std::string file = default_map ? GetCMIName (module) : std::string ();
  199. auto res = map.emplace (module, file);
  200. iter = res.first;
  201. }
  202. if (iter->second.empty ())
  203. s->ErrorResponse ("no such module");
  204. else
  205. s->PathnameResponse (iter->second);
  206. return 0;
  207. }
  208. int
  209. module_resolver::ModuleExportRequest (Cody::Server *s, Cody::Flags,
  210. std::string &module)
  211. {
  212. return cmi_response (s, module);
  213. }
  214. int
  215. module_resolver::ModuleImportRequest (Cody::Server *s, Cody::Flags,
  216. std::string &module)
  217. {
  218. return cmi_response (s, module);
  219. }
  220. int
  221. module_resolver::IncludeTranslateRequest (Cody::Server *s, Cody::Flags,
  222. std::string &include)
  223. {
  224. auto iter = map.find (include);
  225. if (iter == map.end () && default_translate)
  226. {
  227. // Not found, look for it
  228. auto file = GetCMIName (include);
  229. struct stat statbuf;
  230. bool ok = true;
  231. #if HAVE_FSTATAT
  232. int fd_dir = AT_FDCWD;
  233. if (!repo.empty ())
  234. {
  235. if (fd_repo == -1)
  236. {
  237. fd_repo = open (repo.c_str (),
  238. O_RDONLY | O_CLOEXEC | O_DIRECTORY);
  239. if (fd_repo < 0)
  240. fd_repo = -2;
  241. }
  242. fd_dir = fd_repo;
  243. }
  244. if (!repo.empty () && fd_repo < 0)
  245. ok = false;
  246. else if (fstatat (fd_dir, file.c_str (), &statbuf, 0) < 0
  247. || !S_ISREG (statbuf.st_mode))
  248. ok = false;
  249. #else
  250. auto append = repo;
  251. append.push_back (DIR_SEPARATOR);
  252. append.append (file);
  253. if (stat (append.c_str (), &statbuf) < 0
  254. || !S_ISREG (statbuf.st_mode))
  255. ok = false;
  256. #endif
  257. if (!ok)
  258. // Mark as not present
  259. file.clear ();
  260. auto res = map.emplace (include, file);
  261. iter = res.first;
  262. }
  263. if (iter == map.end () || iter->second.empty ())
  264. s->BoolResponse (false);
  265. else
  266. s->PathnameResponse (iter->second);
  267. return 0;
  268. }
  269. /* This handles a client notification to the server that a CMI has been
  270. produced for a module. For this simplified server, we just accept
  271. the transaction and respond with "OK". */
  272. int
  273. module_resolver::ModuleCompiledRequest (Cody::Server *s, Cody::Flags,
  274. std::string &)
  275. {
  276. s->OKResponse();
  277. return 0;
  278. }