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