Branch data Line data Source code
1 : : /*
2 : : * Copyright (C) Andrew Tridgell 2002
3 : : * Copyright (C) Joel Rosdahl 2011
4 : : *
5 : : * This program is free software; you can redistribute it and/or modify it
6 : : * under the terms of the GNU General Public License as published by the Free
7 : : * Software Foundation; either version 3 of the License, or (at your option)
8 : : * any later version.
9 : : *
10 : : * This program is distributed in the hope that it will be useful, but WITHOUT
11 : : * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 : : * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 : : * more details.
14 : : *
15 : : * You should have received a copy of the GNU General Public License along with
16 : : * this program; if not, write to the Free Software Foundation, Inc., 51
17 : : * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 : : */
19 : :
20 : : #include "ccache.h"
21 : :
22 : : extern struct conf *conf;
23 : :
24 : : static char *
25 : : find_executable_in_path(const char *name, const char *exclude_name, char *path);
26 : :
27 : : #ifdef _WIN32
28 : : /*
29 : : * Re-create a win32 command line string based on **argv.
30 : : * http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
31 : : */
32 : : char *
33 : : win32argvtos(char *prefix, char **argv)
34 : : {
35 : : char *arg;
36 : : char *ptr;
37 : : char *str;
38 : : int l = 0;
39 : : int i, j;
40 : :
41 : : i = 0;
42 : : arg = prefix ? prefix : argv[i++];
43 : : do {
44 : : int bs = 0;
45 : : for (j = 0; arg[j]; j++) {
46 : : switch (arg[j]) {
47 : : case '\\':
48 : : bs++;
49 : : break;
50 : : case '"':
51 : : bs = (bs << 1) + 1;
52 : : default:
53 : : l += bs + 1;
54 : : bs = 0;
55 : : }
56 : : }
57 : : l += (bs << 1) + 3;
58 : : } while ((arg = argv[i++]));
59 : :
60 : : str = ptr = malloc(l + 1);
61 : : if (!str)
62 : : return NULL;
63 : :
64 : : i = 0;
65 : : arg = prefix ? prefix : argv[i++];
66 : : do {
67 : : int bs = 0;
68 : : *ptr++ = '"';
69 : : for (j = 0; arg[j]; j++) {
70 : : switch (arg[j]) {
71 : : case '\\':
72 : : bs++;
73 : : break;
74 : : case '"':
75 : : bs = (bs << 1) + 1;
76 : : default:
77 : : while (bs && bs--)
78 : : *ptr++ = '\\';
79 : : *ptr++ = arg[j];
80 : : }
81 : : }
82 : : bs <<= 1;
83 : : while (bs && bs--)
84 : : *ptr++ = '\\';
85 : : *ptr++ = '"';
86 : : *ptr++ = ' ';
87 : : } while ((arg = argv[i++]));
88 : : ptr[-1] = '\0';
89 : :
90 : : return str;
91 : : }
92 : :
93 : : char *
94 : : win32getshell(char *path)
95 : : {
96 : : char *path_env;
97 : : char *sh = NULL;
98 : : const char *ext;
99 : :
100 : : ext = get_extension(path);
101 : : if (ext && strcasecmp(ext, ".sh") == 0 && (path_env = getenv("PATH")))
102 : : sh = find_executable_in_path("sh.exe", NULL, path_env);
103 : : if (!sh && getenv("CCACHE_DETECT_SHEBANG")) {
104 : : /* Detect shebang. */
105 : : FILE *fp;
106 : : fp = fopen(path, "r");
107 : : if (fp) {
108 : : char buf[10];
109 : : fgets(buf, sizeof(buf), fp);
110 : : buf[9] = 0;
111 : : if (str_eq(buf, "#!/bin/sh") && (path_env = getenv("PATH")))
112 : : sh = find_executable_in_path("sh.exe", NULL, path_env);
113 : : fclose(fp);
114 : : }
115 : : }
116 : :
117 : : return sh;
118 : : }
119 : :
120 : : void add_exe_ext_if_no_to_fullpath(char *full_path_win_ext, size_t max_size,
121 : : const char *ext, const char *path) {
122 : : if (!ext || (!str_eq(".exe", ext)
123 : : && !str_eq(".bat", ext)
124 : : && !str_eq(".EXE", ext)
125 : : && !str_eq(".BAT", ext))) {
126 : : snprintf(full_path_win_ext, max_size, "%s.exe", path);
127 : : } else {
128 : : snprintf(full_path_win_ext, max_size, "%s", path);
129 : : }
130 : : }
131 : :
132 : : int
133 : : win32execute(char *path, char **argv, int doreturn,
134 : : int fd_stdout, int fd_stderr)
135 : : {
136 : : PROCESS_INFORMATION pi;
137 : : STARTUPINFO si;
138 : : BOOL ret;
139 : : DWORD exitcode;
140 : : char *sh = NULL;
141 : : char *args;
142 : :
143 : : memset(&pi, 0x00, sizeof(pi));
144 : : memset(&si, 0x00, sizeof(si));
145 : :
146 : : sh = win32getshell(path);
147 : : if (sh)
148 : : path = sh;
149 : :
150 : : si.cb = sizeof(STARTUPINFO);
151 : : if (fd_stdout != -1) {
152 : : si.hStdOutput = (HANDLE)_get_osfhandle(fd_stdout);
153 : : si.hStdError = (HANDLE)_get_osfhandle(fd_stderr);
154 : : si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
155 : : si.dwFlags = STARTF_USESTDHANDLES;
156 : : if (si.hStdOutput == INVALID_HANDLE_VALUE
157 : : || si.hStdError == INVALID_HANDLE_VALUE) {
158 : : return -1;
159 : : }
160 : : } else {
161 : : /* redirect subprocess stdout, stderr into current process */
162 : : si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
163 : : si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
164 : : si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
165 : : si.dwFlags = STARTF_USESTDHANDLES;
166 : : if (si.hStdOutput == INVALID_HANDLE_VALUE
167 : : || si.hStdError == INVALID_HANDLE_VALUE) {
168 : : return -1;
169 : : }
170 : : }
171 : : args = win32argvtos(sh, argv);
172 : :
173 : : const char *ext = strrchr(path, '.');
174 : : char full_path_win_ext[MAX_PATH] = {0};
175 : : add_exe_ext_if_no_to_fullpath(full_path_win_ext, MAX_PATH, ext, path);
176 : : ret = CreateProcess(full_path_win_ext, args, NULL, NULL, 1, 0, NULL, NULL,
177 : : &si, &pi);
178 : : if (fd_stdout != -1) {
179 : : close(fd_stdout);
180 : : close(fd_stderr);
181 : : }
182 : : free(args);
183 : : if (ret == 0) {
184 : : LPVOID lpMsgBuf;
185 : : LPVOID lpDisplayBuf;
186 : : DWORD dw = GetLastError();
187 : :
188 : : FormatMessage(
189 : : FORMAT_MESSAGE_ALLOCATE_BUFFER |
190 : : FORMAT_MESSAGE_FROM_SYSTEM |
191 : : FORMAT_MESSAGE_IGNORE_INSERTS,
192 : : NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf,
193 : : 0, NULL);
194 : :
195 : : lpDisplayBuf =
196 : : (LPVOID) LocalAlloc(LMEM_ZEROINIT,
197 : : (lstrlen((LPCTSTR) lpMsgBuf)
198 : : + lstrlen((LPCTSTR) __FILE__) + 200)
199 : : * sizeof(TCHAR));
200 : : _snprintf((LPTSTR) lpDisplayBuf,
201 : : LocalSize(lpDisplayBuf) / sizeof(TCHAR),
202 : : TEXT("%s failed with error %d: %s"), __FILE__, dw, lpMsgBuf);
203 : :
204 : : cc_log("can't execute %s; OS returned error: %s",
205 : : full_path_win_ext, (char*)lpDisplayBuf);
206 : :
207 : : LocalFree(lpMsgBuf);
208 : : LocalFree(lpDisplayBuf);
209 : :
210 : : return -1;
211 : : }
212 : : WaitForSingleObject(pi.hProcess, INFINITE);
213 : : GetExitCodeProcess(pi.hProcess, &exitcode);
214 : : CloseHandle(pi.hProcess);
215 : : CloseHandle(pi.hThread);
216 : : if (!doreturn)
217 : : x_exit(exitcode);
218 : : return exitcode;
219 : : }
220 : :
221 : : #else
222 : :
223 : : /*
224 : : execute a compiler backend, capturing all output to the given paths
225 : : the full path to the compiler to run is in argv[0]
226 : : */
227 : : int
228 : 2 : execute(char **argv, int fd_out, int fd_err)
229 : : {
230 : : pid_t pid;
231 : : int status;
232 : :
233 : 2 : cc_log_argv("Executing ", argv);
234 : 2 : pid = fork();
235 [ - + ]: 4 : if (pid == -1) {
236 : 0 : fatal("Failed to fork: %s", strerror(errno));
237 : : }
238 : :
239 [ + + ]: 4 : if (pid == 0) {
240 : : /* Child. */
241 : 2 : dup2(fd_out, 1);
242 : 2 : close(fd_out);
243 : 2 : dup2(fd_err, 2);
244 : 2 : close(fd_err);
245 : 2 : x_exit(execv(argv[0], argv));
246 : : }
247 : :
248 : 2 : close(fd_out);
249 : 2 : close(fd_err);
250 : :
251 [ - + ]: 2 : if (waitpid(pid, &status, 0) != pid) {
252 : 0 : fatal("waitpid failed: %s", strerror(errno));
253 : : }
254 : :
255 [ + - ][ - + ]: 2 : if (WEXITSTATUS(status) == 0 && WIFSIGNALED(status)) {
256 : 0 : return -1;
257 : : }
258 : :
259 : 2 : return WEXITSTATUS(status);
260 : : }
261 : : #endif
262 : :
263 : :
264 : : /*
265 : : * Find an executable by name in $PATH. Exclude any that are links to
266 : : * exclude_name.
267 : : */
268 : : char *
269 : 1 : find_executable(const char *name, const char *exclude_name)
270 : : {
271 : : char *path;
272 : :
273 [ - + ]: 1 : if (is_absolute_path(name)) {
274 : 0 : return x_strdup(name);
275 : : }
276 : :
277 : 1 : path = conf->path;
278 [ + - ]: 1 : if (str_eq(path, "")) {
279 : 1 : path = getenv("PATH");
280 : : }
281 [ - + ]: 1 : if (!path) {
282 : 0 : cc_log("No PATH variable");
283 : 0 : return NULL;
284 : : }
285 : :
286 : 1 : return find_executable_in_path(name, exclude_name, path);
287 : : }
288 : :
289 : : static char *
290 : 1 : find_executable_in_path(const char *name, const char *exclude_name, char *path)
291 : : {
292 : 1 : char *tok, *saveptr = NULL;
293 : :
294 : 1 : path = x_strdup(path);
295 : :
296 : : /* search the path looking for the first compiler of the right name
297 : : that isn't us */
298 [ + - ]: 2 : for (tok = strtok_r(path, PATH_DELIM, &saveptr);
299 : : tok;
300 : 1 : tok = strtok_r(NULL, PATH_DELIM, &saveptr)) {
301 : : #ifdef _WIN32
302 : : char namebuf[MAX_PATH];
303 : : int ret = SearchPath(tok, name, NULL,
304 : : sizeof(namebuf), namebuf, NULL);
305 : : if (!ret) {
306 : : char *exename = format("%s.exe", name);
307 : : ret = SearchPath(tok, exename, NULL,
308 : : sizeof(namebuf), namebuf, NULL);
309 : : free(exename);
310 : : }
311 : : (void) exclude_name;
312 : : if (ret) {
313 : : free(path);
314 : : return x_strdup(namebuf);
315 : : }
316 : : #else
317 : : struct stat st1, st2;
318 : 2 : char *fname = format("%s/%s", tok, name);
319 : : /* look for a normal executable file */
320 [ + + + - : 4 : if (access(fname, X_OK) == 0 &&
+ - ][ + - ]
321 : 1 : lstat(fname, &st1) == 0 &&
322 : 1 : stat(fname, &st2) == 0 &&
323 : 1 : S_ISREG(st2.st_mode)) {
324 [ - + ]: 1 : if (S_ISLNK(st1.st_mode)) {
325 : 0 : char *buf = x_realpath(fname);
326 [ # # ]: 0 : if (buf) {
327 : 0 : char *p = basename(buf);
328 [ # # ]: 0 : if (str_eq(p, exclude_name)) {
329 : : /* It's a link to "ccache"! */
330 : 0 : free(p);
331 : 0 : free(buf);
332 : 0 : continue;
333 : : }
334 : 0 : free(buf);
335 : 0 : free(p);
336 : : }
337 : : }
338 : :
339 : : /* Found it! */
340 : 1 : free(path);
341 : 1 : return fname;
342 : : }
343 : 1 : free(fname);
344 : : #endif
345 : : }
346 : :
347 : 0 : free(path);
348 : 1 : return NULL;
349 : : }
350 : :
351 : : void
352 : 3 : print_command(FILE *fp, char **argv)
353 : : {
354 : : int i;
355 [ + + ]: 15 : for (i = 0; argv[i]; i++) {
356 [ + + ]: 12 : fprintf(fp, "%s%s", (i == 0) ? "" : " ", argv[i]);
357 : : }
358 : 3 : fprintf(fp, "\n");
359 : 3 : }
|