diff options
author | Igor Pashev <igor.pashev@nexenta.com> | 2012-06-29 14:36:07 +0400 |
---|---|---|
committer | Igor Pashev <igor.pashev@nexenta.com> | 2012-06-29 14:36:07 +0400 |
commit | e0463df9c3d2ee6155221cc443c571d5da47098a (patch) | |
tree | 5c6b99e64c1b65d986e2722728c74f202a578be6 /usr/src/make_src/Make/bin/make/common/parallel.cc | |
download | sunmake-e0463df9c3d2ee6155221cc443c571d5da47098a.tar.gz |
Initial import of DevPro make sourcesorig
Downloaded from http://dlc.sun.com/osol/devpro/downloads/current/
Licensed under CDDL http://www.opensource.org/licenses/CDDL-1.0
Diffstat (limited to 'usr/src/make_src/Make/bin/make/common/parallel.cc')
-rw-r--r-- | usr/src/make_src/Make/bin/make/common/parallel.cc | 2477 |
1 files changed, 2477 insertions, 0 deletions
diff --git a/usr/src/make_src/Make/bin/make/common/parallel.cc b/usr/src/make_src/Make/bin/make/common/parallel.cc new file mode 100644 index 0000000..1831415 --- /dev/null +++ b/usr/src/make_src/Make/bin/make/common/parallel.cc @@ -0,0 +1,2477 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * @(#)parallel.cc 1.75 06/12/12 + */ + +#pragma ident "@(#)parallel.cc 1.75 06/12/12" + +#ifdef TEAMWARE_MAKE_CMN + +/* + * parallel.cc + * + * Deal with the parallel processing + */ + +/* + * Included files + */ +#ifdef DISTRIBUTED +#include <avo/strings.h> /* AVO_STRDUP() */ +#include <dm/Avo_DoJobMsg.h> +#include <dm/Avo_MToolJobResultMsg.h> +#endif +#include <errno.h> /* errno */ +#include <fcntl.h> +#include <avo/util.h> /* avo_get_user(), avo_hostname() */ +#include <mk/defs.h> +#include <mksh/dosys.h> /* redirect_io() */ +#include <mksh/macro.h> /* expand_value() */ +#include <mksh/misc.h> /* getmem() */ +#include <sys/signal.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/utsname.h> +#include <sys/wait.h> +#include <unistd.h> + +#ifdef SGE_SUPPORT +#include <dmthread/Avo_PathNames.h> +#endif + + +/* + * Defined macros + */ +#define MAXRULES 100 + +/* + * This const should be in avo_dms/include/AvoDmakeCommand.h + */ +const int local_host_mask = 0x20; + + +/* + * typedefs & structs + */ + + +/* + * Static variables + */ +#ifdef TEAMWARE_MAKE_CMN +static Boolean just_did_subtree = false; +static char local_host[MAXNAMELEN] = ""; +static char user_name[MAXNAMELEN] = ""; +#endif +static int pmake_max_jobs = 0; +static pid_t process_running = -1; +static Running *running_tail = &running_list; +static Name subtree_conflict; +static Name subtree_conflict2; + + +/* + * File table of contents + */ +#ifdef DISTRIBUTED +static void append_dmake_cmd(Avo_DoJobMsg *dmake_job_msg, char *orig_cmd_line, int cmd_options); +static void append_job_result_msg(Avo_MToolJobResultMsg *msg, char *outFn, char *errFn); +static void send_job_result_msg(Running rp); +#endif +static void delete_running_struct(Running rp); +static Boolean dependency_conflict(Name target); +static Doname distribute_process(char **commands, Property line); +static void doname_subtree(Name target, Boolean do_get, Boolean implicit); +static void dump_out_file(char *filename, Boolean err); +static void finish_doname(Running rp); +static void maybe_reread_make_state(void); +static void process_next(void); +static void reset_conditionals(int cnt, Name *targets, Property *locals); +static pid_t run_rule_commands(char *host, char **commands); +static Property *set_conditionals(int cnt, Name *targets); +static void store_conditionals(Running rp); + + +/* + * execute_parallel(line, waitflg) + * + * DMake 2.x: + * parallel mode: spawns a parallel process to execute the command group. + * distributed mode: sends the command group down the pipe to rxm. + * + * Return value: + * The result of the execution + * + * Parameters: + * line The command group to execute + */ +Doname +execute_parallel(Property line, Boolean waitflg, Boolean local) +{ + int argcnt; + int cmd_options = 0; + char *commands[MAXRULES + 5]; + char *cp; +#ifdef DISTRIBUTED + Avo_DoJobMsg *dmake_job_msg = NULL; +#endif + Name dmake_name; + Name dmake_value; + int ignore; + Name make_machines_name; + char **p; + Property prop; + Doname result = build_ok; + Cmd_line rule; + Boolean silent_flag; + Name target = line->body.line.target; + Boolean wrote_state_file = false; + + if ((pmake_max_jobs == 0) && + (dmake_mode_type == parallel_mode)) { + if (user_name[0] == '\0') { + avo_get_user(user_name, NULL); + } + if (local_host[0] == '\0') { + strcpy(local_host, avo_hostname()); + } + MBSTOWCS(wcs_buffer, NOCATGETS("DMAKE_MAX_JOBS")); + dmake_name = GETNAME(wcs_buffer, FIND_LENGTH); + if (((prop = get_prop(dmake_name->prop, macro_prop)) != NULL) && + ((dmake_value = prop->body.macro.value) != NULL)) { + pmake_max_jobs = atoi(dmake_value->string_mb); + if (pmake_max_jobs <= 0) { + warning(catgets(catd, 1, 308, "DMAKE_MAX_JOBS cannot be less than or equal to zero.")); + warning(catgets(catd, 1, 309, "setting DMAKE_MAX_JOBS to %d."), PMAKE_DEF_MAX_JOBS); + pmake_max_jobs = PMAKE_DEF_MAX_JOBS; + } + } else { + /* + * For backwards compatibility w/ PMake 1.x, when + * DMake 2.x is being run in parallel mode, DMake + * should parse the PMake startup file + * $(HOME)/.make.machines to get the pmake_max_jobs. + */ + MBSTOWCS(wcs_buffer, NOCATGETS("PMAKE_MACHINESFILE")); + dmake_name = GETNAME(wcs_buffer, FIND_LENGTH); + if (((prop = get_prop(dmake_name->prop, macro_prop)) != NULL) && + ((dmake_value = prop->body.macro.value) != NULL)) { + make_machines_name = dmake_value; + } else { + make_machines_name = NULL; + } + if ((pmake_max_jobs = read_make_machines(make_machines_name)) <= 0) { + pmake_max_jobs = PMAKE_DEF_MAX_JOBS; + } + } +#ifdef DISTRIBUTED + if (send_mtool_msgs) { + send_rsrc_info_msg(pmake_max_jobs, local_host, user_name); + } +#endif + } + + if ((dmake_mode_type == serial_mode) || + ((dmake_mode_type == parallel_mode) && (waitflg))) { + return (execute_serial(line)); + } + +#ifdef DISTRIBUTED + if (dmake_mode_type == distributed_mode) { + if(local) { +// return (execute_serial(line)); + waitflg = true; + } + dmake_job_msg = new Avo_DoJobMsg(); + dmake_job_msg->setJobId(++job_msg_id); + dmake_job_msg->setTarget(target->string_mb); + dmake_job_msg->setImmediateOutput(0); + called_make = false; + } else +#endif + { + p = commands; + } + + argcnt = 0; + for (rule = line->body.line.command_used; + rule != NULL; + rule = rule->next) { + if (posix && (touch || quest) && !rule->always_exec) { + continue; + } + if (vpath_defined) { + rule->command_line = + vpath_translation(rule->command_line); + } + if (dmake_mode_type == distributed_mode) { + cmd_options = 0; + if(local) { + cmd_options |= local_host_mask; + } + } else { + silent_flag = false; + ignore = 0; + } + if (rule->command_line->hash.length > 0) { + if (++argcnt == MAXRULES) { + if (dmake_mode_type == distributed_mode) { + /* XXX - tell rxm to execute on local host. */ + /* I WAS HERE!!! */ + } else { + /* Too many rules, run serially instead. */ + return build_serial; + } + } +#ifdef DISTRIBUTED + if (dmake_mode_type == distributed_mode) { + /* + * XXX - set assign_mask to tell rxm + * to do the following. + */ +/* From execute_serial(): + if (rule->assign) { + result = build_ok; + do_assign(rule->command_line, target); + */ + if (0) { + } else if (report_dependencies_level == 0) { + if (rule->ignore_error) { + cmd_options |= ignore_mask; + } + if (rule->silent) { + cmd_options |= silent_mask; + } + if (rule->command_line->meta) { + cmd_options |= meta_mask; + } + if (rule->make_refd) { + cmd_options |= make_refd_mask; + } + if (do_not_exec_rule) { + cmd_options |= do_not_exec_mask; + } + append_dmake_cmd(dmake_job_msg, + rule->command_line->string_mb, + cmd_options); + /* Copying dosys()... */ + if (rule->make_refd) { + if (waitflg) { + dmake_job_msg->setImmediateOutput(1); + } + called_make = true; + if (command_changed && + !wrote_state_file) { + write_state_file(0, false); + wrote_state_file = true; + } + } + } + } else +#endif + { + if (rule->silent && !silent) { + silent_flag = true; + } + if (rule->ignore_error) { + ignore++; + } + /* XXX - need to add support for + prefix */ + if (silent_flag || ignore) { + *p = getmem((silent_flag ? 1 : 0) + + ignore + + (strlen(rule-> + command_line-> + string_mb)) + + 1); + cp = *p++; + if (silent_flag) { + *cp++ = (int) at_char; + } + if (ignore) { + *cp++ = (int) hyphen_char; + } + (void) strcpy(cp, rule->command_line->string_mb); + } else { + *p++ = rule->command_line->string_mb; + } + } + } + } + if ((argcnt == 0) || + (report_dependencies_level > 0)) { +#ifdef DISTRIBUTED + if (dmake_job_msg) { + delete dmake_job_msg; + } +#endif + return build_ok; + } +#ifdef DISTRIBUTED + if (dmake_mode_type == distributed_mode) { + // Send a DoJob message to the rxm process. + distribute_rxm(dmake_job_msg); + + // Wait for an acknowledgement. + Avo_AcknowledgeMsg *ackMsg = getAcknowledgeMsg(); + if (ackMsg) { + delete ackMsg; + } + + if (waitflg) { + // Wait for, and process a job result. + result = await_dist(waitflg); + if (called_make) { + maybe_reread_make_state(); + } + check_state(temp_file_name); + if (result == build_failed) { + if (!continue_after_error) { + +#ifdef PRINT_EXIT_STATUS + warning(NOCATGETS("I'm in execute_parallel. await_dist() returned build_failed")); +#endif + + fatal(catgets(catd, 1, 252, "Command failed for target `%s'"), + target->string_mb); + } + /* + * Make sure a failing command is not + * saved in .make.state. + */ + line->body.line.command_used = NULL; + } + if (temp_file_name != NULL) { + free_name(temp_file_name); + } + temp_file_name = NULL; + Property spro = get_prop(sunpro_dependencies->prop, macro_prop); + if(spro != NULL) { + Name val = spro->body.macro.value; + if(val != NULL) { + free_name(val); + spro->body.macro.value = NULL; + } + } + spro = get_prop(sunpro_dependencies->prop, env_mem_prop); + if(spro) { + char *val = spro->body.env_mem.value; + if(val != NULL) { + retmem_mb(val); + spro->body.env_mem.value = NULL; + } + } + return result; + } else { + parallel_process_cnt++; + return build_running; + } + } else +#endif + { + *p = NULL; + + Doname res = distribute_process(commands, line); + if (res == build_running) { + parallel_process_cnt++; + } + + /* + * Return only those memory that were specially allocated + * for part of commands. + */ + for (int i = 0; commands[i] != NULL; i++) { + if ((commands[i][0] == (int) at_char) || + (commands[i][0] == (int) hyphen_char)) { + retmem_mb(commands[i]); + } + } + return res; + } +} + +#ifdef DISTRIBUTED +/* + * append_dmake_cmd() + * + * Replaces all escaped newline's (\<cr>) + * in the original command line with space's, + * then append the new command line to the DoJobMsg object. + */ +static void +append_dmake_cmd(Avo_DoJobMsg *dmake_job_msg, + char *orig_cmd_line, + int cmd_options) +{ +/* + Avo_DmakeCommand *tmp_dmake_command; + + tmp_dmake_command = new Avo_DmakeCommand(orig_cmd_line, cmd_options); + dmake_job_msg->appendCmd(tmp_dmake_command); + delete tmp_dmake_command; + */ + dmake_job_msg->appendCmd(new Avo_DmakeCommand(orig_cmd_line, cmd_options)); +} +#endif + +#ifdef TEAMWARE_MAKE_CMN +#define MAXJOBS_ADJUST_RFE4694000 + +#ifdef MAXJOBS_ADJUST_RFE4694000 + +#include <unistd.h> /* sysconf(_SC_NPROCESSORS_ONLN) */ +#include <sys/ipc.h> /* ftok() */ +#include <sys/shm.h> /* shmget(), shmat(), shmdt(), shmctl() */ +#include <semaphore.h> /* sem_init(), sem_trywait(), sem_post(), sem_destroy() */ +#if defined(linux) +#define LOADAVG_1MIN 0 +#else +#include <sys/loadavg.h> /* getloadavg() */ +#endif /* linux */ + +/* + * adjust_pmake_max_jobs (int pmake_max_jobs) + * + * Parameters: + * pmake_max_jobs - max jobs limit set by user + * + * External functions used: + * sysconf() + * getloadavg() + */ +static int +adjust_pmake_max_jobs (int pmake_max_jobs) +{ + static int ncpu = 0; + double loadavg[3]; + int adjustment; + int adjusted_max_jobs; + + if (ncpu <= 0) { + if ((ncpu = sysconf(_SC_NPROCESSORS_ONLN)) <= 0) { + ncpu = 1; + } + } + if (getloadavg(loadavg, 3) != 3) return(pmake_max_jobs); + adjustment = ((int)loadavg[LOADAVG_1MIN]); + if (adjustment < 2) return(pmake_max_jobs); + if (ncpu > 1) { + adjustment = adjustment / ncpu; + } + adjusted_max_jobs = pmake_max_jobs - adjustment; + if (adjusted_max_jobs < 1) adjusted_max_jobs = 1; + return(adjusted_max_jobs); +} + +/* + * M2 adjust mode data and functions + * + * m2_init() - initializes M2 shared semaphore + * m2_acquire_job() - decrements M2 semaphore counter + * m2_release_job() - increments M2 semaphore counter + * m2_fini() - destroys M2 semaphore and shared memory* + * + * Environment variables: + * __DMAKE_M2_FILE__ + * + * External functions: + * ftok(), shmget(), shmat(), shmdt(), shmctl() + * sem_init(), sem_trywait(), sem_post(), sem_destroy() + * creat(), close(), unlink() + * getenv(), putenv() + * + * Static variables: + * m2_file - tmp file name to create ipc key for shared memory + * m2_shm_id - shared memory id + * m2_shm_sem - shared memory semaphore + */ + +static char m2_file[MAXPATHLEN]; +static int m2_shm_id = -1; +static sem_t* m2_shm_sem = 0; + +static int +m2_init() { + char *var; + key_t key; + + if ((var = getenv(NOCATGETS("__DMAKE_M2_FILE__"))) == 0) { + /* compose tmp file name */ + sprintf(m2_file, NOCATGETS("%s/dmake.m2.%d.XXXXXX"), tmpdir, getpid()); + + /* create tmp file */ + int fd = mkstemp(m2_file); + if (fd < 0) { + return -1; + } else { + close(fd); + } + } else { + /* using existing semaphore */ + strcpy(m2_file, var); + } + + /* combine IPC key */ + if ((key = ftok(m2_file, 38)) == (key_t) -1) { + return -1; + } + + /* create shared memory */ + if ((m2_shm_id = shmget(key, sizeof(*m2_shm_sem), 0666 | (var ? 0 : IPC_CREAT|IPC_EXCL))) == -1) { + return -1; + } + + /* attach shared memory */ + if ((m2_shm_sem = (sem_t*) shmat(m2_shm_id, 0, 0666)) == (sem_t*)-1) { + return -1; + } + + /* root process */ + if (var == 0) { + /* initialize semaphore */ + if (sem_init(m2_shm_sem, 1, pmake_max_jobs)) { + return -1; + } + + /* alloc memory for env variable */ + if ((var = (char*) malloc(MAXPATHLEN)) == 0) { + return -1; + } + + /* put key to env */ + sprintf(var, NOCATGETS("__DMAKE_M2_FILE__=%s"), m2_file); + if (putenv(var)) { + return -1; + } + } + return 0; +} + +static void +m2_fini() { + if (m2_shm_id >= 0) { + struct shmid_ds stat; + + /* determine the number of attached processes */ + if (shmctl(m2_shm_id, IPC_STAT, &stat) == 0) { + if (stat.shm_nattch <= 1) { + /* destroy semaphore */ + if (m2_shm_sem != 0) { + (void) sem_destroy(m2_shm_sem); + } + + /* destroy shared memory */ + (void) shmctl(m2_shm_id, IPC_RMID, &stat); + + /* remove tmp file created for the key */ + (void) unlink(m2_file); + } else { + /* detach shared memory */ + if (m2_shm_sem != 0) { + (void) shmdt((char*) m2_shm_sem); + } + } + } + + m2_shm_id = -1; + m2_shm_sem = 0; + } +} + +static int +m2_acquire_job() { + if ((m2_shm_id >= 0) && (m2_shm_sem != 0)) { + if (sem_trywait(m2_shm_sem) == 0) { + return 1; + } + if (errno == EAGAIN) { + return 0; + } + } + return -1; +} + +static int +m2_release_job() { + if ((m2_shm_id >= 0) && (m2_shm_sem != 0)) { + if (sem_post(m2_shm_sem) == 0) { + return 0; + } + } + return -1; +} + +/* + * job adjust mode + * + * Possible values: + * ADJUST_M1 - adjustment by system load (default) + * ADJUST_M2 - fixed limit of jobs for the group of nested dmakes + * ADJUST_NONE - no adjustment - fixed limit of jobs for the current dmake + */ +static enum { + ADJUST_UNKNOWN, + ADJUST_M1, + ADJUST_M2, + ADJUST_NONE +} job_adjust_mode = ADJUST_UNKNOWN; + +/* + * void job_adjust_fini() + * + * Description: + * Cleans up job adjust data. + * + * Static variables: + * job_adjust_mode Current job adjust mode + */ +void +job_adjust_fini() { + if (job_adjust_mode == ADJUST_M2) { + m2_fini(); + } +} + +/* + * void job_adjust_error() + * + * Description: + * Prints warning message, cleans up job adjust data, and disables job adjustment + * + * Environment: + * DMAKE_ADJUST_MAX_JOBS + * + * External functions: + * putenv() + * + * Static variables: + * job_adjust_mode Current job adjust mode + */ +static void +job_adjust_error() { + if (job_adjust_mode != ADJUST_NONE) { + /* cleanup internals */ + job_adjust_fini(); + + /* warning message for the user */ + warning(catgets(catd, 1, 339, "Encountered max jobs auto adjustment error - disabling auto adjustment.")); + + /* switch off job adjustment for the children */ + putenv(NOCATGETS("DMAKE_ADJUST_MAX_JOBS=NO")); + + /* and for this dmake */ + job_adjust_mode = ADJUST_NONE; + } +} + +/* + * void job_adjust_init() + * + * Description: + * Parses DMAKE_ADJUST_MAX_JOBS env variable + * and performs appropriate initializations. + * + * Environment: + * DMAKE_ADJUST_MAX_JOBS + * DMAKE_ADJUST_MAX_JOBS == "NO" - no adjustment + * DMAKE_ADJUST_MAX_JOBS == "M2" - M2 adjust mode + * other - M1 adjust mode + * + * External functions: + * getenv() + * + * Static variables: + * job_adjust_mode Current job adjust mode + */ +static void +job_adjust_init() { + if (job_adjust_mode == ADJUST_UNKNOWN) { + /* default mode */ + job_adjust_mode = ADJUST_M1; + + /* determine adjust mode */ + if (char *var = getenv(NOCATGETS("DMAKE_ADJUST_MAX_JOBS"))) { + if (strcasecmp(var, NOCATGETS("NO")) == 0) { + job_adjust_mode = ADJUST_NONE; + } else if (strcasecmp(var, NOCATGETS("M2")) == 0) { + job_adjust_mode = ADJUST_M2; + } + } + + /* M2 specific initialization */ + if (job_adjust_mode == ADJUST_M2) { + if (m2_init()) { + job_adjust_error(); + } + } + } +} + +#endif /* MAXJOBS_ADJUST_RFE4694000 */ +#endif /* TEAMWARE_MAKE_CMN */ + +/* + * distribute_process(char **commands, Property line) + * + * Parameters: + * commands argv vector of commands to execute + * + * Return value: + * The result of the execution + * + * Static variables used: + * process_running Set to the pid of the process set running + * #if defined (TEAMWARE_MAKE_CMN) && defined (MAXJOBS_ADJUST_RFE4694000) + * job_adjust_mode Current job adjust mode + * #endif + */ +static Doname +distribute_process(char **commands, Property line) +{ + static unsigned file_number = 0; + wchar_t string[MAXPATHLEN]; + char mbstring[MAXPATHLEN]; + int filed; + int res; + int tmp_index; + char *tmp_index_str_ptr; + +#if !defined (TEAMWARE_MAKE_CMN) || !defined (MAXJOBS_ADJUST_RFE4694000) + while (parallel_process_cnt >= pmake_max_jobs) { + await_parallel(false); + finish_children(true); + } +#else /* TEAMWARE_MAKE_CMN && MAXJOBS_ADJUST_RFE4694000 */ + /* initialize adjust mode, if not initialized */ + if (job_adjust_mode == ADJUST_UNKNOWN) { + job_adjust_init(); + } + + /* actions depend on adjust mode */ + switch (job_adjust_mode) { + case ADJUST_M1: + while (parallel_process_cnt >= adjust_pmake_max_jobs (pmake_max_jobs)) { + await_parallel(false); + finish_children(true); + } + break; + case ADJUST_M2: + if ((res = m2_acquire_job()) == 0) { + if (parallel_process_cnt > 0) { + await_parallel(false); + finish_children(true); + + if ((res = m2_acquire_job()) == 0) { + return build_serial; + } + } else { + return build_serial; + } + } + if (res < 0) { + /* job adjustment error */ + job_adjust_error(); + + /* no adjustment */ + while (parallel_process_cnt >= pmake_max_jobs) { + await_parallel(false); + finish_children(true); + } + } + break; + default: + while (parallel_process_cnt >= pmake_max_jobs) { + await_parallel(false); + finish_children(true); + } + } +#endif /* TEAMWARE_MAKE_CMN && MAXJOBS_ADJUST_RFE4694000 */ +#ifdef DISTRIBUTED + if (send_mtool_msgs) { + send_job_start_msg(line); + } +#endif +#ifdef DISTRIBUTED + setvar_envvar((Avo_DoJobMsg *)NULL); +#else + setvar_envvar(); +#endif + /* + * Tell the user what DMake is doing. + */ + if (!silent && output_mode != txt2_mode) { + /* + * Print local_host --> x job(s). + */ + (void) fprintf(stdout, + catgets(catd, 1, 325, "%s --> %d %s\n"), + local_host, + parallel_process_cnt + 1, + (parallel_process_cnt == 0) ? catgets(catd, 1, 124, "job") : catgets(catd, 1, 125, "jobs")); + + /* Print command line(s). */ + tmp_index = 0; + while (commands[tmp_index] != NULL) { + /* No @ char. */ + /* XXX - need to add [2] when + prefix is added */ + if ((commands[tmp_index][0] != (int) at_char) && + (commands[tmp_index][1] != (int) at_char)) { + tmp_index_str_ptr = commands[tmp_index]; + if (*tmp_index_str_ptr == (int) hyphen_char) { + tmp_index_str_ptr++; + } + (void) fprintf(stdout, "%s\n", tmp_index_str_ptr); + } + tmp_index++; + } + (void) fflush(stdout); + } + + (void) sprintf(mbstring, + NOCATGETS("%s/dmake.stdout.%d.%d.XXXXXX"), + tmpdir, + getpid(), + file_number++); + + mktemp(mbstring); + + stdout_file = strdup(mbstring); + stderr_file = NULL; +#if defined (TEAMWARE_MAKE_CMN) && defined(REDIRECT_ERR) + if (!out_err_same) { + (void) sprintf(mbstring, + NOCATGETS("%s/dmake.stderr.%d.%d.XXXXXX"), + tmpdir, + getpid(), + file_number++); + + mktemp(mbstring); + + stderr_file = strdup(mbstring); + } +#endif + +#ifdef SGE_SUPPORT + if (grid) { + static char *dir4gridscripts = NULL; + static char *hostName = NULL; + if (dir4gridscripts == NULL) { + Name dmakeOdir_name, dmakeOdir_value; + Property prop; + MBSTOWCS(wcs_buffer, NOCATGETS("DMAKE_ODIR")); + dmakeOdir_name = GETNAME(wcs_buffer, FIND_LENGTH); + if (((prop = get_prop(dmakeOdir_name->prop, macro_prop)) != NULL) && + ((dmakeOdir_value = prop->body.macro.value) != NULL)) { + dir4gridscripts = dmakeOdir_value->string_mb; + } + dir4gridscripts = Avo_PathNames::pathname_output_directory(dir4gridscripts); + hostName = Avo_PathNames::pathname_local_host(); + } + (void) sprintf(script_file, + NOCATGETS("%s/dmake.script.%s.%d.%d.XXXXXX"), + dir4gridscripts, + hostName, + getpid(), + file_number++); + } +#endif /* SGE_SUPPORT */ + process_running = run_rule_commands(local_host, commands); + + return build_running; +} + +/* + * doname_parallel(target, do_get, implicit) + * + * Processes the given target and finishes up any parallel + * processes left running. + * + * Return value: + * Result of target build + * + * Parameters: + * target Target to build + * do_get True if sccs get to be done + * implicit True if this is an implicit target + */ +Doname +doname_parallel(Name target, Boolean do_get, Boolean implicit) +{ + Doname result; + + result = doname_check(target, do_get, implicit, false); + if (result == build_ok || result == build_failed) { + return result; + } + finish_running(); + return (Doname) target->state; +} + +/* + * doname_subtree(target, do_get, implicit) + * + * Completely computes an object and its dependents for a + * serial subtree build. + * + * Parameters: + * target Target to build + * do_get True if sccs get to be done + * implicit True if this is an implicit target + * + * Static variables used: + * running_tail Tail of the list of running processes + * + * Global variables used: + * running_list The list of running processes + */ +static void +doname_subtree(Name target, Boolean do_get, Boolean implicit) +{ + Running save_running_list; + Running *save_running_tail; + + save_running_list = running_list; + save_running_tail = running_tail; + running_list = NULL; + running_tail = &running_list; + target->state = build_subtree; + target->checking_subtree = true; + while(doname_check(target, do_get, implicit, false) == build_running) { + target->checking_subtree = false; + finish_running(); + target->state = build_subtree; + } + target->checking_subtree = false; + running_list = save_running_list; + running_tail = save_running_tail; +} + +/* + * finish_running() + * + * Keeps processing until the running_list is emptied out. + * + * Parameters: + * + * Global variables used: + * running_list The list of running processes + */ +void +finish_running(void) +{ + while (running_list != NULL) { +#ifdef DISTRIBUTED + if (dmake_mode_type == distributed_mode) { + if ((just_did_subtree) || + (parallel_process_cnt == 0)) { + just_did_subtree = false; + } else { + (void) await_dist(false); + finish_children(true); + } + } else +#endif + { + await_parallel(false); + finish_children(true); + } + if (running_list != NULL) { + process_next(); + } + } +} + +/* + * process_next() + * + * Searches the running list for any targets which can start processing. + * This can be a pending target, a serial target, or a subtree target. + * + * Parameters: + * + * Static variables used: + * running_tail The end of the list of running procs + * subtree_conflict A target which conflicts with a subtree + * subtree_conflict2 The other target which conflicts + * + * Global variables used: + * commands_done True if commands executed + * debug_level Controls debug output + * parallel_process_cnt Number of parallel process running + * recursion_level Indentation for debug output + * running_list List of running processes + */ +static void +process_next(void) +{ + Running rp; + Running *rp_prev; + Property line; + Chain target_group; + Dependency dep; + Boolean quiescent = true; + Running *subtree_target; + Boolean saved_commands_done; + Property *conditionals; + + subtree_target = NULL; + subtree_conflict = NULL; + subtree_conflict2 = NULL; + /* + * If nothing currently running, build a serial target, if any. + */ +start_loop_1: + for (rp_prev = &running_list, rp = running_list; + rp != NULL && parallel_process_cnt == 0; + rp = rp->next) { + if (rp->state == build_serial) { + *rp_prev = rp->next; + if (rp->next == NULL) { + running_tail = rp_prev; + } + recursion_level = rp->recursion_level; + rp->target->state = build_pending; + (void) doname_check(rp->target, + rp->do_get, + rp->implicit, + false); + quiescent = false; + delete_running_struct(rp); + goto start_loop_1; + } else { + rp_prev = &rp->next; + } + } + /* + * Find a target to build. The target must be pending, have all + * its dependencies built, and not be in a target group with a target + * currently building. + */ +start_loop_2: + for (rp_prev = &running_list, rp = running_list; + rp != NULL; + rp = rp->next) { + if (!(rp->state == build_pending || + rp->state == build_subtree)) { + quiescent = false; + rp_prev = &rp->next; + } else if (rp->state == build_pending) { + line = get_prop(rp->target->prop, line_prop); + for (dep = line->body.line.dependencies; + dep != NULL; + dep = dep->next) { + if (dep->name->state == build_running || + dep->name->state == build_pending || + dep->name->state == build_serial) { + break; + } + } + if (dep == NULL) { + for (target_group = line->body.line.target_group; + target_group != NULL; + target_group = target_group->next) { + if (is_running(target_group->name)) { + break; + } + } + if (target_group == NULL) { + *rp_prev = rp->next; + if (rp->next == NULL) { + running_tail = rp_prev; + } + recursion_level = rp->recursion_level; + rp->target->state = rp->redo ? + build_dont_know : build_pending; + saved_commands_done = commands_done; + conditionals = + set_conditionals + (rp->conditional_cnt, + rp->conditional_targets); + rp->target->dont_activate_cond_values = true; + if ((doname_check(rp->target, + rp->do_get, + rp->implicit, + rp->target->has_target_prop ? true : false) != + build_running) && + !commands_done) { + commands_done = + saved_commands_done; + } + rp->target->dont_activate_cond_values = false; + reset_conditionals + (rp->conditional_cnt, + rp->conditional_targets, + conditionals); + quiescent = false; + delete_running_struct(rp); + goto start_loop_2; + } else { + rp_prev = &rp->next; + } + } else { + rp_prev = &rp->next; + } + } else { + rp_prev = &rp->next; + } + } + /* + * If nothing has been found to build and there exists a subtree + * target with no dependency conflicts, build it. + */ + if (quiescent) { +start_loop_3: + for (rp_prev = &running_list, rp = running_list; + rp != NULL; + rp = rp->next) { + if (rp->state == build_subtree) { + if (!dependency_conflict(rp->target)) { + *rp_prev = rp->next; + if (rp->next == NULL) { + running_tail = rp_prev; + } + recursion_level = rp->recursion_level; + doname_subtree(rp->target, + rp->do_get, + rp->implicit); +#ifdef DISTRIBUTED + just_did_subtree = true; +#endif + quiescent = false; + delete_running_struct(rp); + goto start_loop_3; + } else { + subtree_target = rp_prev; + rp_prev = &rp->next; + } + } else { + rp_prev = &rp->next; + } + } + } + /* + * If still nothing found to build, we either have a deadlock + * or a subtree with a dependency conflict with something waiting + * to build. + */ + if (quiescent) { + if (subtree_target == NULL) { + fatal(catgets(catd, 1, 126, "Internal error: deadlock detected in process_next")); + } else { + rp = *subtree_target; + if (debug_level > 0) { + warning(catgets(catd, 1, 127, "Conditional macro conflict encountered for %s between %s and %s"), + subtree_conflict2->string_mb, + rp->target->string_mb, + subtree_conflict->string_mb); + } + *subtree_target = (*subtree_target)->next; + if (rp->next == NULL) { + running_tail = subtree_target; + } + recursion_level = rp->recursion_level; + doname_subtree(rp->target, rp->do_get, rp->implicit); +#ifdef DISTRIBUTED + just_did_subtree = true; +#endif + delete_running_struct(rp); + } + } +} + +/* + * set_conditionals(cnt, targets) + * + * Sets the conditional macros for the targets given in the array of + * targets. The old macro values are returned in an array of + * Properties for later resetting. + * + * Return value: + * Array of conditional macro settings + * + * Parameters: + * cnt Number of targets + * targets Array of targets + */ +static Property * +set_conditionals(int cnt, Name *targets) +{ + Property *locals, *lp; + Name *tp; + + locals = (Property *) getmem(cnt * sizeof(Property)); + for (lp = locals, tp = targets; + cnt > 0; + cnt--, lp++, tp++) { + *lp = (Property) getmem((*tp)->conditional_cnt * + sizeof(struct _Property)); + set_locals(*tp, *lp); + } + return locals; +} + +/* + * reset_conditionals(cnt, targets, locals) + * + * Resets the conditional macros as saved in the given array of + * Properties. The resets are done in reverse order. Afterwards the + * data structures are freed. + * + * Parameters: + * cnt Number of targets + * targets Array of targets + * locals Array of dependency macro settings + */ +static void +reset_conditionals(int cnt, Name *targets, Property *locals) +{ + Name *tp; + Property *lp; + + for (tp = targets + (cnt - 1), lp = locals + (cnt - 1); + cnt > 0; + cnt--, tp--, lp--) { + reset_locals(*tp, + *lp, + get_prop((*tp)->prop, conditional_prop), + 0); + retmem_mb((caddr_t) *lp); + } + retmem_mb((caddr_t) locals); +} + +/* + * dependency_conflict(target) + * + * Returns true if there is an intersection between + * the subtree of the target and any dependents of the pending targets. + * + * Return value: + * True if conflict found + * + * Parameters: + * target Subtree target to check + * + * Static variables used: + * subtree_conflict Target conflict found + * subtree_conflict2 Second conflict found + * + * Global variables used: + * running_list List of running processes + * wait_name .WAIT, not a real dependency + */ +static Boolean +dependency_conflict(Name target) +{ + Property line; + Property pending_line; + Dependency dp; + Dependency pending_dp; + Running rp; + + /* Return if we are already checking this target */ + if (target->checking_subtree) { + return false; + } + target->checking_subtree = true; + line = get_prop(target->prop, line_prop); + if (line == NULL) { + target->checking_subtree = false; + return false; + } + /* Check each dependency of the target for conflicts */ + for (dp = line->body.line.dependencies; dp != NULL; dp = dp->next) { + /* Ignore .WAIT dependency */ + if (dp->name == wait_name) { + continue; + } + /* + * For each pending target, look for a dependency which + * is the same as a dependency of the subtree target. Since + * we can't build the subtree until all pending targets have + * finished which depend on the same dependency, this is + * a conflict. + */ + for (rp = running_list; rp != NULL; rp = rp->next) { + if (rp->state == build_pending) { + pending_line = get_prop(rp->target->prop, + line_prop); + if (pending_line == NULL) { + continue; + } + for(pending_dp = pending_line-> + body.line.dependencies; + pending_dp != NULL; + pending_dp = pending_dp->next) { + if (dp->name == pending_dp->name) { + target->checking_subtree + = false; + subtree_conflict = rp->target; + subtree_conflict2 = dp->name; + return true; + } + } + } + } + if (dependency_conflict(dp->name)) { + target->checking_subtree = false; + return true; + } + } + target->checking_subtree = false; + return false; +} + +/* + * await_parallel(waitflg) + * + * Waits for parallel children to exit and finishes their processing. + * If waitflg is false, the function returns after update_delay. + * + * Parameters: + * waitflg dwight + */ +void +await_parallel(Boolean waitflg) +{ +#ifdef _CHECK_UPDATE_H + static int number_of_unknown_children = 0; +#endif /* _CHECK_UPDATE_H */ + Boolean nohang; + pid_t pid; + int status; + Running rp; + int waiterr; + + nohang = false; + for ( ; ; ) { + if (!nohang) { + (void) alarm((int) update_delay); + } + pid = waitpid((pid_t)-1, + &status, + nohang ? WNOHANG : 0); + waiterr = errno; + if (!nohang) { + (void) alarm(0); + } + if (pid <= 0) { + if (waiterr == EINTR) { + if (waitflg) { + continue; + } else { + return; + } + } else { + return; + } + } + for (rp = running_list; + (rp != NULL) && (rp->pid != pid); + rp = rp->next) { + ; + } + if (rp == NULL) { +#ifdef _CHECK_UPDATE_H + /* Ignore first child - it is check_update */ + if (number_of_unknown_children <= 0) { + number_of_unknown_children = 1; + return; + } +#endif /* _CHECK_UPDATE_H */ + if (send_mtool_msgs) { + continue; + } else { + fatal(catgets(catd, 1, 128, "Internal error: returned child pid not in running_list")); + } + } else { + rp->state = (WIFEXITED(status) && WEXITSTATUS(status) == 0) ? build_ok : build_failed; + } + nohang = true; + parallel_process_cnt--; + +#if defined (TEAMWARE_MAKE_CMN) && defined (MAXJOBS_ADJUST_RFE4694000) + if (job_adjust_mode == ADJUST_M2) { + if (m2_release_job()) { + job_adjust_error(); + } + } +#endif + } +} + +/* + * finish_children(docheck) + * + * Finishes the processing for all targets which were running + * and have now completed. + * + * Parameters: + * docheck Completely check the finished target + * + * Static variables used: + * running_tail The tail of the running list + * + * Global variables used: + * continue_after_error -k flag + * fatal_in_progress True if we are finishing up after fatal err + * running_list List of running processes + */ +void +finish_children(Boolean docheck) +{ + int cmds_length; + Property line; + Property line2; + struct stat out_buf; + Running rp; + Running *rp_prev; + Cmd_line rule; + Boolean silent_flag; + + for (rp_prev = &running_list, rp = running_list; + rp != NULL; + rp = rp->next) { +bypass_for_loop_inc_4: + /* + * If the state is ok or failed, then this target has + * finished building. + * In parallel_mode, output the accumulated stdout/stderr. + * Read the auto dependency stuff, handle a failed build, + * update the target, then finish the doname process for + * that target. + */ + if (rp->state == build_ok || rp->state == build_failed) { + *rp_prev = rp->next; + if (rp->next == NULL) { + running_tail = rp_prev; + } + if ((line2 = rp->command) == NULL) { + line2 = get_prop(rp->target->prop, line_prop); + } + if (dmake_mode_type == distributed_mode) { + if (rp->make_refd) { + maybe_reread_make_state(); + } + } else { + /* + * Send an Avo_MToolJobResultMsg to maketool. + */ +#ifdef DISTRIBUTED + if (send_mtool_msgs) { + send_job_result_msg(rp); + } +#endif + /* + * Check if there were any job output + * from the parallel build. + */ + if (rp->stdout_file != NULL) { + if (stat(rp->stdout_file, &out_buf) < 0) { + fatal(catgets(catd, 1, 130, "stat of %s failed: %s"), + rp->stdout_file, + errmsg(errno)); + } + if ((line2 != NULL) && + (out_buf.st_size > 0)) { + cmds_length = 0; + for (rule = line2->body.line.command_used, + silent_flag = silent; + rule != NULL; + rule = rule->next) { + cmds_length += rule->command_line->hash.length + 1; + silent_flag = BOOLEAN(silent_flag || rule->silent); + } + if (out_buf.st_size != cmds_length || silent_flag || + output_mode == txt2_mode) { + dump_out_file(rp->stdout_file, false); + } + } + (void) unlink(rp->stdout_file); + retmem_mb(rp->stdout_file); + rp->stdout_file = NULL; + } +#if defined(REDIRECT_ERR) + if (!out_err_same && (rp->stderr_file != NULL)) { + if (stat(rp->stderr_file, &out_buf) < 0) { + fatal(catgets(catd, 1, 130, "stat of %s failed: %s"), + rp->stderr_file, + errmsg(errno)); + } + if ((line2 != NULL) && + (out_buf.st_size > 0)) { + dump_out_file(rp->stderr_file, true); + } + (void) unlink(rp->stderr_file); + retmem_mb(rp->stderr_file); + rp->stderr_file = NULL; + } +#endif + } + check_state(rp->temp_file); + if (rp->temp_file != NULL) { + free_name(rp->temp_file); + } + rp->temp_file = NULL; + if (rp->state == build_failed) { + line = get_prop(rp->target->prop, line_prop); + if (line != NULL) { + line->body.line.command_used = NULL; + } + if (continue_after_error || + fatal_in_progress || + !docheck) { + warning(catgets(catd, 1, 256, "Command failed for target `%s'"), + rp->command ? line2->body.line.target->string_mb : rp->target->string_mb); + build_failed_seen = true; + } else { + /* + * XXX??? - DMake needs to exit(), + * but shouldn't call fatal(). + */ +#ifdef PRINT_EXIT_STATUS + warning(NOCATGETS("I'm in finish_children. rp->state == build_failed.")); +#endif + + fatal(catgets(catd, 1, 258, "Command failed for target `%s'"), + rp->command ? line2->body.line.target->string_mb : rp->target->string_mb); + } + } + if (!docheck) { + delete_running_struct(rp); + rp = *rp_prev; + if (rp == NULL) { + break; + } else { + goto bypass_for_loop_inc_4; + } + } + update_target(get_prop(rp->target->prop, line_prop), + rp->state); + finish_doname(rp); + delete_running_struct(rp); + rp = *rp_prev; + if (rp == NULL) { + break; + } else { + goto bypass_for_loop_inc_4; + } + } else { + rp_prev = &rp->next; + } + } +} + +/* + * dump_out_file(filename, err) + * + * Write the contents of the file to stdout, then unlink the file. + * + * Parameters: + * filename Name of temp file containing output + * + * Global variables used: + */ +static void +dump_out_file(char *filename, Boolean err) +{ + int chars_read; + char copybuf[BUFSIZ]; + int fd; + int out_fd = (err ? 2 : 1); + + if ((fd = open(filename, O_RDONLY)) < 0) { + fatal(catgets(catd, 1, 141, "open failed for output file %s: %s"), + filename, + errmsg(errno)); + } + if (!silent && output_mode != txt2_mode) { + (void) fprintf(err ? stderr : stdout, + err ? + catgets(catd, 1, 338, "%s --> Job errors\n") : + catgets(catd, 1, 259, "%s --> Job output\n"), + local_host); + (void) fflush(err ? stderr : stdout); + } + for (chars_read = read(fd, copybuf, BUFSIZ); + chars_read > 0; + chars_read = read(fd, copybuf, BUFSIZ)) { + /* + * Read buffers from the source file until end or error. + */ + if (write(out_fd, copybuf, chars_read) < 0) { + fatal(catgets(catd, 1, 260, "write failed for output file %s: %s"), + filename, + errmsg(errno)); + } + } + (void) close(fd); + (void) unlink(filename); +} + +/* + * finish_doname(rp) + * + * Completes the processing for a target which was left running. + * + * Parameters: + * rp Running list entry for target + * + * Global variables used: + * debug_level Debug flag + * recursion_level Indentation for debug output + */ +static void +finish_doname(Running rp) +{ + int auto_count = rp->auto_count; + Name *automatics = rp->automatics; + Doname result = rp->state; + Name target = rp->target; + Name true_target = rp->true_target; + Property *conditionals; + + recursion_level = rp->recursion_level; + if (result == build_ok) { + if (true_target == NULL) { + (void) printf(NOCATGETS("Target = %s\n"), target->string_mb); + (void) printf(NOCATGETS(" State = %d\n"), result); + fatal(NOCATGETS("Internal error: NULL true_target in finish_doname")); + } + /* If all went OK, set a nice timestamp */ + if (true_target->stat.time == file_doesnt_exist) { + true_target->stat.time = file_max_time; + } + } + target->state = result; + if (target->is_member) { + Property member; + + /* Propagate the timestamp from the member file to the member */ + if ((target->stat.time != file_max_time) && + ((member = get_prop(target->prop, member_prop)) != NULL) && + (exists(member->body.member.member) > file_doesnt_exist)) { + target->stat.time = +/* + exists(member->body.member.member); + */ + member->body.member.member->stat.time; + } + } + /* + * Check if we found any new auto dependencies when we + * built the target. + */ + if ((result == build_ok) && check_auto_dependencies(target, + auto_count, + automatics)) { + if (debug_level > 0) { + (void) printf(catgets(catd, 1, 261, "%*sTarget `%s' acquired new dependencies from build, checking all dependencies\n"), + recursion_level, + "", + true_target->string_mb); + } + target->rechecking_target = true; + target->state = build_running; + + /* [tolik, Tue Mar 25 1997] + * Fix for bug 4038824: + * command line options set by conditional macros get dropped + * rp->conditional_cnt and rp->conditional_targets must be copied + * to new 'rp' during add_pending(). Set_conditionals() stores + * rp->conditional_targets to the global variable 'conditional_targets' + * Add_pending() will use this variable to set up 'rp'. + */ + conditionals = set_conditionals(rp->conditional_cnt, rp->conditional_targets); + add_pending(target, + recursion_level, + rp->do_get, + rp->implicit, + true); + reset_conditionals(rp->conditional_cnt, rp->conditional_targets, conditionals); + } +} + +/* + * new_running_struct() + * + * Constructor for Running struct. Creates a structure and initializes + * its fields. + * + */ +static Running new_running_struct() +{ + Running rp; + + rp = ALLOC(Running); + rp->target = NULL; + rp->true_target = NULL; + rp->command = NULL; + rp->sprodep_value = NULL; + rp->sprodep_env = NULL; + rp->auto_count = 0; + rp->automatics = NULL; + rp->pid = -1; + rp->job_msg_id = -1; + rp->stdout_file = NULL; + rp->stderr_file = NULL; + rp->temp_file = NULL; + rp->next = NULL; + return rp; +} + +/* + * add_running(target, true_target, command, recursion_level, auto_count, + * automatics, do_get, implicit) + * + * Adds a record on the running list for this target, which + * was just spawned and is running. + * + * Parameters: + * target Target being built + * true_target True target for target + * command Running command. + * recursion_level Debug indentation level + * auto_count Count of automatic dependencies + * automatics List of automatic dependencies + * do_get Sccs get flag + * implicit Implicit flag + * + * Static variables used: + * running_tail Tail of running list + * process_running PID of process + * + * Global variables used: + * current_line Current line for target + * current_target Current target being built + * stderr_file Temporary file for stdout + * stdout_file Temporary file for stdout + * temp_file_name Temporary file for auto dependencies + */ +void +add_running(Name target, Name true_target, Property command, int recursion_level, int auto_count, Name *automatics, Boolean do_get, Boolean implicit) +{ + Running rp; + Name *p; + + rp = new_running_struct(); + rp->state = build_running; + rp->target = target; + rp->true_target = true_target; + rp->command = command; + Property spro_val = get_prop(sunpro_dependencies->prop, macro_prop); + if(spro_val) { + rp->sprodep_value = spro_val->body.macro.value; + spro_val->body.macro.value = NULL; + spro_val = get_prop(sunpro_dependencies->prop, env_mem_prop); + if(spro_val) { + rp->sprodep_env = spro_val->body.env_mem.value; + spro_val->body.env_mem.value = NULL; + } + } + rp->recursion_level = recursion_level; + rp->do_get = do_get; + rp->implicit = implicit; + rp->auto_count = auto_count; + if (auto_count > 0) { + rp->automatics = (Name *) getmem(auto_count * sizeof (Name)); + for (p = rp->automatics; auto_count > 0; auto_count--) { + *p++ = *automatics++; + } + } else { + rp->automatics = NULL; + } +#ifdef DISTRIBUTED + if (dmake_mode_type == distributed_mode) { + rp->make_refd = called_make; + called_make = false; + } else +#endif + { + rp->pid = process_running; + process_running = -1; + childPid = -1; + } + rp->job_msg_id = job_msg_id; + rp->stdout_file = stdout_file; + rp->stderr_file = stderr_file; + rp->temp_file = temp_file_name; + rp->redo = false; + rp->next = NULL; + store_conditionals(rp); + stdout_file = NULL; + stderr_file = NULL; + temp_file_name = NULL; + current_target = NULL; + current_line = NULL; + *running_tail = rp; + running_tail = &rp->next; +} + +/* + * add_pending(target, recursion_level, do_get, implicit, redo) + * + * Adds a record on the running list for a pending target + * (waiting for its dependents to finish running). + * + * Parameters: + * target Target being built + * recursion_level Debug indentation level + * do_get Sccs get flag + * implicit Implicit flag + * redo True if this target is being redone + * + * Static variables used: + * running_tail Tail of running list + */ +void +add_pending(Name target, int recursion_level, Boolean do_get, Boolean implicit, Boolean redo) +{ + Running rp; + rp = new_running_struct(); + rp->state = build_pending; + rp->target = target; + rp->recursion_level = recursion_level; + rp->do_get = do_get; + rp->implicit = implicit; + rp->redo = redo; + store_conditionals(rp); + *running_tail = rp; + running_tail = &rp->next; +} + +/* + * add_serial(target, recursion_level, do_get, implicit) + * + * Adds a record on the running list for a target which must be + * executed in serial after others have finished. + * + * Parameters: + * target Target being built + * recursion_level Debug indentation level + * do_get Sccs get flag + * implicit Implicit flag + * + * Static variables used: + * running_tail Tail of running list + */ +void +add_serial(Name target, int recursion_level, Boolean do_get, Boolean implicit) +{ + Running rp; + + rp = new_running_struct(); + rp->target = target; + rp->recursion_level = recursion_level; + rp->do_get = do_get; + rp->implicit = implicit; + rp->state = build_serial; + rp->redo = false; + store_conditionals(rp); + *running_tail = rp; + running_tail = &rp->next; +} + +/* + * add_subtree(target, recursion_level, do_get, implicit) + * + * Adds a record on the running list for a target which must be + * executed in isolation after others have finished. + * + * Parameters: + * target Target being built + * recursion_level Debug indentation level + * do_get Sccs get flag + * implicit Implicit flag + * + * Static variables used: + * running_tail Tail of running list + */ +void +add_subtree(Name target, int recursion_level, Boolean do_get, Boolean implicit) +{ + Running rp; + + rp = new_running_struct(); + rp->target = target; + rp->recursion_level = recursion_level; + rp->do_get = do_get; + rp->implicit = implicit; + rp->state = build_subtree; + rp->redo = false; + store_conditionals(rp); + *running_tail = rp; + running_tail = &rp->next; +} + +/* + * store_conditionals(rp) + * + * Creates an array of the currently active targets with conditional + * macros (found in the chain conditional_targets) and puts that + * array in the Running struct. + * + * Parameters: + * rp Running struct for storing chain + * + * Global variables used: + * conditional_targets Chain of current dynamic conditionals + */ +static void +store_conditionals(Running rp) +{ + int cnt; + Chain cond_name; + + if (conditional_targets == NULL) { + rp->conditional_cnt = 0; + rp->conditional_targets = NULL; + return; + } + cnt = 0; + for (cond_name = conditional_targets; + cond_name != NULL; + cond_name = cond_name->next) { + cnt++; + } + rp->conditional_cnt = cnt; + rp->conditional_targets = (Name *) getmem(cnt * sizeof(Name)); + for (cond_name = conditional_targets; + cond_name != NULL; + cond_name = cond_name->next) { + rp->conditional_targets[--cnt] = cond_name->name; + } +} + +/* + * parallel_ok(target, line_prop_must_exists) + * + * Returns true if the target can be run in parallel + * + * Return value: + * True if can run in parallel + * + * Parameters: + * target Target being tested + * + * Global variables used: + * all_parallel True if all targets default to parallel + * only_parallel True if no targets default to parallel + */ +Boolean +parallel_ok(Name target, Boolean line_prop_must_exists) +{ + Boolean assign; + Boolean make_refd; + Property line; + Cmd_line rule; + + assign = make_refd = false; + if (((line = get_prop(target->prop, line_prop)) == NULL) && + line_prop_must_exists) { + return false; + } + if (line != NULL) { + for (rule = line->body.line.command_used; + rule != NULL; + rule = rule->next) { + if (rule->assign) { + assign = true; + } else if (rule->make_refd) { + make_refd = true; + } + } + } + if (assign) { + return false; + } else if (target->parallel) { + return true; + } else if (target->no_parallel) { + return false; + } else if (all_parallel) { + return true; + } else if (only_parallel) { + return false; + } else if (make_refd) { + return false; + } else { + return true; + } +} + +/* + * is_running(target) + * + * Returns true if the target is running. + * + * Return value: + * True if target is running + * + * Parameters: + * target Target to check + * + * Global variables used: + * running_list List of running processes + */ +Boolean +is_running(Name target) +{ + Running rp; + + if (target->state != build_running) { + return false; + } + for (rp = running_list; + rp != NULL && target != rp->target; + rp = rp->next); + if (rp == NULL) { + return false; + } else { + return (rp->state == build_running) ? true : false; + } +} + +/* + * This function replaces the makesh binary. + */ + +#ifdef SGE_SUPPORT +#define DO_CHECK(f) if (f <= 0) { \ + fprintf(stderr, \ + catgets(catd, 1, 347, "Could not write to file: %s: %s\n"), \ + script_file, errmsg(errno)); \ + _exit(1); \ + } +#endif /* SGE_SUPPORT */ + +static pid_t +run_rule_commands(char *host, char **commands) +{ + Boolean always_exec; + Name command; + Boolean ignore; + int length; + Doname result; + Boolean silent_flag; +#ifdef SGE_SUPPORT + wchar_t *wcmd, *tmp_wcs_buffer = NULL; + char *cmd, *tmp_mbs_buffer = NULL; + FILE *scrfp; + Name shell = getvar(shell_name); +#else + wchar_t *tmp_wcs_buffer; +#endif /* SGE_SUPPORT */ + + childPid = fork(); + switch (childPid) { + case -1: /* Error */ + fatal(catgets(catd, 1, 337, "Could not fork child process for dmake job: %s"), + errmsg(errno)); + break; + case 0: /* Child */ + /* To control the processed targets list is not the child's business */ + running_list = NULL; +#if defined(REDIRECT_ERR) + if(out_err_same) { + redirect_io(stdout_file, (char*)NULL); + } else { + redirect_io(stdout_file, stderr_file); + } +#else + redirect_io(stdout_file, (char*)NULL); +#endif +#ifdef SGE_SUPPORT + if (grid) { + int fdes = mkstemp(script_file); + if ((fdes < 0) || (scrfp = fdopen(fdes, "w")) == NULL) { + fprintf(stderr, + catgets(catd, 1, 341, "Could not create file: %s: %s\n"), + script_file, errmsg(errno)); + _exit(1); + } + if (IS_EQUAL(shell->string_mb, "")) { + shell = shell_name; + } + } +#endif /* SGE_SUPPORT */ + for (commands = commands; + (*commands != (char *)NULL); + commands++) { + silent_flag = silent; + ignore = false; + always_exec = false; + while ((**commands == (int) at_char) || + (**commands == (int) hyphen_char) || + (**commands == (int) plus_char)) { + if (**commands == (int) at_char) { + silent_flag = true; + } + if (**commands == (int) hyphen_char) { + ignore = true; + } + if (**commands == (int) plus_char) { + always_exec = true; + } + (*commands)++; + } +#ifdef SGE_SUPPORT + if (grid) { + if ((length = strlen(*commands)) >= MAXPATHLEN / 2) { + wcmd = tmp_wcs_buffer = ALLOC_WC(length * 2 + 1); + (void) mbstowcs(tmp_wcs_buffer, *commands, length * 2 + 1); + } else { + MBSTOWCS(wcs_buffer, *commands); + wcmd = wcs_buffer; + cmd = mbs_buffer; + } + wchar_t *from = wcmd + wslen(wcmd); + wchar_t *to = from + (from - wcmd); + *to = (int) nul_char; + while (from > wcmd) { + *--to = *--from; + if (*from == (int) newline_char) { // newline symbols are already quoted + *--to = *--from; + } else if (wschr(char_semantics_char, *from)) { + *--to = (int) backslash_char; + } + } + if (length >= MAXPATHLEN*MB_LEN_MAX/2) { // sizeof(mbs_buffer) / 2 + cmd = tmp_mbs_buffer = getmem((length * MB_LEN_MAX * 2) + 1); + (void) wcstombs(tmp_mbs_buffer, to, (length * MB_LEN_MAX * 2) + 1); + } else { + WCSTOMBS(mbs_buffer, to); + cmd = mbs_buffer; + } + char *mbst, *mbend; + if ((length > 0) && + !silent_flag) { + for (mbst = cmd; (mbend = strstr(mbst, "\\\n")) != NULL; mbst = mbend + 2) { + *mbend = '\0'; + DO_CHECK(fprintf(scrfp, NOCATGETS("/usr/bin/printf '%%s\\n' %s\\\\\n"), mbst)); + *mbend = '\\'; + } + DO_CHECK(fprintf(scrfp, NOCATGETS("/usr/bin/printf '%%s\\n' %s\n"), mbst)); + } + if (!do_not_exec_rule || + !working_on_targets || + always_exec) { +#if defined(linux) + if (0 != strcmp(shell->string_mb, (char*)NOCATGETS("/bin/sh"))) { + DO_CHECK(fprintf(scrfp, NOCATGETS("%s -c %s\n"), shell->string_mb, cmd)); + } else +#endif + DO_CHECK(fprintf(scrfp, NOCATGETS("%s -ce %s\n"), shell->string_mb, cmd)); + DO_CHECK(fputs(NOCATGETS("__DMAKECMDEXITSTAT=$?\nif [ ${__DMAKECMDEXITSTAT} -ne 0 ]; then\n"), scrfp)); + if (ignore) { + DO_CHECK(fprintf(scrfp, NOCATGETS("\techo %s ${__DMAKECMDEXITSTAT} %s\n"), + catgets(catd, 1, 343, "\"*** Error code"), + catgets(catd, 1, 344, "(ignored)\""))); + } else { + DO_CHECK(fprintf(scrfp, NOCATGETS("\techo %s ${__DMAKECMDEXITSTAT}\n"), + catgets(catd, 1, 342, "\"*** Error code\""))); + } + if (silent_flag) { + DO_CHECK(fprintf(scrfp, NOCATGETS("\techo %s\n"), + catgets(catd, 1, 345, "The following command caused the error:"))); + for (mbst = cmd; (mbend = strstr(mbst, "\\\n")) != NULL; mbst = mbend + 2) { + *mbend = '\0'; + DO_CHECK(fprintf(scrfp, NOCATGETS("\t/usr/bin/printf '%%s\\n' %s\\\\\n"), mbst)); + *mbend = '\\'; + } + DO_CHECK(fprintf(scrfp, NOCATGETS("\t/usr/bin/printf '%%s\\n' %s\n"), mbst)); + } + if (!ignore) { + DO_CHECK(fputs(NOCATGETS("\texit ${__DMAKECMDEXITSTAT}\n"), scrfp)); + } + DO_CHECK(fputs(NOCATGETS("fi\n"), scrfp)); + } + if (tmp_wcs_buffer) { + retmem_mb(tmp_mbs_buffer); + tmp_mbs_buffer = NULL; + } + if (tmp_wcs_buffer) { + retmem(tmp_wcs_buffer); + tmp_wcs_buffer = NULL; + } + continue; + } +#endif /* SGE_SUPPORT */ + if ((length = strlen(*commands)) >= MAXPATHLEN) { + tmp_wcs_buffer = ALLOC_WC(length + 1); + (void) mbstowcs(tmp_wcs_buffer, *commands, length + 1); + command = GETNAME(tmp_wcs_buffer, FIND_LENGTH); + retmem(tmp_wcs_buffer); + } else { + MBSTOWCS(wcs_buffer, *commands); + command = GETNAME(wcs_buffer, FIND_LENGTH); + } + if ((command->hash.length > 0) && + !silent_flag) { + (void) printf("%s\n", command->string_mb); + } + result = dosys(command, + ignore, + false, + false, /* bugs #4085164 & #4990057 */ + /* BOOLEAN(silent_flag && ignore), */ + always_exec, + (Name) NULL, + false); + if (result == build_failed) { + if (silent_flag) { + (void) printf(catgets(catd, 1, 152, "The following command caused the error:\n%s\n"), command->string_mb); + } + if (!ignore) { + _exit(1); + } + } + } +#ifndef SGE_SUPPORT + _exit(0); +#else + if (!grid) { + _exit(0); + } + DO_CHECK(fputs(NOCATGETS("exit 0\n"), scrfp)); + if (fclose(scrfp) != 0) { + fprintf(stderr, + catgets(catd, 1, 346, "Could not close file: %s: %s\n"), + script_file, errmsg(errno)); + _exit(1); + } + { + +#define DEFAULT_QRSH_TRIES_NUMBER 1 +#define DEFAULT_QRSH_TIMEOUT 0 + + static char *sge_env_var = NULL; + static int qrsh_tries_number = DEFAULT_QRSH_TRIES_NUMBER; + static int qrsh_timeout = DEFAULT_QRSH_TIMEOUT; +#define SGE_DEBUG +#ifdef SGE_DEBUG + static Boolean do_not_remove = false; +#endif /* SGE_DEBUG */ + if (sge_env_var == NULL) { + sge_env_var = getenv(NOCATGETS("__SPRO_DMAKE_SGE_TRIES")); + if (sge_env_var != NULL) { + qrsh_tries_number = atoi(sge_env_var); + if (qrsh_tries_number < 1 || qrsh_tries_number > 9) { + qrsh_tries_number = DEFAULT_QRSH_TRIES_NUMBER; + } + } + sge_env_var = getenv(NOCATGETS("__SPRO_DMAKE_SGE_TIMEOUT")); + if (sge_env_var != NULL) { + qrsh_timeout = atoi(sge_env_var); + if (qrsh_timeout <= 0) { + qrsh_timeout = DEFAULT_QRSH_TIMEOUT; + } + } else { + sge_env_var = ""; + } +#ifdef SGE_DEBUG + sge_env_var = getenv(NOCATGETS("__SPRO_DMAKE_SGE_DEBUG")); + if (sge_env_var == NULL) { + sge_env_var = ""; + } + if (strstr(sge_env_var, NOCATGETS("noqrsh")) != NULL) + qrsh_tries_number = 0; + if (strstr(sge_env_var, NOCATGETS("donotremove")) != NULL) + do_not_remove = true; +#endif /* SGE_DEBUG */ + } + for (int i = qrsh_tries_number; ; i--) + if ((childPid = fork()) < 0) { + fatal(catgets(catd, 1, 348, "Could not fork child process for qrsh job: %s"), + errmsg(errno)); + _exit(1); + } else if (childPid == 0) { + enable_interrupt((void (*) (int))SIG_DFL); + if (i > 0) { + static char qrsh_cmd[50+MAXPATHLEN] = NOCATGETS("qrsh -cwd -V -noshell -nostdin /bin/sh "); + static char *fname_ptr = NULL; + static char *argv[] = { NOCATGETS("sh"), + NOCATGETS("-fce"), + qrsh_cmd, + NULL}; + if (fname_ptr == NULL) { + fname_ptr = qrsh_cmd + strlen(qrsh_cmd); + } + strcpy(fname_ptr, script_file); + (void) execve(NOCATGETS("/bin/sh"), argv, environ); + } else { + static char *argv[] = { NOCATGETS("sh"), + script_file, + NULL}; + (void) execve(NOCATGETS("/bin/sh"), argv, environ); + } + fprintf(stderr, + catgets(catd, 1, 349, "Could not load `qrsh': %s\n"), + errmsg(errno)); + _exit(1); + } else { +#if defined (HP_UX) || defined (linux) || defined (SUN5_0) + int status; +#else + union wait status; +#endif + pid_t pid; + while ((pid = wait(&status)) != childPid) { + if (pid == -1) { + fprintf(stderr, + catgets(catd, 1, 350, "wait() failed: %s\n"), + errmsg(errno)); + _exit(1); + } + } + if (status != 0 && i > 0) { + if (i > 1) { + sleep(qrsh_timeout); + } + continue; + } +#ifdef SGE_DEBUG + if (do_not_remove) { + if (status) { + fprintf(stderr, + NOCATGETS("SGE script failed: %s\n"), + script_file); + } + _exit(status ? 1 : 0); + } +#endif /* SGE_DEBUG */ + (void) unlink(script_file); + _exit(status ? 1 : 0); + } + } +#endif /* SGE_SUPPORT */ + break; + default: + break; + } + return childPid; +} + +static void +maybe_reread_make_state(void) +{ + /* Copying dosys()... */ + if (report_dependencies_level == 0) { + make_state->stat.time = file_no_time; + (void) exists(make_state); + if (make_state_before == make_state->stat.time) { + return; + } + makefile_type = reading_statefile; + if (read_trace_level > 1) { + trace_reader = true; + } + temp_file_number++; + (void) read_simple_file(make_state, + false, + false, + false, + false, + false, + true); + trace_reader = false; + } +} + +#ifdef DISTRIBUTED +/* + * Create and send an Avo_MToolJobResultMsg. + */ +static void +send_job_result_msg(Running rp) +{ + Avo_MToolJobResultMsg *msg; + RWCollectable *xdr_msg; + + msg = new Avo_MToolJobResultMsg(); + msg->setResult(rp->job_msg_id, + (rp->state == build_ok) ? 0 : 1, + DONE); + append_job_result_msg(msg, + rp->stdout_file, + rp->stderr_file); + + xdr_msg = (RWCollectable *)msg; + xdr(get_xdrs_ptr(), xdr_msg); + (void) fflush(get_mtool_msgs_fp()); + + delete msg; +} + +/* + * Append the stdout/err to Avo_MToolJobResultMsg. + */ +static void +append_job_result_msg(Avo_MToolJobResultMsg *msg, char *outFn, char *errFn) +{ + FILE *fp; + char line[MAXPATHLEN]; + + fp = fopen(outFn, "r"); + if (fp == NULL) { + /* Hmmm... what should we do here? */ + return; + } + while (fgets(line, MAXPATHLEN, fp) != NULL) { + if (line[strlen(line) - 1] == '\n') { + line[strlen(line) - 1] = '\0'; + } + msg->appendOutput(AVO_STRDUP(line)); + } + (void) fclose(fp); +} +#endif + +static void +delete_running_struct(Running rp) +{ + if ((rp->conditional_cnt > 0) && + (rp->conditional_targets != NULL)) { + retmem_mb((char *) rp->conditional_targets); + } +/**/ + if ((rp->auto_count > 0) && + (rp->automatics != NULL)) { + retmem_mb((char *) rp->automatics); + } +/**/ + if(rp->sprodep_value) { + free_name(rp->sprodep_value); + } + if(rp->sprodep_env) { + retmem_mb(rp->sprodep_env); + } + retmem_mb((char *) rp); + +} + +#endif + |