1 /* C++ modules. Experimental! -*- c++ -*-
2 Copyright (C) 2017-2024 Free Software Foundation, Inc.
3 Written by Nathan Sidwell <nathan@acm.org> while at FaceBook
5 This file is part of GCC.
7 GCC is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3, or (at your option)
12 GCC is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3. If not see
19 <http://www.gnu.org/licenses/>. */
32 #if 0 // 1 for testing no mmap
33 #define MAPPED_READING 0
36 #if HAVE_MMAP_FILE && _POSIX_MAPPED_FILES > 0
37 #define MAPPED_READING 1
39 #define MAPPED_READING 0
42 #ifdef HAVE_SYS_MMAN_H
44 #define MAPPED_READING 1
46 #define MAPPED_READING 0
51 #include <sys/types.h>
54 #if !defined (IN_GCC) && !MAPPED_READING
55 #define xmalloc(X) malloc(X)
58 #if !HOST_HAS_O_CLOEXEC
63 #define DIR_SEPARATOR '/'
66 module_resolver::module_resolver (bool map
, bool xlate
)
67 : default_map (map
), default_translate (xlate
)
71 module_resolver::~module_resolver ()
78 module_resolver::set_repo (std::string
&&r
, bool force
)
80 if (force
|| repo
.empty ())
89 module_resolver::add_mapping (std::string
&&module
, std::string
&&file
,
92 auto res
= map
.emplace (std::move (module
), std::move (file
));
96 res
.first
->second
= std::move (file
);
102 module_resolver::read_tuple_file (int fd
, char const *prefix
, bool force
)
105 if (fstat (fd
, &stat
) < 0)
111 void *buffer
= nullptr;
113 // Just map the file, we're gonna read all of it, so no need for
115 buffer
= mmap (nullptr, stat
.st_size
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
116 if (buffer
== MAP_FAILED
)
119 void operator()(void* p
) const { munmap(p
, size
); }
122 std::unique_ptr
<void, Deleter
> guard(buffer
, Deleter
{(size_t)stat
.st_size
});
124 buffer
= xmalloc (stat
.st_size
);
127 struct Deleter
{ void operator()(void* p
) const { free(p
); } };
128 std::unique_ptr
<void, Deleter
> guard(buffer
);
129 if (read (fd
, buffer
, stat
.st_size
) != stat
.st_size
)
133 size_t prefix_len
= prefix
? strlen (prefix
) : 0;
136 for (char const *begin
= reinterpret_cast <char const *> (buffer
),
137 *end
= begin
+ stat
.st_size
, *eol
;
138 begin
!= end
; begin
= eol
+ 1)
141 eol
= std::find (begin
, end
, '\n');
143 // last line has no \n, ignore the line, you lose
147 bool pfx_search
= prefix_len
!= 0;
150 while (*pos
== ' ' || *pos
== '\t')
154 while (*space
!= '\n' && *space
!= ' ' && *space
!= '\t')
158 // at end of line, nothing here
163 if (size_t (space
- pos
) == prefix_len
164 && std::equal (pos
, space
, prefix
))
170 std::string
module (pos
, space
);
171 while (*space
== ' ' || *space
== '\t')
173 std::string
file (space
, eol
);
175 if (module
[0] == '$')
177 if (module
== "$root")
178 set_repo (std::move (file
));
185 file
= GetCMIName (module
);
186 add_mapping (std::move (module
), std::move (file
), force
);
194 module_resolver::GetCMISuffix ()
200 module_resolver::ConnectRequest (Cody::Server
*s
, unsigned version
,
201 std::string
&a
, std::string
&i
)
203 if (!version
|| version
> Cody::Version
)
204 s
->ErrorResponse ("version mismatch");
206 // Refuse anything but GCC
207 ErrorResponse (s
, std::string ("only GCC supported"));
208 else if (!ident
.empty () && ident
!= i
)
209 // Failed ident check
210 ErrorResponse (s
, std::string ("bad ident"));
213 s
->ConnectResponse ("gcc");
219 module_resolver::ModuleRepoRequest (Cody::Server
*s
)
221 s
->PathnameResponse (repo
);
226 module_resolver::cmi_response (Cody::Server
*s
, std::string
&module
)
228 auto iter
= map
.find (module
);
229 if (iter
== map
.end ())
231 std::string file
= default_map
? GetCMIName (module
) : std::string ();
232 auto res
= map
.emplace (module
, file
);
236 if (iter
->second
.empty ())
237 s
->ErrorResponse ("no such module");
239 s
->PathnameResponse (iter
->second
);
245 module_resolver::ModuleExportRequest (Cody::Server
*s
, Cody::Flags
,
248 return cmi_response (s
, module
);
252 module_resolver::ModuleImportRequest (Cody::Server
*s
, Cody::Flags
,
255 return cmi_response (s
, module
);
259 module_resolver::IncludeTranslateRequest (Cody::Server
*s
, Cody::Flags
,
260 std::string
&include
)
262 auto iter
= map
.find (include
);
263 if (iter
== map
.end () && default_translate
)
265 // Not found, look for it
266 auto file
= GetCMIName (include
);
271 int fd_dir
= AT_FDCWD
;
276 fd_repo
= open (repo
.c_str (),
277 O_RDONLY
| O_CLOEXEC
| O_DIRECTORY
);
284 if (!repo
.empty () && fd_repo
< 0)
286 else if (fstatat (fd_dir
, file
.c_str (), &statbuf
, 0) < 0
287 || !S_ISREG (statbuf
.st_mode
))
291 append
.push_back (DIR_SEPARATOR
);
292 append
.append (file
);
293 if (stat (append
.c_str (), &statbuf
) < 0
294 || !S_ISREG (statbuf
.st_mode
))
298 // Mark as not present
300 auto res
= map
.emplace (include
, file
);
304 if (iter
== map
.end () || iter
->second
.empty ())
305 s
->BoolResponse (false);
307 s
->PathnameResponse (iter
->second
);
312 /* This handles a client notification to the server that a CMI has been
313 produced for a module. For this simplified server, we just accept
314 the transaction and respond with "OK". */
317 module_resolver::ModuleCompiledRequest (Cody::Server
*s
, Cody::Flags
,