1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #ifdef TEAMWARE_MAKE_CMN
27
28 /*
29 * parallel.cc
30 *
31 * Deal with the parallel processing
32 */
33
34 /*
35 * Included files
36 */
37 #ifdef DISTRIBUTED
38 #include <avo/strings.h> /* AVO_STRDUP() */
39 #include <dm/Avo_DoJobMsg.h>
40 #include <dm/Avo_MToolJobResultMsg.h>
41 #endif
42 #include <errno.h> /* errno */
43 #include <fcntl.h>
44 #include <avo/util.h> /* avo_get_user(), avo_hostname() */
45 #include <mk/defs.h>
46 #include <mksh/dosys.h> /* redirect_io() */
47 #include <mksh/macro.h> /* expand_value() */
48 #include <mksh/misc.h> /* getmem() */
49 #include <sys/signal.h>
50 #include <sys/stat.h>
51 #include <sys/types.h>
52 #include <sys/utsname.h>
53 #include <sys/wait.h>
54 #include <unistd.h>
55
56
57
58 /*
59 * Defined macros
60 */
61 #define MAXRULES 100
62
63 /*
64 * This const should be in avo_dms/include/AvoDmakeCommand.h
65 */
66 const int local_host_mask = 0x20;
67
68
69 /*
70 * typedefs & structs
71 */
72
73
74 /*
75 * Static variables
76 */
77 #ifdef TEAMWARE_MAKE_CMN
78 static Boolean just_did_subtree = false;
79 static char local_host[MAXNAMELEN] = "";
80 static char user_name[MAXNAMELEN] = "";
81 #endif
82 static int pmake_max_jobs = 0;
83 static pid_t process_running = -1;
84 static Running *running_tail = &running_list;
85 static Name subtree_conflict;
86 static Name subtree_conflict2;
87
88
89 /*
90 * File table of contents
91 */
92 #ifdef DISTRIBUTED
93 static void append_dmake_cmd(Avo_DoJobMsg *dmake_job_msg, char *orig_cmd_line, int cmd_options);
94 static void append_job_result_msg(Avo_MToolJobResultMsg *msg, char *outFn, char *errFn);
95 static void send_job_result_msg(Running rp);
96 #endif
97 static void delete_running_struct(Running rp);
98 static Boolean dependency_conflict(Name target);
99 static Doname distribute_process(char **commands, Property line);
100 static void doname_subtree(Name target, Boolean do_get, Boolean implicit);
101 static void dump_out_file(char *filename, Boolean err);
102 static void finish_doname(Running rp);
103 static void maybe_reread_make_state(void);
104 static void process_next(void);
105 static void reset_conditionals(int cnt, Name *targets, Property *locals);
106 static pid_t run_rule_commands(char *host, char **commands);
107 static Property *set_conditionals(int cnt, Name *targets);
108 static void store_conditionals(Running rp);
109
110
111 /*
112 * execute_parallel(line, waitflg)
113 *
114 * DMake 2.x:
115 * parallel mode: spawns a parallel process to execute the command group.
116 * distributed mode: sends the command group down the pipe to rxm.
117 *
118 * Return value:
119 * The result of the execution
120 *
121 * Parameters:
122 * line The command group to execute
123 */
124 Doname
125 execute_parallel(Property line, Boolean waitflg, Boolean local)
126 {
127 int argcnt;
128 int cmd_options = 0;
129 char *commands[MAXRULES + 5];
130 char *cp;
131 #ifdef DISTRIBUTED
132 Avo_DoJobMsg *dmake_job_msg = NULL;
133 #endif
134 Name dmake_name;
135 Name dmake_value;
136 int ignore;
137 Name make_machines_name;
138 char **p;
139 Property prop;
140 Doname result = build_ok;
141 Cmd_line rule;
142 Boolean silent_flag;
143 Name target = line->body.line.target;
144 Boolean wrote_state_file = false;
145
146 if ((pmake_max_jobs == 0) &&
147 (dmake_mode_type == parallel_mode)) {
148 if (user_name[0] == '\0') {
149 avo_get_user(user_name, NULL);
150 }
151 if (local_host[0] == '\0') {
152 strcpy(local_host, avo_hostname());
153 }
154 MBSTOWCS(wcs_buffer, NOCATGETS("DMAKE_MAX_JOBS"));
155 dmake_name = GETNAME(wcs_buffer, FIND_LENGTH);
156 if (((prop = get_prop(dmake_name->prop, macro_prop)) != NULL) &&
157 ((dmake_value = prop->body.macro.value) != NULL)) {
158 pmake_max_jobs = atoi(dmake_value->string_mb);
159 if (pmake_max_jobs <= 0) {
160 warning(catgets(catd, 1, 308, "DMAKE_MAX_JOBS cannot be less than or equal to zero."));
161 warning(catgets(catd, 1, 309, "setting DMAKE_MAX_JOBS to %d."), PMAKE_DEF_MAX_JOBS);
162 pmake_max_jobs = PMAKE_DEF_MAX_JOBS;
163 }
164 } else {
165 /*
166 * For backwards compatibility w/ PMake 1.x, when
167 * DMake 2.x is being run in parallel mode, DMake
168 * should parse the PMake startup file
169 * $(HOME)/.make.machines to get the pmake_max_jobs.
170 */
171 MBSTOWCS(wcs_buffer, NOCATGETS("PMAKE_MACHINESFILE"));
172 dmake_name = GETNAME(wcs_buffer, FIND_LENGTH);
173 if (((prop = get_prop(dmake_name->prop, macro_prop)) != NULL) &&
174 ((dmake_value = prop->body.macro.value) != NULL)) {
175 make_machines_name = dmake_value;
176 } else {
177 make_machines_name = NULL;
178 }
179 if ((pmake_max_jobs = read_make_machines(make_machines_name)) <= 0) {
180 pmake_max_jobs = PMAKE_DEF_MAX_JOBS;
181 }
182 }
183 #ifdef DISTRIBUTED
184 if (send_mtool_msgs) {
185 send_rsrc_info_msg(pmake_max_jobs, local_host, user_name);
186 }
187 #endif
188 }
189
190 if ((dmake_mode_type == serial_mode) ||
191 ((dmake_mode_type == parallel_mode) && (waitflg))) {
192 return (execute_serial(line));
193 }
194
195 #ifdef DISTRIBUTED
196 if (dmake_mode_type == distributed_mode) {
197 if(local) {
198 // return (execute_serial(line));
199 waitflg = true;
200 }
201 dmake_job_msg = new Avo_DoJobMsg();
202 dmake_job_msg->setJobId(++job_msg_id);
203 dmake_job_msg->setTarget(target->string_mb);
204 dmake_job_msg->setImmediateOutput(0);
205 called_make = false;
206 } else
207 #endif
208 {
209 p = commands;
210 }
211
212 argcnt = 0;
213 for (rule = line->body.line.command_used;
214 rule != NULL;
215 rule = rule->next) {
216 if (posix && (touch || quest) && !rule->always_exec) {
217 continue;
218 }
219 if (vpath_defined) {
220 rule->command_line =
221 vpath_translation(rule->command_line);
222 }
223 if (dmake_mode_type == distributed_mode) {
224 cmd_options = 0;
225 if(local) {
226 cmd_options |= local_host_mask;
227 }
228 } else {
229 silent_flag = false;
230 ignore = 0;
231 }
232 if (rule->command_line->hash.length > 0) {
233 if (++argcnt == MAXRULES) {
234 if (dmake_mode_type == distributed_mode) {
235 /* XXX - tell rxm to execute on local host. */
236 /* I WAS HERE!!! */
237 } else {
238 /* Too many rules, run serially instead. */
239 return build_serial;
240 }
241 }
242 #ifdef DISTRIBUTED
243 if (dmake_mode_type == distributed_mode) {
244 /*
245 * XXX - set assign_mask to tell rxm
246 * to do the following.
247 */
248 /* From execute_serial():
249 if (rule->assign) {
250 result = build_ok;
251 do_assign(rule->command_line, target);
252 */
253 if (0) {
254 } else if (report_dependencies_level == 0) {
255 if (rule->ignore_error) {
256 cmd_options |= ignore_mask;
257 }
258 if (rule->silent) {
259 cmd_options |= silent_mask;
260 }
261 if (rule->command_line->meta) {
262 cmd_options |= meta_mask;
263 }
264 if (rule->make_refd) {
265 cmd_options |= make_refd_mask;
266 }
267 if (do_not_exec_rule) {
268 cmd_options |= do_not_exec_mask;
269 }
270 append_dmake_cmd(dmake_job_msg,
271 rule->command_line->string_mb,
272 cmd_options);
273 /* Copying dosys()... */
274 if (rule->make_refd) {
275 if (waitflg) {
276 dmake_job_msg->setImmediateOutput(1);
277 }
278 called_make = true;
279 if (command_changed &&
280 !wrote_state_file) {
281 write_state_file(0, false);
282 wrote_state_file = true;
283 }
284 }
285 }
286 } else
287 #endif
288 {
289 if (rule->silent && !silent) {
290 silent_flag = true;
291 }
292 if (rule->ignore_error) {
293 ignore++;
294 }
295 /* XXX - need to add support for + prefix */
296 if (silent_flag || ignore) {
297 *p = getmem((silent_flag ? 1 : 0) +
298 ignore +
299 (strlen(rule->
300 command_line->
301 string_mb)) +
302 1);
303 cp = *p++;
304 if (silent_flag) {
305 *cp++ = (int) at_char;
306 }
307 if (ignore) {
308 *cp++ = (int) hyphen_char;
309 }
310 (void) strcpy(cp, rule->command_line->string_mb);
311 } else {
312 *p++ = rule->command_line->string_mb;
313 }
314 }
315 }
316 }
317 if ((argcnt == 0) ||
318 (report_dependencies_level > 0)) {
319 #ifdef DISTRIBUTED
320 if (dmake_job_msg) {
321 delete dmake_job_msg;
322 }
323 #endif
324 return build_ok;
325 }
326 #ifdef DISTRIBUTED
327 if (dmake_mode_type == distributed_mode) {
328 // Send a DoJob message to the rxm process.
329 distribute_rxm(dmake_job_msg);
330
331 // Wait for an acknowledgement.
332 Avo_AcknowledgeMsg *ackMsg = getAcknowledgeMsg();
333 if (ackMsg) {
334 delete ackMsg;
335 }
336
337 if (waitflg) {
338 // Wait for, and process a job result.
339 result = await_dist(waitflg);
340 if (called_make) {
341 maybe_reread_make_state();
342 }
343 check_state(temp_file_name);
344 if (result == build_failed) {
345 if (!continue_after_error) {
346
347 #ifdef PRINT_EXIT_STATUS
348 warning(NOCATGETS("I'm in execute_parallel. await_dist() returned build_failed"));
349 #endif
350
351 fatal(catgets(catd, 1, 252, "Command failed for target `%s'"),
352 target->string_mb);
353 }
354 /*
355 * Make sure a failing command is not
356 * saved in .make.state.
357 */
358 line->body.line.command_used = NULL;
359 }
360 if (temp_file_name != NULL) {
361 free_name(temp_file_name);
362 }
363 temp_file_name = NULL;
364 Property spro = get_prop(sunpro_dependencies->prop, macro_prop);
365 if(spro != NULL) {
366 Name val = spro->body.macro.value;
367 if(val != NULL) {
368 free_name(val);
369 spro->body.macro.value = NULL;
370 }
371 }
372 spro = get_prop(sunpro_dependencies->prop, env_mem_prop);
373 if(spro) {
374 char *val = spro->body.env_mem.value;
375 if(val != NULL) {
376 retmem_mb(val);
377 spro->body.env_mem.value = NULL;
378 }
379 }
380 return result;
381 } else {
382 parallel_process_cnt++;
383 return build_running;
384 }
385 } else
386 #endif
387 {
388 *p = NULL;
389
390 Doname res = distribute_process(commands, line);
391 if (res == build_running) {
392 parallel_process_cnt++;
393 }
394
395 /*
396 * Return only those memory that were specially allocated
397 * for part of commands.
398 */
399 for (int i = 0; commands[i] != NULL; i++) {
400 if ((commands[i][0] == (int) at_char) ||
401 (commands[i][0] == (int) hyphen_char)) {
402 retmem_mb(commands[i]);
403 }
404 }
405 return res;
406 }
407 }
408
409 #ifdef DISTRIBUTED
410 /*
411 * append_dmake_cmd()
412 *
413 * Replaces all escaped newline's (\<cr>)
414 * in the original command line with space's,
415 * then append the new command line to the DoJobMsg object.
416 */
417 static void
418 append_dmake_cmd(Avo_DoJobMsg *dmake_job_msg,
419 char *orig_cmd_line,
420 int cmd_options)
421 {
422 /*
423 Avo_DmakeCommand *tmp_dmake_command;
424
425 tmp_dmake_command = new Avo_DmakeCommand(orig_cmd_line, cmd_options);
426 dmake_job_msg->appendCmd(tmp_dmake_command);
427 delete tmp_dmake_command;
428 */
429 dmake_job_msg->appendCmd(new Avo_DmakeCommand(orig_cmd_line, cmd_options));
430 }
431 #endif
432
433 #ifdef TEAMWARE_MAKE_CMN
434 #define MAXJOBS_ADJUST_RFE4694000
435
436 #ifdef MAXJOBS_ADJUST_RFE4694000
437
438 #include <unistd.h> /* sysconf(_SC_NPROCESSORS_ONLN) */
439 #include <sys/ipc.h> /* ftok() */
440 #include <sys/shm.h> /* shmget(), shmat(), shmdt(), shmctl() */
441 #include <semaphore.h> /* sem_init(), sem_trywait(), sem_post(), sem_destroy() */
442 #include <sys/loadavg.h> /* getloadavg() */
443
444 /*
445 * adjust_pmake_max_jobs (int pmake_max_jobs)
446 *
447 * Parameters:
448 * pmake_max_jobs - max jobs limit set by user
449 *
450 * External functions used:
451 * sysconf()
452 * getloadavg()
453 */
454 static int
455 adjust_pmake_max_jobs (int pmake_max_jobs)
456 {
457 static int ncpu = 0;
458 double loadavg[3];
459 int adjustment;
460 int adjusted_max_jobs;
461
462 if (ncpu <= 0) {
463 if ((ncpu = sysconf(_SC_NPROCESSORS_ONLN)) <= 0) {
464 ncpu = 1;
465 }
466 }
467 if (getloadavg(loadavg, 3) != 3) return(pmake_max_jobs);
468 adjustment = ((int)loadavg[LOADAVG_1MIN]);
469 if (adjustment < 2) return(pmake_max_jobs);
470 if (ncpu > 1) {
471 adjustment = adjustment / ncpu;
472 }
473 adjusted_max_jobs = pmake_max_jobs - adjustment;
474 if (adjusted_max_jobs < 1) adjusted_max_jobs = 1;
475 return(adjusted_max_jobs);
476 }
477
478 /*
479 * M2 adjust mode data and functions
480 *
481 * m2_init() - initializes M2 shared semaphore
482 * m2_acquire_job() - decrements M2 semaphore counter
483 * m2_release_job() - increments M2 semaphore counter
484 * m2_fini() - destroys M2 semaphore and shared memory*
485 *
486 * Environment variables:
487 * __DMAKE_M2_FILE__
488 *
489 * External functions:
490 * ftok(), shmget(), shmat(), shmdt(), shmctl()
491 * sem_init(), sem_trywait(), sem_post(), sem_destroy()
492 * creat(), close(), unlink()
493 * getenv(), putenv()
494 *
495 * Static variables:
496 * m2_file - tmp file name to create ipc key for shared memory
497 * m2_shm_id - shared memory id
498 * m2_shm_sem - shared memory semaphore
499 */
500
501 static char m2_file[MAXPATHLEN];
502 static int m2_shm_id = -1;
503 static sem_t* m2_shm_sem = 0;
504
505 static int
506 m2_init() {
507 char *var;
508 key_t key;
509
510 if ((var = getenv(NOCATGETS("__DMAKE_M2_FILE__"))) == 0) {
511 /* compose tmp file name */
512 sprintf(m2_file, NOCATGETS("%s/dmake.m2.%d.XXXXXX"), tmpdir, getpid());
513
514 /* create tmp file */
515 int fd = mkstemp(m2_file);
516 if (fd < 0) {
517 return -1;
518 } else {
519 close(fd);
520 }
521 } else {
522 /* using existing semaphore */
523 strcpy(m2_file, var);
524 }
525
526 /* combine IPC key */
527 if ((key = ftok(m2_file, 38)) == (key_t) -1) {
528 return -1;
529 }
530
531 /* create shared memory */
532 if ((m2_shm_id = shmget(key, sizeof(*m2_shm_sem), 0666 | (var ? 0 : IPC_CREAT|IPC_EXCL))) == -1) {
533 return -1;
534 }
535
536 /* attach shared memory */
537 if ((m2_shm_sem = (sem_t*) shmat(m2_shm_id, 0, 0666)) == (sem_t*)-1) {
538 return -1;
539 }
540
541 /* root process */
542 if (var == 0) {
543 /* initialize semaphore */
544 if (sem_init(m2_shm_sem, 1, pmake_max_jobs)) {
545 return -1;
546 }
547
548 /* alloc memory for env variable */
549 if ((var = (char*) malloc(MAXPATHLEN)) == 0) {
550 return -1;
551 }
552
553 /* put key to env */
554 sprintf(var, NOCATGETS("__DMAKE_M2_FILE__=%s"), m2_file);
555 if (putenv(var)) {
556 return -1;
557 }
558 }
559 return 0;
560 }
561
562 static void
563 m2_fini() {
564 if (m2_shm_id >= 0) {
565 struct shmid_ds stat;
566
567 /* determine the number of attached processes */
568 if (shmctl(m2_shm_id, IPC_STAT, &stat) == 0) {
569 if (stat.shm_nattch <= 1) {
570 /* destroy semaphore */
571 if (m2_shm_sem != 0) {
572 (void) sem_destroy(m2_shm_sem);
573 }
574
575 /* destroy shared memory */
576 (void) shmctl(m2_shm_id, IPC_RMID, &stat);
577
578 /* remove tmp file created for the key */
579 (void) unlink(m2_file);
580 } else {
581 /* detach shared memory */
582 if (m2_shm_sem != 0) {
583 (void) shmdt((char*) m2_shm_sem);
584 }
585 }
586 }
587
588 m2_shm_id = -1;
589 m2_shm_sem = 0;
590 }
591 }
592
593 static int
594 m2_acquire_job() {
595 if ((m2_shm_id >= 0) && (m2_shm_sem != 0)) {
596 if (sem_trywait(m2_shm_sem) == 0) {
597 return 1;
598 }
599 if (errno == EAGAIN) {
600 return 0;
601 }
602 }
603 return -1;
604 }
605
606 static int
607 m2_release_job() {
608 if ((m2_shm_id >= 0) && (m2_shm_sem != 0)) {
609 if (sem_post(m2_shm_sem) == 0) {
610 return 0;
611 }
612 }
613 return -1;
614 }
615
616 /*
617 * job adjust mode
618 *
619 * Possible values:
620 * ADJUST_M1 - adjustment by system load (default)
621 * ADJUST_M2 - fixed limit of jobs for the group of nested dmakes
622 * ADJUST_NONE - no adjustment - fixed limit of jobs for the current dmake
623 */
624 static enum {
625 ADJUST_UNKNOWN,
626 ADJUST_M1,
627 ADJUST_M2,
628 ADJUST_NONE
629 } job_adjust_mode = ADJUST_UNKNOWN;
630
631 /*
632 * void job_adjust_fini()
633 *
634 * Description:
635 * Cleans up job adjust data.
636 *
637 * Static variables:
638 * job_adjust_mode Current job adjust mode
639 */
640 void
641 job_adjust_fini() {
642 if (job_adjust_mode == ADJUST_M2) {
643 m2_fini();
644 }
645 }
646
647 /*
648 * void job_adjust_error()
649 *
650 * Description:
651 * Prints warning message, cleans up job adjust data, and disables job adjustment
652 *
653 * Environment:
654 * DMAKE_ADJUST_MAX_JOBS
655 *
656 * External functions:
657 * putenv()
658 *
659 * Static variables:
660 * job_adjust_mode Current job adjust mode
661 */
662 static void
663 job_adjust_error() {
664 if (job_adjust_mode != ADJUST_NONE) {
665 /* cleanup internals */
666 job_adjust_fini();
667
668 /* warning message for the user */
669 warning(catgets(catd, 1, 339, "Encountered max jobs auto adjustment error - disabling auto adjustment."));
670
671 /* switch off job adjustment for the children */
672 putenv(NOCATGETS("DMAKE_ADJUST_MAX_JOBS=NO"));
673
674 /* and for this dmake */
675 job_adjust_mode = ADJUST_NONE;
676 }
677 }
678
679 /*
680 * void job_adjust_init()
681 *
682 * Description:
683 * Parses DMAKE_ADJUST_MAX_JOBS env variable
684 * and performs appropriate initializations.
685 *
686 * Environment:
687 * DMAKE_ADJUST_MAX_JOBS
688 * DMAKE_ADJUST_MAX_JOBS == "NO" - no adjustment
689 * DMAKE_ADJUST_MAX_JOBS == "M2" - M2 adjust mode
690 * other - M1 adjust mode
691 *
692 * External functions:
693 * getenv()
694 *
695 * Static variables:
696 * job_adjust_mode Current job adjust mode
697 */
698 static void
699 job_adjust_init() {
700 if (job_adjust_mode == ADJUST_UNKNOWN) {
701 /* default mode */
702 job_adjust_mode = ADJUST_M1;
703
704 /* determine adjust mode */
705 if (char *var = getenv(NOCATGETS("DMAKE_ADJUST_MAX_JOBS"))) {
706 if (strcasecmp(var, NOCATGETS("NO")) == 0) {
707 job_adjust_mode = ADJUST_NONE;
708 } else if (strcasecmp(var, NOCATGETS("M2")) == 0) {
709 job_adjust_mode = ADJUST_M2;
710 }
711 }
712
713 /* M2 specific initialization */
714 if (job_adjust_mode == ADJUST_M2) {
715 if (m2_init()) {
716 job_adjust_error();
717 }
718 }
719 }
720 }
721
722 #endif /* MAXJOBS_ADJUST_RFE4694000 */
723 #endif /* TEAMWARE_MAKE_CMN */
724
725 /*
726 * distribute_process(char **commands, Property line)
727 *
728 * Parameters:
729 * commands argv vector of commands to execute
730 *
731 * Return value:
732 * The result of the execution
733 *
734 * Static variables used:
735 * process_running Set to the pid of the process set running
736 * #if defined (TEAMWARE_MAKE_CMN) && defined (MAXJOBS_ADJUST_RFE4694000)
737 * job_adjust_mode Current job adjust mode
738 * #endif
739 */
740 static Doname
741 distribute_process(char **commands, Property line)
742 {
743 static unsigned file_number = 0;
744 wchar_t string[MAXPATHLEN];
745 char mbstring[MAXPATHLEN];
746 int filed;
747 int res;
748 int tmp_index;
749 char *tmp_index_str_ptr;
750
751 #if !defined (TEAMWARE_MAKE_CMN) || !defined (MAXJOBS_ADJUST_RFE4694000)
752 while (parallel_process_cnt >= pmake_max_jobs) {
753 await_parallel(false);
754 finish_children(true);
755 }
756 #else /* TEAMWARE_MAKE_CMN && MAXJOBS_ADJUST_RFE4694000 */
757 /* initialize adjust mode, if not initialized */
758 if (job_adjust_mode == ADJUST_UNKNOWN) {
759 job_adjust_init();
760 }
761
762 /* actions depend on adjust mode */
763 switch (job_adjust_mode) {
764 case ADJUST_M1:
765 while (parallel_process_cnt >= adjust_pmake_max_jobs (pmake_max_jobs)) {
766 await_parallel(false);
767 finish_children(true);
768 }
769 break;
770 case ADJUST_M2:
771 if ((res = m2_acquire_job()) == 0) {
772 if (parallel_process_cnt > 0) {
773 await_parallel(false);
774 finish_children(true);
775
776 if ((res = m2_acquire_job()) == 0) {
777 return build_serial;
778 }
779 } else {
780 return build_serial;
781 }
782 }
783 if (res < 0) {
784 /* job adjustment error */
785 job_adjust_error();
786
787 /* no adjustment */
788 while (parallel_process_cnt >= pmake_max_jobs) {
789 await_parallel(false);
790 finish_children(true);
791 }
792 }
793 break;
794 default:
795 while (parallel_process_cnt >= pmake_max_jobs) {
796 await_parallel(false);
797 finish_children(true);
798 }
799 }
800 #endif /* TEAMWARE_MAKE_CMN && MAXJOBS_ADJUST_RFE4694000 */
801 #ifdef DISTRIBUTED
802 if (send_mtool_msgs) {
803 send_job_start_msg(line);
804 }
805 #endif
806 #ifdef DISTRIBUTED
807 setvar_envvar((Avo_DoJobMsg *)NULL);
808 #else
809 setvar_envvar();
810 #endif
811 /*
812 * Tell the user what DMake is doing.
813 */
814 if (!silent && output_mode != txt2_mode) {
815 /*
816 * Print local_host --> x job(s).
817 */
818 (void) fprintf(stdout,
819 catgets(catd, 1, 325, "%s --> %d %s\n"),
820 local_host,
821 parallel_process_cnt + 1,
822 (parallel_process_cnt == 0) ? catgets(catd, 1, 124, "job") : catgets(catd, 1, 125, "jobs"));
823
824 /* Print command line(s). */
825 tmp_index = 0;
826 while (commands[tmp_index] != NULL) {
827 /* No @ char. */
828 /* XXX - need to add [2] when + prefix is added */
829 if ((commands[tmp_index][0] != (int) at_char) &&
830 (commands[tmp_index][1] != (int) at_char)) {
831 tmp_index_str_ptr = commands[tmp_index];
832 if (*tmp_index_str_ptr == (int) hyphen_char) {
833 tmp_index_str_ptr++;
834 }
835 (void) fprintf(stdout, "%s\n", tmp_index_str_ptr);
836 }
837 tmp_index++;
838 }
839 (void) fflush(stdout);
840 }
841
842 (void) sprintf(mbstring,
843 NOCATGETS("%s/dmake.stdout.%d.%d.XXXXXX"),
844 tmpdir,
845 getpid(),
846 file_number++);
847
848 mktemp(mbstring);
849
850 stdout_file = strdup(mbstring);
851 stderr_file = NULL;
852 #if defined (TEAMWARE_MAKE_CMN)
853 if (!out_err_same) {
854 (void) sprintf(mbstring,
855 NOCATGETS("%s/dmake.stderr.%d.%d.XXXXXX"),
856 tmpdir,
857 getpid(),
858 file_number++);
859
860 mktemp(mbstring);
861
862 stderr_file = strdup(mbstring);
863 }
864 #endif
865
866 process_running = run_rule_commands(local_host, commands);
867
868 return build_running;
869 }
870
871 /*
872 * doname_parallel(target, do_get, implicit)
873 *
874 * Processes the given target and finishes up any parallel
875 * processes left running.
876 *
877 * Return value:
878 * Result of target build
879 *
880 * Parameters:
881 * target Target to build
882 * do_get True if sccs get to be done
883 * implicit True if this is an implicit target
884 */
885 Doname
886 doname_parallel(Name target, Boolean do_get, Boolean implicit)
887 {
888 Doname result;
889
890 result = doname_check(target, do_get, implicit, false);
891 if (result == build_ok || result == build_failed) {
892 return result;
893 }
894 finish_running();
895 return (Doname) target->state;
896 }
897
898 /*
899 * doname_subtree(target, do_get, implicit)
900 *
901 * Completely computes an object and its dependents for a
902 * serial subtree build.
903 *
904 * Parameters:
905 * target Target to build
906 * do_get True if sccs get to be done
907 * implicit True if this is an implicit target
908 *
909 * Static variables used:
910 * running_tail Tail of the list of running processes
911 *
912 * Global variables used:
913 * running_list The list of running processes
914 */
915 static void
916 doname_subtree(Name target, Boolean do_get, Boolean implicit)
917 {
918 Running save_running_list;
919 Running *save_running_tail;
920
921 save_running_list = running_list;
922 save_running_tail = running_tail;
923 running_list = NULL;
924 running_tail = &running_list;
925 target->state = build_subtree;
926 target->checking_subtree = true;
927 while(doname_check(target, do_get, implicit, false) == build_running) {
928 target->checking_subtree = false;
929 finish_running();
930 target->state = build_subtree;
931 }
932 target->checking_subtree = false;
933 running_list = save_running_list;
934 running_tail = save_running_tail;
935 }
936
937 /*
938 * finish_running()
939 *
940 * Keeps processing until the running_list is emptied out.
941 *
942 * Parameters:
943 *
944 * Global variables used:
945 * running_list The list of running processes
946 */
947 void
948 finish_running(void)
949 {
950 while (running_list != NULL) {
951 #ifdef DISTRIBUTED
952 if (dmake_mode_type == distributed_mode) {
953 if ((just_did_subtree) ||
954 (parallel_process_cnt == 0)) {
955 just_did_subtree = false;
956 } else {
957 (void) await_dist(false);
958 finish_children(true);
959 }
960 } else
961 #endif
962 {
963 await_parallel(false);
964 finish_children(true);
965 }
966 if (running_list != NULL) {
967 process_next();
968 }
969 }
970 }
971
972 /*
973 * process_next()
974 *
975 * Searches the running list for any targets which can start processing.
976 * This can be a pending target, a serial target, or a subtree target.
977 *
978 * Parameters:
979 *
980 * Static variables used:
981 * running_tail The end of the list of running procs
982 * subtree_conflict A target which conflicts with a subtree
983 * subtree_conflict2 The other target which conflicts
984 *
985 * Global variables used:
986 * commands_done True if commands executed
987 * debug_level Controls debug output
988 * parallel_process_cnt Number of parallel process running
989 * recursion_level Indentation for debug output
990 * running_list List of running processes
991 */
992 static void
993 process_next(void)
994 {
995 Running rp;
996 Running *rp_prev;
997 Property line;
998 Chain target_group;
999 Dependency dep;
1000 Boolean quiescent = true;
1001 Running *subtree_target;
1002 Boolean saved_commands_done;
1003 Property *conditionals;
1004
1005 subtree_target = NULL;
1006 subtree_conflict = NULL;
1007 subtree_conflict2 = NULL;
1008 /*
1009 * If nothing currently running, build a serial target, if any.
1010 */
1011 start_loop_1:
1012 for (rp_prev = &running_list, rp = running_list;
1013 rp != NULL && parallel_process_cnt == 0;
1014 rp = rp->next) {
1015 if (rp->state == build_serial) {
1016 *rp_prev = rp->next;
1017 if (rp->next == NULL) {
1018 running_tail = rp_prev;
1019 }
1020 recursion_level = rp->recursion_level;
1021 rp->target->state = build_pending;
1022 (void) doname_check(rp->target,
1023 rp->do_get,
1024 rp->implicit,
1025 false);
1026 quiescent = false;
1027 delete_running_struct(rp);
1028 goto start_loop_1;
1029 } else {
1030 rp_prev = &rp->next;
1031 }
1032 }
1033 /*
1034 * Find a target to build. The target must be pending, have all
1035 * its dependencies built, and not be in a target group with a target
1036 * currently building.
1037 */
1038 start_loop_2:
1039 for (rp_prev = &running_list, rp = running_list;
1040 rp != NULL;
1041 rp = rp->next) {
1042 if (!(rp->state == build_pending ||
1043 rp->state == build_subtree)) {
1044 quiescent = false;
1045 rp_prev = &rp->next;
1046 } else if (rp->state == build_pending) {
1047 line = get_prop(rp->target->prop, line_prop);
1048 for (dep = line->body.line.dependencies;
1049 dep != NULL;
1050 dep = dep->next) {
1051 if (dep->name->state == build_running ||
1052 dep->name->state == build_pending ||
1053 dep->name->state == build_serial) {
1054 break;
1055 }
1056 }
1057 if (dep == NULL) {
1058 for (target_group = line->body.line.target_group;
1059 target_group != NULL;
1060 target_group = target_group->next) {
1061 if (is_running(target_group->name)) {
1062 break;
1063 }
1064 }
1065 if (target_group == NULL) {
1066 *rp_prev = rp->next;
1067 if (rp->next == NULL) {
1068 running_tail = rp_prev;
1069 }
1070 recursion_level = rp->recursion_level;
1071 rp->target->state = rp->redo ?
1072 build_dont_know : build_pending;
1073 saved_commands_done = commands_done;
1074 conditionals =
1075 set_conditionals
1076 (rp->conditional_cnt,
1077 rp->conditional_targets);
1078 rp->target->dont_activate_cond_values = true;
1079 if ((doname_check(rp->target,
1080 rp->do_get,
1081 rp->implicit,
1082 rp->target->has_target_prop ? true : false) !=
1083 build_running) &&
1084 !commands_done) {
1085 commands_done =
1086 saved_commands_done;
1087 }
1088 rp->target->dont_activate_cond_values = false;
1089 reset_conditionals
1090 (rp->conditional_cnt,
1091 rp->conditional_targets,
1092 conditionals);
1093 quiescent = false;
1094 delete_running_struct(rp);
1095 goto start_loop_2;
1096 } else {
1097 rp_prev = &rp->next;
1098 }
1099 } else {
1100 rp_prev = &rp->next;
1101 }
1102 } else {
1103 rp_prev = &rp->next;
1104 }
1105 }
1106 /*
1107 * If nothing has been found to build and there exists a subtree
1108 * target with no dependency conflicts, build it.
1109 */
1110 if (quiescent) {
1111 start_loop_3:
1112 for (rp_prev = &running_list, rp = running_list;
1113 rp != NULL;
1114 rp = rp->next) {
1115 if (rp->state == build_subtree) {
1116 if (!dependency_conflict(rp->target)) {
1117 *rp_prev = rp->next;
1118 if (rp->next == NULL) {
1119 running_tail = rp_prev;
1120 }
1121 recursion_level = rp->recursion_level;
1122 doname_subtree(rp->target,
1123 rp->do_get,
1124 rp->implicit);
1125 #ifdef DISTRIBUTED
1126 just_did_subtree = true;
1127 #endif
1128 quiescent = false;
1129 delete_running_struct(rp);
1130 goto start_loop_3;
1131 } else {
1132 subtree_target = rp_prev;
1133 rp_prev = &rp->next;
1134 }
1135 } else {
1136 rp_prev = &rp->next;
1137 }
1138 }
1139 }
1140 /*
1141 * If still nothing found to build, we either have a deadlock
1142 * or a subtree with a dependency conflict with something waiting
1143 * to build.
1144 */
1145 if (quiescent) {
1146 if (subtree_target == NULL) {
1147 fatal(catgets(catd, 1, 126, "Internal error: deadlock detected in process_next"));
1148 } else {
1149 rp = *subtree_target;
1150 if (debug_level > 0) {
1151 warning(catgets(catd, 1, 127, "Conditional macro conflict encountered for %s between %s and %s"),
1152 subtree_conflict2->string_mb,
1153 rp->target->string_mb,
1154 subtree_conflict->string_mb);
1155 }
1156 *subtree_target = (*subtree_target)->next;
1157 if (rp->next == NULL) {
1158 running_tail = subtree_target;
1159 }
1160 recursion_level = rp->recursion_level;
1161 doname_subtree(rp->target, rp->do_get, rp->implicit);
1162 #ifdef DISTRIBUTED
1163 just_did_subtree = true;
1164 #endif
1165 delete_running_struct(rp);
1166 }
1167 }
1168 }
1169
1170 /*
1171 * set_conditionals(cnt, targets)
1172 *
1173 * Sets the conditional macros for the targets given in the array of
1174 * targets. The old macro values are returned in an array of
1175 * Properties for later resetting.
1176 *
1177 * Return value:
1178 * Array of conditional macro settings
1179 *
1180 * Parameters:
1181 * cnt Number of targets
1182 * targets Array of targets
1183 */
1184 static Property *
1185 set_conditionals(int cnt, Name *targets)
1186 {
1187 Property *locals, *lp;
1188 Name *tp;
1189
1190 locals = (Property *) getmem(cnt * sizeof(Property));
1191 for (lp = locals, tp = targets;
1192 cnt > 0;
1193 cnt--, lp++, tp++) {
1194 *lp = (Property) getmem((*tp)->conditional_cnt *
1195 sizeof(struct _Property));
1196 set_locals(*tp, *lp);
1197 }
1198 return locals;
1199 }
1200
1201 /*
1202 * reset_conditionals(cnt, targets, locals)
1203 *
1204 * Resets the conditional macros as saved in the given array of
1205 * Properties. The resets are done in reverse order. Afterwards the
1206 * data structures are freed.
1207 *
1208 * Parameters:
1209 * cnt Number of targets
1210 * targets Array of targets
1211 * locals Array of dependency macro settings
1212 */
1213 static void
1214 reset_conditionals(int cnt, Name *targets, Property *locals)
1215 {
1216 Name *tp;
1217 Property *lp;
1218
1219 for (tp = targets + (cnt - 1), lp = locals + (cnt - 1);
1220 cnt > 0;
1221 cnt--, tp--, lp--) {
1222 reset_locals(*tp,
1223 *lp,
1224 get_prop((*tp)->prop, conditional_prop),
1225 0);
1226 retmem_mb((caddr_t) *lp);
1227 }
1228 retmem_mb((caddr_t) locals);
1229 }
1230
1231 /*
1232 * dependency_conflict(target)
1233 *
1234 * Returns true if there is an intersection between
1235 * the subtree of the target and any dependents of the pending targets.
1236 *
1237 * Return value:
1238 * True if conflict found
1239 *
1240 * Parameters:
1241 * target Subtree target to check
1242 *
1243 * Static variables used:
1244 * subtree_conflict Target conflict found
1245 * subtree_conflict2 Second conflict found
1246 *
1247 * Global variables used:
1248 * running_list List of running processes
1249 * wait_name .WAIT, not a real dependency
1250 */
1251 static Boolean
1252 dependency_conflict(Name target)
1253 {
1254 Property line;
1255 Property pending_line;
1256 Dependency dp;
1257 Dependency pending_dp;
1258 Running rp;
1259
1260 /* Return if we are already checking this target */
1261 if (target->checking_subtree) {
1262 return false;
1263 }
1264 target->checking_subtree = true;
1265 line = get_prop(target->prop, line_prop);
1266 if (line == NULL) {
1267 target->checking_subtree = false;
1268 return false;
1269 }
1270 /* Check each dependency of the target for conflicts */
1271 for (dp = line->body.line.dependencies; dp != NULL; dp = dp->next) {
1272 /* Ignore .WAIT dependency */
1273 if (dp->name == wait_name) {
1274 continue;
1275 }
1276 /*
1277 * For each pending target, look for a dependency which
1278 * is the same as a dependency of the subtree target. Since
1279 * we can't build the subtree until all pending targets have
1280 * finished which depend on the same dependency, this is
1281 * a conflict.
1282 */
1283 for (rp = running_list; rp != NULL; rp = rp->next) {
1284 if (rp->state == build_pending) {
1285 pending_line = get_prop(rp->target->prop,
1286 line_prop);
1287 if (pending_line == NULL) {
1288 continue;
1289 }
1290 for(pending_dp = pending_line->
1291 body.line.dependencies;
1292 pending_dp != NULL;
1293 pending_dp = pending_dp->next) {
1294 if (dp->name == pending_dp->name) {
1295 target->checking_subtree
1296 = false;
1297 subtree_conflict = rp->target;
1298 subtree_conflict2 = dp->name;
1299 return true;
1300 }
1301 }
1302 }
1303 }
1304 if (dependency_conflict(dp->name)) {
1305 target->checking_subtree = false;
1306 return true;
1307 }
1308 }
1309 target->checking_subtree = false;
1310 return false;
1311 }
1312
1313 /*
1314 * await_parallel(waitflg)
1315 *
1316 * Waits for parallel children to exit and finishes their processing.
1317 * If waitflg is false, the function returns after update_delay.
1318 *
1319 * Parameters:
1320 * waitflg dwight
1321 */
1322 void
1323 await_parallel(Boolean waitflg)
1324 {
1325 Boolean nohang;
1326 pid_t pid;
1327 int status;
1328 Running rp;
1329 int waiterr;
1330
1331 nohang = false;
1332 for ( ; ; ) {
1333 if (!nohang) {
1334 (void) alarm((int) update_delay);
1335 }
1336 pid = waitpid((pid_t)-1,
1337 &status,
1338 nohang ? WNOHANG : 0);
1339 waiterr = errno;
1340 if (!nohang) {
1341 (void) alarm(0);
1342 }
1343 if (pid <= 0) {
1344 if (waiterr == EINTR) {
1345 if (waitflg) {
1346 continue;
1347 } else {
1348 return;
1349 }
1350 } else {
1351 return;
1352 }
1353 }
1354 for (rp = running_list;
1355 (rp != NULL) && (rp->pid != pid);
1356 rp = rp->next) {
1357 ;
1358 }
1359 if (rp == NULL) {
1360 if (send_mtool_msgs) {
1361 continue;
1362 } else {
1363 fatal(catgets(catd, 1, 128, "Internal error: returned child pid not in running_list"));
1364 }
1365 } else {
1366 rp->state = (WIFEXITED(status) && WEXITSTATUS(status) == 0) ? build_ok : build_failed;
1367 }
1368 nohang = true;
1369 parallel_process_cnt--;
1370
1371 #if defined (TEAMWARE_MAKE_CMN) && defined (MAXJOBS_ADJUST_RFE4694000)
1372 if (job_adjust_mode == ADJUST_M2) {
1373 if (m2_release_job()) {
1374 job_adjust_error();
1375 }
1376 }
1377 #endif
1378 }
1379 }
1380
1381 /*
1382 * finish_children(docheck)
1383 *
1384 * Finishes the processing for all targets which were running
1385 * and have now completed.
1386 *
1387 * Parameters:
1388 * docheck Completely check the finished target
1389 *
1390 * Static variables used:
1391 * running_tail The tail of the running list
1392 *
1393 * Global variables used:
1394 * continue_after_error -k flag
1395 * fatal_in_progress True if we are finishing up after fatal err
1396 * running_list List of running processes
1397 */
1398 void
1399 finish_children(Boolean docheck)
1400 {
1401 int cmds_length;
1402 Property line;
1403 Property line2;
1404 struct stat out_buf;
1405 Running rp;
1406 Running *rp_prev;
1407 Cmd_line rule;
1408 Boolean silent_flag;
1409
1410 for (rp_prev = &running_list, rp = running_list;
1411 rp != NULL;
1412 rp = rp->next) {
1413 bypass_for_loop_inc_4:
1414 /*
1415 * If the state is ok or failed, then this target has
1416 * finished building.
1417 * In parallel_mode, output the accumulated stdout/stderr.
1418 * Read the auto dependency stuff, handle a failed build,
1419 * update the target, then finish the doname process for
1420 * that target.
1421 */
1422 if (rp->state == build_ok || rp->state == build_failed) {
1423 *rp_prev = rp->next;
1424 if (rp->next == NULL) {
1425 running_tail = rp_prev;
1426 }
1427 if ((line2 = rp->command) == NULL) {
1428 line2 = get_prop(rp->target->prop, line_prop);
1429 }
1430 if (dmake_mode_type == distributed_mode) {
1431 if (rp->make_refd) {
1432 maybe_reread_make_state();
1433 }
1434 } else {
1435 /*
1436 * Send an Avo_MToolJobResultMsg to maketool.
1437 */
1438 #ifdef DISTRIBUTED
1439 if (send_mtool_msgs) {
1440 send_job_result_msg(rp);
1441 }
1442 #endif
1443 /*
1444 * Check if there were any job output
1445 * from the parallel build.
1446 */
1447 if (rp->stdout_file != NULL) {
1448 if (stat(rp->stdout_file, &out_buf) < 0) {
1449 fatal(catgets(catd, 1, 130, "stat of %s failed: %s"),
1450 rp->stdout_file,
1451 errmsg(errno));
1452 }
1453 if ((line2 != NULL) &&
1454 (out_buf.st_size > 0)) {
1455 cmds_length = 0;
1456 for (rule = line2->body.line.command_used,
1457 silent_flag = silent;
1458 rule != NULL;
1459 rule = rule->next) {
1460 cmds_length += rule->command_line->hash.length + 1;
1461 silent_flag = BOOLEAN(silent_flag || rule->silent);
1462 }
1463 if (out_buf.st_size != cmds_length || silent_flag ||
1464 output_mode == txt2_mode) {
1465 dump_out_file(rp->stdout_file, false);
1466 }
1467 }
1468 (void) unlink(rp->stdout_file);
1469 retmem_mb(rp->stdout_file);
1470 rp->stdout_file = NULL;
1471 }
1472
1473 if (!out_err_same && (rp->stderr_file != NULL)) {
1474 if (stat(rp->stderr_file, &out_buf) < 0) {
1475 fatal(catgets(catd, 1, 130, "stat of %s failed: %s"),
1476 rp->stderr_file,
1477 errmsg(errno));
1478 }
1479 if ((line2 != NULL) &&
1480 (out_buf.st_size > 0)) {
1481 dump_out_file(rp->stderr_file, true);
1482 }
1483 (void) unlink(rp->stderr_file);
1484 retmem_mb(rp->stderr_file);
1485 rp->stderr_file = NULL;
1486 }
1487 }
1488 check_state(rp->temp_file);
1489 if (rp->temp_file != NULL) {
1490 free_name(rp->temp_file);
1491 }
1492 rp->temp_file = NULL;
1493 if (rp->state == build_failed) {
1494 line = get_prop(rp->target->prop, line_prop);
1495 if (line != NULL) {
1496 line->body.line.command_used = NULL;
1497 }
1498 if (continue_after_error ||
1499 fatal_in_progress ||
1500 !docheck) {
1501 warning(catgets(catd, 1, 256, "Command failed for target `%s'"),
1502 rp->command ? line2->body.line.target->string_mb : rp->target->string_mb);
1503 build_failed_seen = true;
1504 } else {
1505 /*
1506 * XXX??? - DMake needs to exit(),
1507 * but shouldn't call fatal().
1508 */
1509 #ifdef PRINT_EXIT_STATUS
1510 warning(NOCATGETS("I'm in finish_children. rp->state == build_failed."));
1511 #endif
1512
1513 fatal(catgets(catd, 1, 258, "Command failed for target `%s'"),
1514 rp->command ? line2->body.line.target->string_mb : rp->target->string_mb);
1515 }
1516 }
1517 if (!docheck) {
1518 delete_running_struct(rp);
1519 rp = *rp_prev;
1520 if (rp == NULL) {
1521 break;
1522 } else {
1523 goto bypass_for_loop_inc_4;
1524 }
1525 }
1526 update_target(get_prop(rp->target->prop, line_prop),
1527 rp->state);
1528 finish_doname(rp);
1529 delete_running_struct(rp);
1530 rp = *rp_prev;
1531 if (rp == NULL) {
1532 break;
1533 } else {
1534 goto bypass_for_loop_inc_4;
1535 }
1536 } else {
1537 rp_prev = &rp->next;
1538 }
1539 }
1540 }
1541
1542 /*
1543 * dump_out_file(filename, err)
1544 *
1545 * Write the contents of the file to stdout, then unlink the file.
1546 *
1547 * Parameters:
1548 * filename Name of temp file containing output
1549 *
1550 * Global variables used:
1551 */
1552 static void
1553 dump_out_file(char *filename, Boolean err)
1554 {
1555 int chars_read;
1556 char copybuf[BUFSIZ];
1557 int fd;
1558 int out_fd = (err ? 2 : 1);
1559
1560 if ((fd = open(filename, O_RDONLY)) < 0) {
1561 fatal(catgets(catd, 1, 141, "open failed for output file %s: %s"),
1562 filename,
1563 errmsg(errno));
1564 }
1565 if (!silent && output_mode != txt2_mode) {
1566 (void) fprintf(err ? stderr : stdout,
1567 err ?
1568 catgets(catd, 1, 338, "%s --> Job errors\n") :
1569 catgets(catd, 1, 259, "%s --> Job output\n"),
1570 local_host);
1571 (void) fflush(err ? stderr : stdout);
1572 }
1573 for (chars_read = read(fd, copybuf, BUFSIZ);
1574 chars_read > 0;
1575 chars_read = read(fd, copybuf, BUFSIZ)) {
1576 /*
1577 * Read buffers from the source file until end or error.
1578 */
1579 if (write(out_fd, copybuf, chars_read) < 0) {
1580 fatal(catgets(catd, 1, 260, "write failed for output file %s: %s"),
1581 filename,
1582 errmsg(errno));
1583 }
1584 }
1585 (void) close(fd);
1586 (void) unlink(filename);
1587 }
1588
1589 /*
1590 * finish_doname(rp)
1591 *
1592 * Completes the processing for a target which was left running.
1593 *
1594 * Parameters:
1595 * rp Running list entry for target
1596 *
1597 * Global variables used:
1598 * debug_level Debug flag
1599 * recursion_level Indentation for debug output
1600 */
1601 static void
1602 finish_doname(Running rp)
1603 {
1604 int auto_count = rp->auto_count;
1605 Name *automatics = rp->automatics;
1606 Doname result = rp->state;
1607 Name target = rp->target;
1608 Name true_target = rp->true_target;
1609 Property *conditionals;
1610
1611 recursion_level = rp->recursion_level;
1612 if (result == build_ok) {
1613 if (true_target == NULL) {
1614 (void) printf(NOCATGETS("Target = %s\n"), target->string_mb);
1615 (void) printf(NOCATGETS(" State = %d\n"), result);
1616 fatal(NOCATGETS("Internal error: NULL true_target in finish_doname"));
1617 }
1618 /* If all went OK, set a nice timestamp */
1619 if (true_target->stat.time == file_doesnt_exist) {
1620 true_target->stat.time = file_max_time;
1621 }
1622 }
1623 target->state = result;
1624 if (target->is_member) {
1625 Property member;
1626
1627 /* Propagate the timestamp from the member file to the member */
1628 if ((target->stat.time != file_max_time) &&
1629 ((member = get_prop(target->prop, member_prop)) != NULL) &&
1630 (exists(member->body.member.member) > file_doesnt_exist)) {
1631 target->stat.time =
1632 /*
1633 exists(member->body.member.member);
1634 */
1635 member->body.member.member->stat.time;
1636 }
1637 }
1638 /*
1639 * Check if we found any new auto dependencies when we
1640 * built the target.
1641 */
1642 if ((result == build_ok) && check_auto_dependencies(target,
1643 auto_count,
1644 automatics)) {
1645 if (debug_level > 0) {
1646 (void) printf(catgets(catd, 1, 261, "%*sTarget `%s' acquired new dependencies from build, checking all dependencies\n"),
1647 recursion_level,
1648 "",
1649 true_target->string_mb);
1650 }
1651 target->rechecking_target = true;
1652 target->state = build_running;
1653
1654 /* [tolik, Tue Mar 25 1997]
1655 * Fix for bug 4038824:
1656 * command line options set by conditional macros get dropped
1657 * rp->conditional_cnt and rp->conditional_targets must be copied
1658 * to new 'rp' during add_pending(). Set_conditionals() stores
1659 * rp->conditional_targets to the global variable 'conditional_targets'
1660 * Add_pending() will use this variable to set up 'rp'.
1661 */
1662 conditionals = set_conditionals(rp->conditional_cnt, rp->conditional_targets);
1663 add_pending(target,
1664 recursion_level,
1665 rp->do_get,
1666 rp->implicit,
1667 true);
1668 reset_conditionals(rp->conditional_cnt, rp->conditional_targets, conditionals);
1669 }
1670 }
1671
1672 /*
1673 * new_running_struct()
1674 *
1675 * Constructor for Running struct. Creates a structure and initializes
1676 * its fields.
1677 *
1678 */
1679 static Running new_running_struct()
1680 {
1681 Running rp;
1682
1683 rp = ALLOC(Running);
1684 rp->target = NULL;
1685 rp->true_target = NULL;
1686 rp->command = NULL;
1687 rp->sprodep_value = NULL;
1688 rp->sprodep_env = NULL;
1689 rp->auto_count = 0;
1690 rp->automatics = NULL;
1691 rp->pid = -1;
1692 rp->job_msg_id = -1;
1693 rp->stdout_file = NULL;
1694 rp->stderr_file = NULL;
1695 rp->temp_file = NULL;
1696 rp->next = NULL;
1697 return rp;
1698 }
1699
1700 /*
1701 * add_running(target, true_target, command, recursion_level, auto_count,
1702 * automatics, do_get, implicit)
1703 *
1704 * Adds a record on the running list for this target, which
1705 * was just spawned and is running.
1706 *
1707 * Parameters:
1708 * target Target being built
1709 * true_target True target for target
1710 * command Running command.
1711 * recursion_level Debug indentation level
1712 * auto_count Count of automatic dependencies
1713 * automatics List of automatic dependencies
1714 * do_get Sccs get flag
1715 * implicit Implicit flag
1716 *
1717 * Static variables used:
1718 * running_tail Tail of running list
1719 * process_running PID of process
1720 *
1721 * Global variables used:
1722 * current_line Current line for target
1723 * current_target Current target being built
1724 * stderr_file Temporary file for stdout
1725 * stdout_file Temporary file for stdout
1726 * temp_file_name Temporary file for auto dependencies
1727 */
1728 void
1729 add_running(Name target, Name true_target, Property command, int recursion_level, int auto_count, Name *automatics, Boolean do_get, Boolean implicit)
1730 {
1731 Running rp;
1732 Name *p;
1733
1734 rp = new_running_struct();
1735 rp->state = build_running;
1736 rp->target = target;
1737 rp->true_target = true_target;
1738 rp->command = command;
1739 Property spro_val = get_prop(sunpro_dependencies->prop, macro_prop);
1740 if(spro_val) {
1741 rp->sprodep_value = spro_val->body.macro.value;
1742 spro_val->body.macro.value = NULL;
1743 spro_val = get_prop(sunpro_dependencies->prop, env_mem_prop);
1744 if(spro_val) {
1745 rp->sprodep_env = spro_val->body.env_mem.value;
1746 spro_val->body.env_mem.value = NULL;
1747 }
1748 }
1749 rp->recursion_level = recursion_level;
1750 rp->do_get = do_get;
1751 rp->implicit = implicit;
1752 rp->auto_count = auto_count;
1753 if (auto_count > 0) {
1754 rp->automatics = (Name *) getmem(auto_count * sizeof (Name));
1755 for (p = rp->automatics; auto_count > 0; auto_count--) {
1756 *p++ = *automatics++;
1757 }
1758 } else {
1759 rp->automatics = NULL;
1760 }
1761 #ifdef DISTRIBUTED
1762 if (dmake_mode_type == distributed_mode) {
1763 rp->make_refd = called_make;
1764 called_make = false;
1765 } else
1766 #endif
1767 {
1768 rp->pid = process_running;
1769 process_running = -1;
1770 childPid = -1;
1771 }
1772 rp->job_msg_id = job_msg_id;
1773 rp->stdout_file = stdout_file;
1774 rp->stderr_file = stderr_file;
1775 rp->temp_file = temp_file_name;
1776 rp->redo = false;
1777 rp->next = NULL;
1778 store_conditionals(rp);
1779 stdout_file = NULL;
1780 stderr_file = NULL;
1781 temp_file_name = NULL;
1782 current_target = NULL;
1783 current_line = NULL;
1784 *running_tail = rp;
1785 running_tail = &rp->next;
1786 }
1787
1788 /*
1789 * add_pending(target, recursion_level, do_get, implicit, redo)
1790 *
1791 * Adds a record on the running list for a pending target
1792 * (waiting for its dependents to finish running).
1793 *
1794 * Parameters:
1795 * target Target being built
1796 * recursion_level Debug indentation level
1797 * do_get Sccs get flag
1798 * implicit Implicit flag
1799 * redo True if this target is being redone
1800 *
1801 * Static variables used:
1802 * running_tail Tail of running list
1803 */
1804 void
1805 add_pending(Name target, int recursion_level, Boolean do_get, Boolean implicit, Boolean redo)
1806 {
1807 Running rp;
1808 rp = new_running_struct();
1809 rp->state = build_pending;
1810 rp->target = target;
1811 rp->recursion_level = recursion_level;
1812 rp->do_get = do_get;
1813 rp->implicit = implicit;
1814 rp->redo = redo;
1815 store_conditionals(rp);
1816 *running_tail = rp;
1817 running_tail = &rp->next;
1818 }
1819
1820 /*
1821 * add_serial(target, recursion_level, do_get, implicit)
1822 *
1823 * Adds a record on the running list for a target which must be
1824 * executed in serial after others have finished.
1825 *
1826 * Parameters:
1827 * target Target being built
1828 * recursion_level Debug indentation level
1829 * do_get Sccs get flag
1830 * implicit Implicit flag
1831 *
1832 * Static variables used:
1833 * running_tail Tail of running list
1834 */
1835 void
1836 add_serial(Name target, int recursion_level, Boolean do_get, Boolean implicit)
1837 {
1838 Running rp;
1839
1840 rp = new_running_struct();
1841 rp->target = target;
1842 rp->recursion_level = recursion_level;
1843 rp->do_get = do_get;
1844 rp->implicit = implicit;
1845 rp->state = build_serial;
1846 rp->redo = false;
1847 store_conditionals(rp);
1848 *running_tail = rp;
1849 running_tail = &rp->next;
1850 }
1851
1852 /*
1853 * add_subtree(target, recursion_level, do_get, implicit)
1854 *
1855 * Adds a record on the running list for a target which must be
1856 * executed in isolation after others have finished.
1857 *
1858 * Parameters:
1859 * target Target being built
1860 * recursion_level Debug indentation level
1861 * do_get Sccs get flag
1862 * implicit Implicit flag
1863 *
1864 * Static variables used:
1865 * running_tail Tail of running list
1866 */
1867 void
1868 add_subtree(Name target, int recursion_level, Boolean do_get, Boolean implicit)
1869 {
1870 Running rp;
1871
1872 rp = new_running_struct();
1873 rp->target = target;
1874 rp->recursion_level = recursion_level;
1875 rp->do_get = do_get;
1876 rp->implicit = implicit;
1877 rp->state = build_subtree;
1878 rp->redo = false;
1879 store_conditionals(rp);
1880 *running_tail = rp;
1881 running_tail = &rp->next;
1882 }
1883
1884 /*
1885 * store_conditionals(rp)
1886 *
1887 * Creates an array of the currently active targets with conditional
1888 * macros (found in the chain conditional_targets) and puts that
1889 * array in the Running struct.
1890 *
1891 * Parameters:
1892 * rp Running struct for storing chain
1893 *
1894 * Global variables used:
1895 * conditional_targets Chain of current dynamic conditionals
1896 */
1897 static void
1898 store_conditionals(Running rp)
1899 {
1900 int cnt;
1901 Chain cond_name;
1902
1903 if (conditional_targets == NULL) {
1904 rp->conditional_cnt = 0;
1905 rp->conditional_targets = NULL;
1906 return;
1907 }
1908 cnt = 0;
1909 for (cond_name = conditional_targets;
1910 cond_name != NULL;
1911 cond_name = cond_name->next) {
1912 cnt++;
1913 }
1914 rp->conditional_cnt = cnt;
1915 rp->conditional_targets = (Name *) getmem(cnt * sizeof(Name));
1916 for (cond_name = conditional_targets;
1917 cond_name != NULL;
1918 cond_name = cond_name->next) {
1919 rp->conditional_targets[--cnt] = cond_name->name;
1920 }
1921 }
1922
1923 /*
1924 * parallel_ok(target, line_prop_must_exists)
1925 *
1926 * Returns true if the target can be run in parallel
1927 *
1928 * Return value:
1929 * True if can run in parallel
1930 *
1931 * Parameters:
1932 * target Target being tested
1933 *
1934 * Global variables used:
1935 * all_parallel True if all targets default to parallel
1936 * only_parallel True if no targets default to parallel
1937 */
1938 Boolean
1939 parallel_ok(Name target, Boolean line_prop_must_exists)
1940 {
1941 Boolean assign;
1942 Boolean make_refd;
1943 Property line;
1944 Cmd_line rule;
1945
1946 assign = make_refd = false;
1947 if (((line = get_prop(target->prop, line_prop)) == NULL) &&
1948 line_prop_must_exists) {
1949 return false;
1950 }
1951 if (line != NULL) {
1952 for (rule = line->body.line.command_used;
1953 rule != NULL;
1954 rule = rule->next) {
1955 if (rule->assign) {
1956 assign = true;
1957 } else if (rule->make_refd) {
1958 make_refd = true;
1959 }
1960 }
1961 }
1962 if (assign) {
1963 return false;
1964 } else if (target->parallel) {
1965 return true;
1966 } else if (target->no_parallel) {
1967 return false;
1968 } else if (all_parallel) {
1969 return true;
1970 } else if (only_parallel) {
1971 return false;
1972 } else if (make_refd) {
1973 return false;
1974 } else {
1975 return true;
1976 }
1977 }
1978
1979 /*
1980 * is_running(target)
1981 *
1982 * Returns true if the target is running.
1983 *
1984 * Return value:
1985 * True if target is running
1986 *
1987 * Parameters:
1988 * target Target to check
1989 *
1990 * Global variables used:
1991 * running_list List of running processes
1992 */
1993 Boolean
1994 is_running(Name target)
1995 {
1996 Running rp;
1997
1998 if (target->state != build_running) {
1999 return false;
2000 }
2001 for (rp = running_list;
2002 rp != NULL && target != rp->target;
2003 rp = rp->next);
2004 if (rp == NULL) {
2005 return false;
2006 } else {
2007 return (rp->state == build_running) ? true : false;
2008 }
2009 }
2010
2011 /*
2012 * This function replaces the makesh binary.
2013 */
2014
2015
2016 static pid_t
2017 run_rule_commands(char *host, char **commands)
2018 {
2019 Boolean always_exec;
2020 Name command;
2021 Boolean ignore;
2022 int length;
2023 Doname result;
2024 Boolean silent_flag;
2025 wchar_t *tmp_wcs_buffer;
2026
2027 childPid = fork();
2028 switch (childPid) {
2029 case -1: /* Error */
2030 fatal(catgets(catd, 1, 337, "Could not fork child process for dmake job: %s"),
2031 errmsg(errno));
2032 break;
2033 case 0: /* Child */
2034 /* To control the processed targets list is not the child's business */
2035 running_list = NULL;
2036 if(out_err_same) {
2037 redirect_io(stdout_file, (char*)NULL);
2038 } else {
2039 redirect_io(stdout_file, stderr_file);
2040 }
2041 for (commands = commands;
2042 (*commands != (char *)NULL);
2043 commands++) {
2044 silent_flag = silent;
2045 ignore = false;
2046 always_exec = false;
2047 while ((**commands == (int) at_char) ||
2048 (**commands == (int) hyphen_char) ||
2049 (**commands == (int) plus_char)) {
2050 if (**commands == (int) at_char) {
2051 silent_flag = true;
2052 }
2053 if (**commands == (int) hyphen_char) {
2054 ignore = true;
2055 }
2056 if (**commands == (int) plus_char) {
2057 always_exec = true;
2058 }
2059 (*commands)++;
2060 }
2061 if ((length = strlen(*commands)) >= MAXPATHLEN) {
2062 tmp_wcs_buffer = ALLOC_WC(length + 1);
2063 (void) mbstowcs(tmp_wcs_buffer, *commands, length + 1);
2064 command = GETNAME(tmp_wcs_buffer, FIND_LENGTH);
2065 retmem(tmp_wcs_buffer);
2066 } else {
2067 MBSTOWCS(wcs_buffer, *commands);
2068 command = GETNAME(wcs_buffer, FIND_LENGTH);
2069 }
2070 if ((command->hash.length > 0) &&
2071 !silent_flag) {
2072 (void) printf("%s\n", command->string_mb);
2073 }
2074 result = dosys(command,
2075 ignore,
2076 false,
2077 false, /* bugs #4085164 & #4990057 */
2078 /* BOOLEAN(silent_flag && ignore), */
2079 always_exec,
2080 (Name) NULL,
2081 false);
2082 if (result == build_failed) {
2083 if (silent_flag) {
2084 (void) printf(catgets(catd, 1, 152, "The following command caused the error:\n%s\n"), command->string_mb);
2085 }
2086 if (!ignore) {
2087 _exit(1);
2088 }
2089 }
2090 }
2091 _exit(0);
2092 break;
2093 default:
2094 break;
2095 }
2096 return childPid;
2097 }
2098
2099 static void
2100 maybe_reread_make_state(void)
2101 {
2102 /* Copying dosys()... */
2103 if (report_dependencies_level == 0) {
2104 make_state->stat.time = file_no_time;
2105 (void) exists(make_state);
2106 if (make_state_before == make_state->stat.time) {
2107 return;
2108 }
2109 makefile_type = reading_statefile;
2110 if (read_trace_level > 1) {
2111 trace_reader = true;
2112 }
2113 temp_file_number++;
2114 (void) read_simple_file(make_state,
2115 false,
2116 false,
2117 false,
2118 false,
2119 false,
2120 true);
2121 trace_reader = false;
2122 }
2123 }
2124
2125 #ifdef DISTRIBUTED
2126 /*
2127 * Create and send an Avo_MToolJobResultMsg.
2128 */
2129 static void
2130 send_job_result_msg(Running rp)
2131 {
2132 Avo_MToolJobResultMsg *msg;
2133 RWCollectable *xdr_msg;
2134
2135 msg = new Avo_MToolJobResultMsg();
2136 msg->setResult(rp->job_msg_id,
2137 (rp->state == build_ok) ? 0 : 1,
2138 DONE);
2139 append_job_result_msg(msg,
2140 rp->stdout_file,
2141 rp->stderr_file);
2142
2143 xdr_msg = (RWCollectable *)msg;
2144 xdr(get_xdrs_ptr(), xdr_msg);
2145 (void) fflush(get_mtool_msgs_fp());
2146
2147 delete msg;
2148 }
2149
2150 /*
2151 * Append the stdout/err to Avo_MToolJobResultMsg.
2152 */
2153 static void
2154 append_job_result_msg(Avo_MToolJobResultMsg *msg, char *outFn, char *errFn)
2155 {
2156 FILE *fp;
2157 char line[MAXPATHLEN];
2158
2159 fp = fopen(outFn, "r");
2160 if (fp == NULL) {
2161 /* Hmmm... what should we do here? */
2162 return;
2163 }
2164 while (fgets(line, MAXPATHLEN, fp) != NULL) {
2165 if (line[strlen(line) - 1] == '\n') {
2166 line[strlen(line) - 1] = '\0';
2167 }
2168 msg->appendOutput(AVO_STRDUP(line));
2169 }
2170 (void) fclose(fp);
2171 }
2172 #endif
2173
2174 static void
2175 delete_running_struct(Running rp)
2176 {
2177 if ((rp->conditional_cnt > 0) &&
2178 (rp->conditional_targets != NULL)) {
2179 retmem_mb((char *) rp->conditional_targets);
2180 }
2181 /**/
2182 if ((rp->auto_count > 0) &&
2183 (rp->automatics != NULL)) {
2184 retmem_mb((char *) rp->automatics);
2185 }
2186 /**/
2187 if(rp->sprodep_value) {
2188 free_name(rp->sprodep_value);
2189 }
2190 if(rp->sprodep_env) {
2191 retmem_mb(rp->sprodep_env);
2192 }
2193 retmem_mb((char *) rp);
2194
2195 }
2196
2197 #endif
2198