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)
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 "rich-location.h"
35 #include "diagnostic-core.h"
36 #include "mapper-client.h"
40 #include "../../c++tools/resolver.h"
42 #if !HOST_HAS_O_CLOEXEC
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
57 // At most every other char could be an argument
58 char **argv
= new char *[name
.size () / 2 + 2];
60 char *str
= new char[name
.size ()];
61 memcpy (str
, name
.c_str () + 1, name
.size ());
63 for (auto ptr
= str
; ; ++ptr
)
72 /* @name means look in the compiler's install dir. */
76 full_program_name
= nullptr;
80 while (*ptr
&& *ptr
!= ' ')
86 argv
[arg_no
] = nullptr;
88 auto *pex
= pex_init (PEX_USE_PIPES
, progname
, NULL
);
89 FILE *to
= pex_input_pipe (pex
, false);
92 *errmsg
= "connecting input";
95 int flags
= PEX_SEARCH
;
97 if (full_program_name
)
99 /* Prepend the invoking path, if the mapper is a simple
101 size_t dir_len
= progname
- full_program_name
;
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 ());
110 *errmsg
= pex_run (pex
, flags
, argv
[0], argv
, NULL
, NULL
, &err
);
115 int fd_from
= -1, fd_to
= -1;
118 FILE *from
= pex_read_output (pex
, false);
119 if (from
&& (fd_to
= dup (fileno (to
))) >= 0)
120 fd_from
= fileno (from
);
122 *errmsg
= "connecting output";
132 return new module_client (pex
, fd_from
, fd_to
);
136 module_client::open_module_client (location_t loc
, const char *o
,
138 void (*set_repo
) (const char *),
139 char const *full_program_name
)
141 module_client
*c
= nullptr;
144 char const *errmsg
= nullptr;
149 /* Maybe a local or ipv6 address. */
151 auto last
= name
.find_last_of ('?');
152 if (last
!= name
.npos
)
154 ident
= name
.substr (last
+ 1);
163 // <from>to or <>fromto, or <>
165 size_t pos
= name
.find ('>', 1);
166 if (pos
== std::string::npos
)
168 std::string
from (name
, 1, pos
- 1);
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
);
184 /* Sadly str::stoul is not portable. */
185 const char *cstr
= from
.c_str ();
186 fd_from
= strtoul (cstr
, &ptr
, 10);
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
);
198 if (!from
.empty () && fd_from
< 0)
200 else if (to
.empty ())
204 const char *cstr
= to
.c_str ();
205 fd_to
= strtoul (cstr
, &ptr
, 10);
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
);
220 if (fd_from
< 0 || fd_to
< 0)
223 c
= new module_client (fd_from
, fd_to
);
232 fd
= Cody::OpenLocal (&errmsg
, name
.c_str () + 1);
237 c
= new module_client (fd
, fd
);
243 c
= spawn_mapper_program (&errmsg
, name
, full_program_name
);
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
;
254 unsigned port
= strtoul (cptr
+ 1, &endp
, 10);
256 if (port
&& endp
!= cptr
+ 1 && !*endp
)
261 fd
= Cody::OpenInet6 (&errmsg
, name
.c_str (), port
);
268 c
= new module_client (fd
, fd
);
280 // Make a default in-process client
281 bool file
= !errmsg
&& !name
.empty ();
282 auto r
= new module_resolver (!file
, true);
286 int fd
= open (name
.c_str (), O_RDONLY
| O_CLOEXEC
);
291 /* Add the mapper file to the dependency tracking. */
293 deps_add_dep (deps
, name
.c_str ());
294 if (int l
= r
->read_tuple_file (fd
, ident
, false))
305 r
->set_repo ("gcm.cache");
307 auto *s
= new Cody::Server (r
);
308 c
= new module_client (s
);
313 /* We need to ignore sig pipe for a while. */
314 c
->sigpipe
= signal (SIGPIPE
, SIG_IGN
);
318 error_at (loc
, line
? G_("failed %s mapper %qs line %u")
319 : G_("failed %s mapper %qs"), errmsg
, name
.c_str (), line
);
323 c
->Connect (std::string ("GCC"), ident
);
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 ());
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 ();
354 int fd_write
= mapper
->GetFDWrite ();
359 pex_get_status (mapper
->pex
, 1, &status
);
361 pex_free (mapper
->pex
);
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
));
373 int fd_read
= mapper
->GetFDRead ();
379 if (mapper
->sigpipe
!= SIG_IGN
)
380 signal (SIGPIPE
, mapper
->sigpipe
);