/tags/rel-2.0.2/CCache/execute.c

# · C · 286 lines · 202 code · 47 blank · 37 comment · 51 complexity · 3dd4bfd552235dc095d06bc58ca2a282 MD5 · raw file

  1. /*
  2. Copyright (C) Andrew Tridgell 2002
  3. This program is free software; you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation; either version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program; if not, write to the Free Software
  13. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  14. */
  15. #include "ccache.h"
  16. #ifdef _WIN32
  17. char *argvtos(char **argv)
  18. {
  19. int i, len;
  20. char *ptr, *str;
  21. for (i = 0, len = 0; argv[i]; i++) {
  22. len += strlen(argv[i]) + 3;
  23. }
  24. str = ptr = (char *)malloc(len + 1);
  25. if (str == NULL)
  26. return NULL;
  27. for (i = 0; argv[i]; i++) {
  28. len = strlen(argv[i]);
  29. *ptr++ = '"';
  30. memcpy(ptr, argv[i], len);
  31. ptr += len;
  32. *ptr++ = '"';
  33. *ptr++ = ' ';
  34. }
  35. *ptr = 0;
  36. return str;
  37. }
  38. #endif
  39. /*
  40. execute a compiler backend, capturing all output to the given paths
  41. the full path to the compiler to run is in argv[0]
  42. */
  43. int execute(char **argv,
  44. const char *path_stdout,
  45. const char *path_stderr)
  46. {
  47. #ifdef _WIN32
  48. #if 1
  49. PROCESS_INFORMATION pinfo;
  50. STARTUPINFO sinfo;
  51. BOOL ret;
  52. DWORD exitcode;
  53. char *args;
  54. HANDLE fd_out, fd_err;
  55. SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
  56. /* TODO: needs moving after possible exit() below, but before stdout is redirected */
  57. if (ccache_verbose) {
  58. display_execute_args(argv);
  59. }
  60. fd_out = CreateFile(path_stdout, GENERIC_WRITE, 0, &sa, CREATE_ALWAYS,
  61. FILE_ATTRIBUTE_NORMAL, NULL);
  62. if (fd_out == INVALID_HANDLE_VALUE) {
  63. return STATUS_NOCACHE;
  64. }
  65. fd_err = CreateFile(path_stderr, GENERIC_WRITE, 0, &sa, CREATE_ALWAYS,
  66. FILE_ATTRIBUTE_NORMAL, NULL);
  67. if (fd_err == INVALID_HANDLE_VALUE) {
  68. return STATUS_NOCACHE;
  69. }
  70. ZeroMemory(&pinfo, sizeof(PROCESS_INFORMATION));
  71. ZeroMemory(&sinfo, sizeof(STARTUPINFO));
  72. sinfo.cb = sizeof(STARTUPINFO);
  73. sinfo.hStdError = fd_err;
  74. sinfo.hStdOutput = fd_out;
  75. sinfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
  76. sinfo.dwFlags |= STARTF_USESTDHANDLES;
  77. args = argvtos(argv);
  78. ret = CreateProcessA(argv[0], args, NULL, NULL, TRUE, 0, NULL, NULL,
  79. &sinfo, &pinfo);
  80. free(args);
  81. CloseHandle(fd_out);
  82. CloseHandle(fd_err);
  83. if (ret == 0)
  84. return -1;
  85. WaitForSingleObject(pinfo.hProcess, INFINITE);
  86. GetExitCodeProcess(pinfo.hProcess, &exitcode);
  87. CloseHandle(pinfo.hProcess);
  88. CloseHandle(pinfo.hThread);
  89. return exitcode;
  90. #else /* possibly slightly faster */
  91. /* needs fixing to quote commandline options to handle spaces in CCACHE_DIR etc */
  92. int status = -2;
  93. int fd, std_od = -1, std_ed = -1;
  94. /* TODO: needs moving after possible exit() below, but before stdout is redirected */
  95. if (ccache_verbose) {
  96. display_execute_args(argv);
  97. }
  98. unlink(path_stdout);
  99. std_od = _dup(1);
  100. fd = _open(path_stdout, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL|O_BINARY, 0666);
  101. if (fd == -1) {
  102. exit(STATUS_NOCACHE);
  103. }
  104. _dup2(fd, 1);
  105. _close(fd);
  106. unlink(path_stderr);
  107. fd = _open(path_stderr, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL|O_BINARY, 0666);
  108. std_ed = _dup(2);
  109. if (fd == -1) {
  110. exit(STATUS_NOCACHE);
  111. }
  112. _dup2(fd, 2);
  113. _close(fd);
  114. /* Spawn process (_exec* familly doesn't return) */
  115. status = _spawnv(_P_WAIT, argv[0], (const char **)argv);
  116. /* Restore descriptors */
  117. if (std_od != -1) _dup2(std_od, 1);
  118. if (std_ed != -1) _dup2(std_ed, 2);
  119. _flushall();
  120. return (status>0);
  121. #endif
  122. #else
  123. pid_t pid;
  124. int status;
  125. pid = fork();
  126. if (pid == -1) fatal("Failed to fork");
  127. if (pid == 0) {
  128. int fd;
  129. /* TODO: needs moving after possible exit() below, but before stdout is redirected */
  130. if (ccache_verbose) {
  131. display_execute_args(argv);
  132. }
  133. unlink(path_stdout);
  134. fd = open(path_stdout, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL|O_BINARY, 0666);
  135. if (fd == -1) {
  136. exit(STATUS_NOCACHE);
  137. }
  138. dup2(fd, 1);
  139. close(fd);
  140. unlink(path_stderr);
  141. fd = open(path_stderr, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL|O_BINARY, 0666);
  142. if (fd == -1) {
  143. exit(STATUS_NOCACHE);
  144. }
  145. dup2(fd, 2);
  146. close(fd);
  147. exit(execv(argv[0], argv));
  148. }
  149. if (waitpid(pid, &status, 0) != pid) {
  150. fatal("waitpid failed");
  151. }
  152. if (WEXITSTATUS(status) == 0 && WIFSIGNALED(status)) {
  153. return -1;
  154. }
  155. return WEXITSTATUS(status);
  156. #endif
  157. }
  158. /*
  159. find an executable by name in $PATH. Exclude any that are links to exclude_name
  160. */
  161. char *find_executable(const char *name, const char *exclude_name)
  162. {
  163. #if _WIN32
  164. (void)exclude_name;
  165. DWORD ret;
  166. char namebuf[MAX_PATH];
  167. ret = SearchPathA(getenv("CCACHE_PATH"), name, ".exe",
  168. sizeof(namebuf), namebuf, NULL);
  169. if (ret != 0) {
  170. return x_strdup(namebuf);
  171. }
  172. return NULL;
  173. #else
  174. char *path;
  175. char *tok;
  176. struct stat st1, st2;
  177. if (*name == '/') {
  178. return x_strdup(name);
  179. }
  180. path = getenv("CCACHE_PATH");
  181. if (!path) {
  182. path = getenv("PATH");
  183. }
  184. if (!path) {
  185. cc_log("no PATH variable!?\n");
  186. stats_update(STATS_ENVIRONMMENT);
  187. return NULL;
  188. }
  189. path = x_strdup(path);
  190. /* search the path looking for the first compiler of the right name
  191. that isn't us */
  192. for (tok=strtok(path,":"); tok; tok = strtok(NULL, ":")) {
  193. char *fname;
  194. x_asprintf(&fname, "%s/%s", tok, name);
  195. /* look for a normal executable file */
  196. if (access(fname, X_OK) == 0 &&
  197. lstat(fname, &st1) == 0 &&
  198. stat(fname, &st2) == 0 &&
  199. S_ISREG(st2.st_mode)) {
  200. /* if its a symlink then ensure it doesn't
  201. point at something called exclude_name */
  202. if (S_ISLNK(st1.st_mode)) {
  203. char *buf = x_realpath(fname);
  204. if (buf) {
  205. char *p = str_basename(buf);
  206. if (strcmp(p, exclude_name) == 0) {
  207. /* its a link to "ccache" ! */
  208. free(p);
  209. free(buf);
  210. continue;
  211. }
  212. free(buf);
  213. free(p);
  214. }
  215. }
  216. /* found it! */
  217. free(path);
  218. return fname;
  219. }
  220. free(fname);
  221. }
  222. return NULL;
  223. #endif
  224. }
  225. void display_execute_args(char **argv)
  226. {
  227. if (argv) {
  228. printf("ccache executing: ");
  229. while (*argv) {
  230. printf("%s ", *argv);
  231. ++argv;
  232. }
  233. printf("\n");
  234. fflush(stdout);
  235. }
  236. }