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