1 /* C++ modules. Experimental!
2 Copyright (C) 2017-2022 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/>. */
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>
27 #define INCLUDE_STRING
28 #define INCLUDE_VECTOR
30 #define INCLUDE_MEMORY
34 #include "diagnostic-core.h"
35 #include "mapper-client.h"
38 #include "../../c++tools/resolver.h"
40 #if !HOST_HAS_O_CLOEXEC
44 module_client::module_client (pex_obj
*p
, int fd_from
, int fd_to
)
45 : Client (fd_from
, fd_to
), pex (p
)
49 static module_client
*
50 spawn_mapper_program (char const **errmsg
, std::string
&name
,
51 char const *full_program_name
)
53 /* Split writable at white-space. No space-containing args for
55 // At most every other char could be an argument
56 char **argv
= new char *[name
.size () / 2 + 2];
58 char *str
= new char[name
.size ()];
59 memcpy (str
, name
.c_str () + 1, name
.size ());
61 for (auto ptr
= str
; ; ++ptr
)
70 /* @name means look in the compiler's install dir. */
74 full_program_name
= nullptr;
78 while (*ptr
&& *ptr
!= ' ')
84 argv
[arg_no
] = nullptr;
86 auto *pex
= pex_init (PEX_USE_PIPES
, progname
, NULL
);
87 FILE *to
= pex_input_pipe (pex
, false);
90 *errmsg
= "connecting input";
93 int flags
= PEX_SEARCH
;
95 if (full_program_name
)
97 /* Prepend the invoking path, if the mapper is a simple
99 size_t dir_len
= progname
- full_program_name
;
101 argv0
.reserve (dir_len
+ name
.size ());
102 argv0
.append (full_program_name
, dir_len
).append (name
);
103 name
= std::move (argv0
);
104 argv
[0] = const_cast <char *> (name
.c_str ());
108 *errmsg
= pex_run (pex
, flags
, argv
[0], argv
, NULL
, NULL
, &err
);
113 int fd_from
= -1, fd_to
= -1;
116 FILE *from
= pex_read_output (pex
, false);
117 if (from
&& (fd_to
= dup (fileno (to
))) >= 0)
118 fd_from
= fileno (from
);
120 *errmsg
= "connecting output";
130 return new module_client (pex
, fd_from
, fd_to
);
134 module_client::open_module_client (location_t loc
, const char *o
,
135 void (*set_repo
) (const char *),
136 char const *full_program_name
)
138 module_client
*c
= nullptr;
141 char const *errmsg
= nullptr;
146 /* Maybe a local or ipv6 address. */
148 auto last
= name
.find_last_of ('?');
149 if (last
!= name
.npos
)
151 ident
= name
.substr (last
+ 1);
160 // <from>to or <>fromto, or <>
162 size_t pos
= name
.find ('>', 1);
163 if (pos
== std::string::npos
)
165 std::string
from (name
, 1, pos
- 1);
167 if (pos
!= name
.size ())
168 to
.append (name
, pos
+ 1, std::string::npos
);
170 int fd_from
= -1, fd_to
= -1;
171 if (from
.empty () && to
.empty ())
173 fd_from
= fileno (stdin
);
174 fd_to
= fileno (stdout
);
181 /* Sadly str::stoul is not portable. */
182 const char *cstr
= from
.c_str ();
183 fd_from
= strtoul (cstr
, &ptr
, 10);
186 /* Not a number -- a named pipe. */
187 int dir
= to
.empty ()
188 ? O_RDWR
| O_CLOEXEC
: O_RDONLY
| O_CLOEXEC
;
189 fd_from
= open (cstr
, dir
);
195 if (!from
.empty () && fd_from
< 0)
197 else if (to
.empty ())
201 const char *cstr
= to
.c_str ();
202 fd_to
= strtoul (cstr
, &ptr
, 10);
205 /* Not a number, a named pipe. */
206 int dir
= from
.empty ()
207 ? O_RDWR
| O_CLOEXEC
: O_WRONLY
| O_CLOEXEC
;
208 fd_to
= open (cstr
, dir
);
217 if (fd_from
< 0 || fd_to
< 0)
220 c
= new module_client (fd_from
, fd_to
);
229 fd
= Cody::OpenLocal (&errmsg
, name
.c_str () + 1);
234 c
= new module_client (fd
, fd
);
240 c
= spawn_mapper_program (&errmsg
, name
, full_program_name
);
244 // file or hostname:port
246 auto colon
= name
.find_last_of (':');
247 if (colon
!= name
.npos
)
249 char const *cptr
= name
.c_str () + colon
;
251 unsigned port
= strtoul (cptr
+ 1, &endp
, 10);
253 if (port
&& endp
!= cptr
+ 1 && !*endp
)
258 fd
= Cody::OpenInet6 (&errmsg
, name
.c_str (), port
);
265 c
= new module_client (fd
, fd
);
277 // Make a default in-process client
278 bool file
= !errmsg
&& !name
.empty ();
279 auto r
= new module_resolver (!file
, true);
283 int fd
= open (name
.c_str (), O_RDONLY
| O_CLOEXEC
);
288 if (int l
= r
->read_tuple_file (fd
, ident
, false))
299 r
->set_repo ("gcm.cache");
301 auto *s
= new Cody::Server (r
);
302 c
= new module_client (s
);
307 /* We need to ignore sig pipe for a while. */
308 c
->sigpipe
= signal (SIGPIPE
, SIG_IGN
);
312 error_at (loc
, line
? G_("failed %s mapper %qs line %u")
313 : G_("failed %s mapper %qs"), errmsg
, name
.c_str (), line
);
317 c
->Connect (std::string ("GCC"), ident
);
319 auto packets
= c
->Uncork ();
321 auto &connect
= packets
[0];
322 if (connect
.GetCode () == Cody::Client::PC_CONNECT
)
323 c
->flags
= Cody::Flags (connect
.GetInteger ());
324 else if (connect
.GetCode () == Cody::Client::PC_ERROR
)
325 error_at (loc
, "failed mapper handshake %s", connect
.GetString ().c_str ());
327 auto &repo
= packets
[1];
328 if (repo
.GetCode () == Cody::Client::PC_PATHNAME
)
329 set_repo (repo
.GetString ().c_str ());
335 module_client::close_module_client (location_t loc
, module_client
*mapper
)
337 if (mapper
->IsDirect ())
339 auto *s
= mapper
->GetServer ();
340 auto *r
= s
->GetResolver ();
348 int fd_write
= mapper
->GetFDWrite ();
353 pex_get_status (mapper
->pex
, 1, &status
);
355 pex_free (mapper
->pex
);
358 if (WIFSIGNALED (status
))
359 error_at (loc
, "mapper died by signal %s",
360 strsignal (WTERMSIG (status
)));
361 else if (WIFEXITED (status
) && WEXITSTATUS (status
) != 0)
362 error_at (loc
, "mapper exit status %d",
363 WEXITSTATUS (status
));
367 int fd_read
= mapper
->GetFDRead ();
373 if (mapper
->sigpipe
!= SIG_IGN
)
374 signal (SIGPIPE
, mapper
->sigpipe
);