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