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