Daily bump.
[official-gcc.git] / gcc / cp / mapper-client.cc
blobf76fc03b76f037bdb7b73465c20a311777afda41
1 /* C++ modules. Experimental!
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)
10 any later version.
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/>. */
21 #include "config.h"
22 #if defined (__unix__)
23 // Solaris11's socket header used bcopy, which we poison. cody.hh
24 // will include it later under the above check
25 #include <sys/socket.h>
26 #endif
27 #define INCLUDE_STRING
28 #define INCLUDE_VECTOR
29 #define INCLUDE_MAP
30 #define INCLUDE_MEMORY
31 #include "system.h"
33 #include "line-map.h"
34 #include "rich-location.h"
35 #include "diagnostic-core.h"
36 #include "mapper-client.h"
37 #include "intl.h"
38 #include "mkdeps.h"
40 #include "../../c++tools/resolver.h"
42 #if !HOST_HAS_O_CLOEXEC
43 #define O_CLOEXEC 0
44 #endif
46 module_client::module_client (pex_obj *p, int fd_from, int fd_to)
47 : Client (fd_from, fd_to), pex (p)
51 static module_client *
52 spawn_mapper_program (char const **errmsg, std::string &name,
53 char const *full_program_name)
55 /* Split writable at white-space. No space-containing args for
56 you! */
57 // At most every other char could be an argument
58 char **argv = new char *[name.size () / 2 + 2];
59 unsigned arg_no = 0;
60 char *str = new char[name.size ()];
61 memcpy (str, name.c_str () + 1, name.size ());
63 for (auto ptr = str; ; ++ptr)
65 while (*ptr == ' ')
66 ptr++;
67 if (!*ptr)
68 break;
70 if (!arg_no)
72 /* @name means look in the compiler's install dir. */
73 if (ptr[0] == '@')
74 ptr++;
75 else
76 full_program_name = nullptr;
79 argv[arg_no++] = ptr;
80 while (*ptr && *ptr != ' ')
81 ptr++;
82 if (!*ptr)
83 break;
84 *ptr = 0;
86 argv[arg_no] = nullptr;
88 auto *pex = pex_init (PEX_USE_PIPES, progname, NULL);
89 FILE *to = pex_input_pipe (pex, false);
90 name = argv[0];
91 if (!to)
92 *errmsg = "connecting input";
93 else
95 int flags = PEX_SEARCH;
97 if (full_program_name)
99 /* Prepend the invoking path, if the mapper is a simple
100 file name. */
101 size_t dir_len = progname - full_program_name;
102 std::string argv0;
103 argv0.reserve (dir_len + name.size ());
104 argv0.append (full_program_name, dir_len).append (name);
105 name = std::move (argv0);
106 argv[0] = const_cast <char *> (name.c_str ());
107 flags = 0;
109 int err;
110 *errmsg = pex_run (pex, flags, argv[0], argv, NULL, NULL, &err);
112 delete[] str;
113 delete[] argv;
115 int fd_from = -1, fd_to = -1;
116 if (!*errmsg)
118 FILE *from = pex_read_output (pex, false);
119 if (from && (fd_to = dup (fileno (to))) >= 0)
120 fd_from = fileno (from);
121 else
122 *errmsg = "connecting output";
123 fclose (to);
126 if (*errmsg)
128 pex_free (pex);
129 return nullptr;
132 return new module_client (pex, fd_from, fd_to);
135 module_client *
136 module_client::open_module_client (location_t loc, const char *o,
137 class mkdeps *deps,
138 void (*set_repo) (const char *),
139 char const *full_program_name)
141 module_client *c = nullptr;
142 std::string ident;
143 std::string name;
144 char const *errmsg = nullptr;
145 unsigned line = 0;
147 if (o && o[0])
149 /* Maybe a local or ipv6 address. */
150 name = o;
151 auto last = name.find_last_of ('?');
152 if (last != name.npos)
154 ident = name.substr (last + 1);
155 name.erase (last);
158 if (name.size ())
160 switch (name[0])
162 case '<':
163 // <from>to or <>fromto, or <>
165 size_t pos = name.find ('>', 1);
166 if (pos == std::string::npos)
167 pos = name.size ();
168 std::string from (name, 1, pos - 1);
169 std::string to;
170 if (pos != name.size ())
171 to.append (name, pos + 1, std::string::npos);
173 int fd_from = -1, fd_to = -1;
174 if (from.empty () && to.empty ())
176 fd_from = fileno (stdin);
177 fd_to = fileno (stdout);
179 else
181 char *ptr;
182 if (!from.empty ())
184 /* Sadly str::stoul is not portable. */
185 const char *cstr = from.c_str ();
186 fd_from = strtoul (cstr, &ptr, 10);
187 if (*ptr)
189 /* Not a number -- a named pipe. */
190 int dir = to.empty ()
191 ? O_RDWR | O_CLOEXEC : O_RDONLY | O_CLOEXEC;
192 fd_from = open (cstr, dir);
194 if (to.empty ())
195 fd_to = fd_from;
198 if (!from.empty () && fd_from < 0)
200 else if (to.empty ())
202 else
204 const char *cstr = to.c_str ();
205 fd_to = strtoul (cstr, &ptr, 10);
206 if (*ptr)
208 /* Not a number, a named pipe. */
209 int dir = from.empty ()
210 ? O_RDWR | O_CLOEXEC : O_WRONLY | O_CLOEXEC;
211 fd_to = open (cstr, dir);
212 if (fd_to < 0)
213 close (fd_from);
215 if (from.empty ())
216 fd_from = fd_to;
220 if (fd_from < 0 || fd_to < 0)
221 errmsg = "opening";
222 else
223 c = new module_client (fd_from, fd_to);
225 break;
227 case '=':
228 // =localsocket
230 int fd = -1;
231 #if CODY_NETWORKING
232 fd = Cody::OpenLocal (&errmsg, name.c_str () + 1);
233 #else
234 errmsg = "disabled";
235 #endif
236 if (fd >= 0)
237 c = new module_client (fd, fd);
239 break;
241 case '|':
242 // |program and args
243 c = spawn_mapper_program (&errmsg, name, full_program_name);
244 break;
246 default:
247 // file or hostname:port
249 auto colon = name.find_last_of (':');
250 if (colon != name.npos)
252 char const *cptr = name.c_str () + colon;
253 char *endp;
254 unsigned port = strtoul (cptr + 1, &endp, 10);
256 if (port && endp != cptr + 1 && !*endp)
258 name[colon] = 0;
259 int fd = -1;
260 #if CODY_NETWORKING
261 fd = Cody::OpenInet6 (&errmsg, name.c_str (), port);
262 #else
263 errmsg = "disabled";
264 #endif
265 name[colon] = ':';
267 if (fd >= 0)
268 c = new module_client (fd, fd);
273 break;
278 if (!c)
280 // Make a default in-process client
281 bool file = !errmsg && !name.empty ();
282 auto r = new module_resolver (!file, true);
284 if (file)
286 int fd = open (name.c_str (), O_RDONLY | O_CLOEXEC);
287 if (fd < 0)
288 errmsg = "opening";
289 else
291 /* Add the mapper file to the dependency tracking. */
292 if (deps)
293 deps_add_dep (deps, name.c_str ());
294 if (int l = r->read_tuple_file (fd, ident, false))
296 if (l > 0)
297 line = l;
298 errmsg = "reading";
301 close (fd);
304 else
305 r->set_repo ("gcm.cache");
307 auto *s = new Cody::Server (r);
308 c = new module_client (s);
311 #ifdef SIGPIPE
312 if (!c->IsDirect ())
313 /* We need to ignore sig pipe for a while. */
314 c->sigpipe = signal (SIGPIPE, SIG_IGN);
315 #endif
317 if (errmsg)
318 error_at (loc, line ? G_("failed %s mapper %qs line %u")
319 : G_("failed %s mapper %qs"), errmsg, name.c_str (), line);
321 // now wave hello!
322 c->Cork ();
323 c->Connect (std::string ("GCC"), ident);
324 c->ModuleRepo ();
325 auto packets = c->Uncork ();
327 auto &connect = packets[0];
328 if (connect.GetCode () == Cody::Client::PC_CONNECT)
329 c->flags = Cody::Flags (connect.GetInteger ());
330 else if (connect.GetCode () == Cody::Client::PC_ERROR)
331 error_at (loc, "failed mapper handshake %s", connect.GetString ().c_str ());
333 auto &repo = packets[1];
334 if (repo.GetCode () == Cody::Client::PC_PATHNAME)
335 set_repo (repo.GetString ().c_str ());
337 return c;
340 void
341 module_client::close_module_client (location_t loc, module_client *mapper)
343 if (mapper->IsDirect ())
345 auto *s = mapper->GetServer ();
346 auto *r = s->GetResolver ();
347 delete s;
348 delete r;
350 else
352 if (mapper->pex)
354 int fd_write = mapper->GetFDWrite ();
355 if (fd_write >= 0)
356 close (fd_write);
358 int status;
359 pex_get_status (mapper->pex, 1, &status);
361 pex_free (mapper->pex);
362 mapper->pex = NULL;
364 if (WIFSIGNALED (status))
365 error_at (loc, "mapper died by signal %s",
366 strsignal (WTERMSIG (status)));
367 else if (WIFEXITED (status) && WEXITSTATUS (status) != 0)
368 error_at (loc, "mapper exit status %d",
369 WEXITSTATUS (status));
371 else
373 int fd_read = mapper->GetFDRead ();
374 close (fd_read);
377 #ifdef SIGPIPE
378 // Restore sigpipe
379 if (mapper->sigpipe != SIG_IGN)
380 signal (SIGPIPE, mapper->sigpipe);
381 #endif
384 delete mapper;