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