qmake.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  1. /*+-------------------------------------------------------------------------
  2. qmake.c - quad-make for WHT development
  3. wht@wht.net
  4. Make up to 4 objects at a time ( for yuriatin quad-m88k)
  5. --------------------------------------------------------------------------*/
  6. /*+:EDITS:*/
  7. /*:04-26-2000-11:16-wht@bob-RELEASE 4.42 */
  8. /*:09-02-1998-13:59-wht@menlo-make 8 at a time */
  9. /*:12-15-1997-20:32-wht@fep-fix pause() unreliability */
  10. /*:01-24-1997-02:38-wht@yuriatin-SOURCE RELEASE 4.00 */
  11. /*:09-30-1996-00:52-wht@yuriatin-creation */
  12. #include <stdio.h>
  13. #include <ctype.h>
  14. #include <signal.h>
  15. #include <string.h>
  16. #include <memory.h>
  17. #include <malloc.h>
  18. #include <fcntl.h>
  19. #include <errno.h>
  20. #include "ecu_config.h"
  21. #include "ecu_types.h"
  22. #include "ecu_time.h"
  23. #include "ecu_stat.h"
  24. #include <sys/wait.h>
  25. #include <time.h>
  26. int keep_all_qfiles = 0;
  27. time_t start_epoch;
  28. int next = 0;
  29. typedef struct target
  30. {
  31. char *ofn;
  32. char *qfn;
  33. int status;
  34. int exit_status;
  35. pid_t pid;
  36. time_t start_epoch;
  37. time_t end_epoch;
  38. }
  39. T;
  40. #define STATUS_NOTSTARTED 0
  41. #define STATUS_RUNNING 1
  42. #define STATUS_DONE 2
  43. #define TMAX 1024
  44. #define MMAX 8 /* max makes outstanding */
  45. T t[TMAX];
  46. int tq = 0;
  47. int targets_to_make;
  48. int running;
  49. int in_progress;
  50. #define MAX_EXEC_ARG 10
  51. char *str_token_static = (char *)0;
  52. /*+-------------------------------------------------------------------------
  53. status_text(status)
  54. --------------------------------------------------------------------------*/
  55. char *
  56. status_text(status)
  57. int status;
  58. {
  59. switch (status)
  60. {
  61. case STATUS_NOTSTARTED:
  62. return ("NOTSTARTED");
  63. case STATUS_RUNNING:
  64. return ("RUNNING");
  65. case STATUS_DONE:
  66. return ("DONE");
  67. }
  68. return ("???");
  69. } /* end of status_text */
  70. /*+-----------------------------------------------------------------------
  71. arg_token(parsestr,termchars)
  72. Get next token from string parsestr ((char *)0 on 2nd, 3rd, etc.
  73. calls), where tokens are nonempty strings separated by runs of chars
  74. from termchars. Writes nulls into parsestr to end tokens.
  75. termchars need not remain constant from call to call.
  76. Treats multiple occurrences of a termchar as one delimiter (does not
  77. allow null fields).
  78. ------------------------------------------------------------------------*/
  79. char *
  80. arg_token(parsestr, termchars)
  81. char *parsestr;
  82. char *termchars;
  83. {
  84. char *parseptr;
  85. char *token;
  86. if (!parsestr && !str_token_static)
  87. return ((char *)0);
  88. if (parsestr)
  89. {
  90. str_token_static = (char *)0;
  91. parseptr = parsestr;
  92. }
  93. else
  94. parseptr = str_token_static;
  95. while (*parseptr)
  96. {
  97. if (!strchr(termchars, *parseptr))
  98. break;
  99. parseptr++;
  100. }
  101. if (!*parseptr)
  102. {
  103. str_token_static = (char *)0;
  104. return ((char *)0);
  105. }
  106. token = parseptr;
  107. /*
  108. * tokens beginning with apostrophe or quotes kept together
  109. */
  110. if (*token == '\'')
  111. {
  112. token++;
  113. parseptr++;
  114. while (*parseptr)
  115. {
  116. if (*parseptr == '\'')
  117. {
  118. str_token_static = parseptr + 1;
  119. *parseptr = 0;
  120. return (token);
  121. }
  122. parseptr++;
  123. }
  124. str_token_static = (char *)0;
  125. return (token);
  126. }
  127. else if (*token == '"')
  128. {
  129. token++;
  130. parseptr++;
  131. while (*parseptr)
  132. {
  133. if (*parseptr == '"')
  134. {
  135. str_token_static = parseptr + 1;
  136. *parseptr = 0;
  137. return (token);
  138. }
  139. parseptr++;
  140. }
  141. str_token_static = (char *)0;
  142. return (token);
  143. }
  144. while (*parseptr)
  145. {
  146. if (strchr(termchars, *parseptr))
  147. {
  148. *parseptr = 0;
  149. str_token_static = parseptr + 1;
  150. while (*str_token_static)
  151. {
  152. if (!strchr(termchars, *str_token_static))
  153. break;
  154. str_token_static++;
  155. }
  156. return (token);
  157. }
  158. parseptr++;
  159. }
  160. str_token_static = (char *)0;
  161. return (token);
  162. } /* end of arg_token */
  163. /*+-------------------------------------------------------------------------
  164. build_arg_array(cmd,arg,arg_max_quan,&narg)
  165. --------------------------------------------------------------------------*/
  166. void
  167. build_arg_array(cmd, arg, arg_max_quan, narg_rtn)
  168. char *cmd;
  169. char **arg;
  170. int arg_max_quan;
  171. int *narg_rtn;
  172. {
  173. int narg;
  174. str_token_static = (char *)0;
  175. memset((char *)arg, 0, sizeof(char *) * arg_max_quan);
  176. if (!(arg[0] = arg_token(cmd, " \t\r\n")))
  177. {
  178. *narg_rtn = 0;
  179. return;
  180. }
  181. for (narg = 1; narg < arg_max_quan; ++narg)
  182. {
  183. if (!(arg[narg] = arg_token((char *)0, " \t\r\n")))
  184. break;
  185. }
  186. *narg_rtn = narg;
  187. } /* end of build_arg_array */
  188. /*+-------------------------------------------------------------------------
  189. is_executable(progname)
  190. --------------------------------------------------------------------------*/
  191. int
  192. is_executable(progname)
  193. char *progname;
  194. {
  195. struct stat ss;
  196. if (stat(progname, &ss) < 0) /* if cannot stat, flunk */
  197. return (0);
  198. if ((ss.st_mode & 0111) == 0) /* if no --x--x--x, flunk */
  199. return (0);
  200. if ((ss.st_mode & S_IFMT) != S_IFREG) /* if no --x--x--x, flunk */
  201. return (0);
  202. return (1); /* whew, this OUGHT to work */
  203. } /* end of is_executable */
  204. /*+-------------------------------------------------------------------------
  205. find_executable(progname)
  206. PATH=':/usr/wht/bin:/bin:/usr/bin:/usr/wht/bin:/etc/tuckerware' len=56
  207. --------------------------------------------------------------------------*/
  208. char *
  209. find_executable(progname)
  210. char *progname;
  211. {
  212. int itmp;
  213. static char *path_buf = (char *)0;
  214. #define PATHNAME_QUAN 64
  215. static char *path_name[PATHNAME_QUAN + 1];
  216. static char rtn_path[256];
  217. static int path_count = 0;
  218. char *cp;
  219. char *getenv();
  220. if (path_buf == (char *)0)
  221. {
  222. if ((cp = getenv("PATH")) == (char *)0)
  223. return (cp);
  224. if (!(path_buf = malloc(strlen(cp) + 1)))
  225. return ((char *)0);
  226. strcpy(path_buf, cp);
  227. path_name[PATHNAME_QUAN] = (char *)0;
  228. cp = path_buf;
  229. for (path_count = 0; path_count < PATHNAME_QUAN; path_count++)
  230. {
  231. if (*cp == 0)
  232. break;
  233. path_name[path_count] = cp;
  234. while ((*cp != ':') && (*cp != 0))
  235. cp++;
  236. if (*cp == ':')
  237. *cp++ = 0;
  238. }
  239. } /* end of get and process path env variable */
  240. /* look for executable */
  241. for (itmp = 0; itmp < path_count; itmp++)
  242. {
  243. if (*path_name[itmp] == 0) /* if null path (./) */
  244. strcpy(rtn_path, "./");
  245. else
  246. sprintf(rtn_path, "%s/", path_name[itmp]);
  247. strcat(rtn_path, progname);
  248. if (is_executable(rtn_path))
  249. return (rtn_path);
  250. }
  251. return ((char *)0);
  252. } /* end of find_executable */
  253. /*+-------------------------------------------------------------------------
  254. execute(cmdstr) - execute an arbitrary program with arguments
  255. kills rcvr process if alive and restarts it when done if was alive
  256. --------------------------------------------------------------------------*/
  257. int
  258. execute(cmdstr)
  259. char *cmdstr;
  260. {
  261. char *cmdpath;
  262. char *cmdargv[MAX_EXEC_ARG];
  263. int itmp;
  264. pid_t child_pid;
  265. build_arg_array(cmdstr, cmdargv, MAX_EXEC_ARG, &itmp);
  266. if (itmp == MAX_EXEC_ARG)
  267. {
  268. puts("Too many arguments to command\n");
  269. return (-1);
  270. }
  271. else if (!itmp)
  272. {
  273. puts("null command\n");
  274. return (-1);
  275. }
  276. if (*cmdargv[0] == '/')
  277. {
  278. cmdpath = cmdargv[0];
  279. cmdargv[0] = strrchr(cmdargv[0], '/') + 1;
  280. }
  281. else
  282. {
  283. if ((cmdpath = find_executable(cmdargv[0])) == (char *)0)
  284. {
  285. printf("Cannot find %s\n", cmdargv[0]);
  286. return (-1);
  287. }
  288. }
  289. if ((child_pid = fork()) < 0)
  290. {
  291. puts("Cannot fork\n");
  292. return (-1);
  293. }
  294. if (child_pid == 0) /* we are the spawned (going to call exec) */
  295. {
  296. sleep(1); /* make sure fast systems let parent run again */
  297. execv(cmdpath, cmdargv);
  298. perror(cmdpath);
  299. _exit(255); /* end of spawned process */
  300. } /* end of if child process */
  301. return (child_pid);
  302. } /* end of execute */
  303. /*+-------------------------------------------------------------------------
  304. start_make(cmd)
  305. --------------------------------------------------------------------------*/
  306. pid_t
  307. start_make(cmd)
  308. char *cmd;
  309. {
  310. return (execute(cmd));
  311. } /* end of start_make */
  312. /*+-------------------------------------------------------------------------
  313. estr()
  314. --------------------------------------------------------------------------*/
  315. char *
  316. estr()
  317. {
  318. static char s64[64];
  319. time_t now = time(0);
  320. sprintf(s64, "%10ld", (long)now - (long)start_epoch);
  321. return (s64);
  322. } /* end of estr */
  323. /*+-------------------------------------------------------------------------
  324. die()
  325. --------------------------------------------------------------------------*/
  326. void
  327. die()
  328. {
  329. T *tp;
  330. for (tp = t; tp < (t + tq); tp++)
  331. {
  332. printf("%s %s (%s) %ld secs\n",
  333. tp->ofn,
  334. status_text(tp->status),
  335. (tp->exit_status) ? "FAILED" : "succeeded",
  336. (long)(tp->end_epoch) - (long)(tp->start_epoch));
  337. }
  338. exit(1);
  339. } /* end of die */
  340. /*+-------------------------------------------------------------------------
  341. report()
  342. --------------------------------------------------------------------------*/
  343. void
  344. report()
  345. {
  346. T *tp;
  347. int exit_status = 0;
  348. for (tp = t; tp < (t + tq); tp++)
  349. {
  350. if (tp->exit_status)
  351. {
  352. printf("%s failed exit status %d\n",
  353. tp->ofn, tp->exit_status);
  354. exit_status++;
  355. }
  356. }
  357. printf("qmake: exit status %d\n",exit_status);
  358. exit(exit_status);
  359. } /* end of report */
  360. /*+-------------------------------------------------------------------------
  361. sigint(sig)
  362. --------------------------------------------------------------------------*/
  363. void
  364. sigint(sig)
  365. int sig;
  366. {
  367. #if 0
  368. printf("qmake: signal %d\n",sig);
  369. exit(100+sig);
  370. #else
  371. T *tp;
  372. int exit_status = 100+sig;
  373. signal(SIGINT,SIG_DFL);
  374. signal(SIGQUIT,SIG_DFL);
  375. printf("\ninterrupted\n");
  376. for (tp = t; tp < (t + tq); tp++)
  377. {
  378. char *status = status_text(tp->status);
  379. switch(tp->status)
  380. {
  381. case STATUS_RUNNING:
  382. break;
  383. case STATUS_DONE:
  384. default:
  385. if(sig == SIGINT)
  386. continue;
  387. break;
  388. }
  389. printf("%s %s %d\n", tp->ofn, status, tp->pid);
  390. }
  391. printf("qmake: exit status %d\n",exit_status);
  392. exit(exit_status);
  393. #endif
  394. } /* end of sigint */
  395. /*+-------------------------------------------------------------------------
  396. reap(sig)
  397. --------------------------------------------------------------------------*/
  398. void
  399. reap(sig)
  400. int sig;
  401. {
  402. int stat_loc;
  403. pid_t pid;
  404. int found = 0;
  405. T *tp;
  406. if ((pid = wait(&stat_loc)) < 0)
  407. {
  408. switch (errno)
  409. {
  410. case ECHILD:
  411. printf("!! reap() found ECHILD\n");
  412. die();
  413. default:
  414. perror("wait");
  415. die();
  416. }
  417. }
  418. found = 0;
  419. for (tp = t; tp < (t + next); tp++)
  420. {
  421. if (tp->pid == pid)
  422. {
  423. char s128[128];
  424. tp->end_epoch = time(0);
  425. tp->exit_status = WEXITSTATUS(stat_loc);
  426. tp->status = STATUS_DONE;
  427. printf("%s %s done (%s) %ld secs\n",
  428. estr(), tp->ofn,
  429. (tp->exit_status) ? "FAILED" : "succeeded",
  430. (long)(tp->end_epoch) - (long)(tp->start_epoch));
  431. if(!tp->exit_status && !keep_all_qfiles)
  432. unlink(tp->qfn);
  433. found = 1;
  434. tp->status = STATUS_DONE;
  435. running--;
  436. targets_to_make--;
  437. break;
  438. }
  439. }
  440. if (!found)
  441. {
  442. printf("did not match terminated pid %d\n", pid);
  443. die();
  444. }
  445. signal(SIGCLD, reap);
  446. } /* end of reap */
  447. /*+-------------------------------------------------------------------------
  448. main(argc,argv)
  449. --------------------------------------------------------------------------*/
  450. main(argc, argv)
  451. int argc;
  452. char **argv;
  453. {
  454. int i;
  455. char s256[256];
  456. int errflg = 0;
  457. extern char *optarg;
  458. extern int optind;
  459. setbuf(stdout,0);
  460. setbuf(stderr,0);
  461. while ((i = getopt(argc, argv, "k")) != -1)
  462. {
  463. switch (i)
  464. {
  465. case 'k':
  466. keep_all_qfiles = 1;
  467. break;
  468. case '?':
  469. errflg++;
  470. break;
  471. }
  472. }
  473. if (errflg || (optind == argc))
  474. {
  475. fprintf(stderr, "qmake [-k] o-files...\n");
  476. exit(1);
  477. }
  478. while (optind < argc)
  479. {
  480. t[tq].ofn = strdup(argv[optind]);
  481. sprintf(s256,"%s.q",t[tq].ofn);
  482. t[tq].qfn = strdup(s256);
  483. t[tq].status = 0;
  484. tq++, optind++;
  485. }
  486. targets_to_make = tq;
  487. running = 0;
  488. start_epoch = time(0);
  489. signal(SIGCLD, reap);
  490. signal(SIGINT, sigint);
  491. signal(SIGQUIT, sigint);
  492. while (targets_to_make)
  493. {
  494. struct timeval tv;
  495. /*
  496. * spawn up to MMAX makes
  497. */
  498. while ((running < MMAX) && (next < tq))
  499. {
  500. sprintf(s256, "sh -c 'time make %s > %s 2>&1'",
  501. t[next].ofn, t[next].qfn);
  502. t[next].start_epoch = time(0);
  503. t[next].pid = start_make(s256);
  504. t[next].status = STATUS_RUNNING;
  505. printf("%s %s started pid=%d\n", estr(), t[next].ofn, t[next].pid);
  506. running++;
  507. next++;
  508. }
  509. /*
  510. * now wait on outcomes; pause() seems unreliable
  511. */
  512. memset((char *)&tv,0,sizeof(tv));
  513. tv.tv_usec = 500*1000;
  514. select(0,0,0,0,&tv);
  515. if (!running)
  516. break;
  517. }
  518. report();
  519. } /* end of main */
  520. /* vi: set tabstop=4 shiftwidth=4: */
  521. /* end of qmake.c */