/*
* 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
* 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.
*/
/*
*
* Deal with the parallel processing
*/
/*
* Included files
*/
#include <errno.h> /* errno */
#include <fcntl.h>
#include <unistd.h>
#include <netdb.h>
#include <libintl.h>
/*
* Defined macros
*/
/*
* This const should be in avo_dms/include/AvoDmakeCommand.h
*/
/*
* typedefs & structs
*/
/*
* Static variables
*/
static int pmake_max_jobs = 0;
/*
* File table of contents
*/
static void maybe_reread_make_state(void);
static void process_next(void);
/*
* execute_parallel(line, waitflg)
*
* DMake 2.x:
* parallel mode: spawns a parallel process to execute the command group.
*
* Return value:
* The result of the execution
*
* Parameters:
* line The command group to execute
*/
{
int argcnt;
int cmd_options = 0;
char *cp;
int ignore;
char **p;
if ((pmake_max_jobs == 0) &&
(dmake_mode_type == parallel_mode)) {
if (local_host[0] == '\0') {
}
if (pmake_max_jobs <= 0) {
}
} 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.
*/
} else {
}
}
}
}
if ((dmake_mode_type == serial_mode) ||
return (execute_serial(line));
}
{
p = commands;
}
argcnt = 0;
continue;
}
if (vpath_defined) {
rule->command_line =
}
silent_flag = false;
ignore = 0;
return build_serial;
}
{
silent_flag = true;
}
if (rule->ignore_error) {
ignore++;
}
/* XXX - need to add support for + prefix */
if (silent_flag || ignore) {
ignore +
string_mb)) +
1);
cp = *p++;
if (silent_flag) {
}
if (ignore) {
*cp++ = (int) hyphen_char;
}
} else {
}
}
}
}
if ((argcnt == 0) ||
(report_dependencies_level > 0)) {
return build_ok;
}
{
*p = NULL;
if (res == build_running) {
}
/*
* Return only those memory that were specially allocated
* for part of commands.
*/
(commands[i][0] == (int) hyphen_char)) {
}
}
return res;
}
}
#include <unistd.h> /* sysconf(_SC_NPROCESSORS_ONLN) */
#include <semaphore.h> /* sem_init(), sem_trywait(), sem_post(), sem_destroy() */
/*
* 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
{
static int ncpu = 0;
int adjustment;
int adjusted_max_jobs;
if (ncpu <= 0) {
ncpu = 1;
}
}
if (ncpu > 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 int
m2_init() {
char *var;
/* compose tmp file name */
/* create tmp file */
if (fd < 0) {
return -1;
} else {
}
} else {
/* using existing semaphore */
}
/* combine IPC key */
return -1;
}
/* create shared memory */
return -1;
}
/* attach shared memory */
return -1;
}
/* root process */
if (var == 0) {
/* initialize semaphore */
return -1;
}
/* alloc memory for env variable */
return -1;
}
/* put key to env */
return -1;
}
}
return 0;
}
static void
m2_fini() {
if (m2_shm_id >= 0) {
/* determine the number of attached processes */
/* destroy semaphore */
if (m2_shm_sem != 0) {
(void) sem_destroy(m2_shm_sem);
}
/* destroy shared memory */
/* remove tmp file created for the key */
} 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;
}
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 {
/*
* 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 */
/* warning message for the user */
/* switch off job adjustment for the children */
/* and for this dmake */
}
}
/*
* 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 */
/* determine adjust mode */
}
}
/* M2 specific initialization */
if (job_adjust_mode == ADJUST_M2) {
if (m2_init()) {
}
}
}
}
/*
* 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
* job_adjust_mode Current job adjust mode
*/
static Doname
{
static unsigned file_number = 0;
int filed;
int res;
int tmp_index;
char *tmp_index_str_ptr;
/* initialize adjust mode, if not initialized */
if (job_adjust_mode == ADJUST_UNKNOWN) {
}
/* actions depend on adjust mode */
switch (job_adjust_mode) {
case ADJUST_M1:
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 */
/* 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);
}
}
/*
* Tell the user what DMake is doing.
*/
/*
* Print local_host --> x job(s).
*/
gettext("%s --> %d %s\n"),
parallel_process_cnt + 1,
/* Print command line(s). */
tmp_index = 0;
/* No @ char. */
/* XXX - need to add [2] when + prefix is added */
if (*tmp_index_str_ptr == (int) hyphen_char) {
}
}
tmp_index++;
}
}
"%s/dmake.stdout.%d.%d.XXXXXX",
getpid(),
file_number++);
stderr_file = NULL;
if (!out_err_same) {
"%s/dmake.stderr.%d.%d.XXXXXX",
getpid(),
file_number++);
}
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
*/
{
return result;
}
}
/*
* 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
{
running_list = NULL;
target->checking_subtree = true;
target->checking_subtree = false;
}
target->checking_subtree = false;
}
/*
* 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) {
{
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)
{
/*
* If nothing currently running, build a serial target, if any.
*/
}
false);
quiescent = false;
goto start_loop_1;
} else {
}
}
/*
* 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.
*/
quiescent = false;
break;
}
}
target_group != NULL;
break;
}
}
if (target_group == NULL) {
}
build_running) &&
!commands_done) {
}
quiescent = false;
goto start_loop_2;
} else {
}
} else {
}
} else {
}
}
/*
* If nothing has been found to build and there exists a subtree
* target with no dependency conflicts, build it.
*/
if (quiescent) {
}
quiescent = false;
goto start_loop_3;
} else {
}
} else {
}
}
}
/*
* 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) {
} else {
rp = *subtree_target;
if (debug_level > 0) {
}
}
}
}
}
/*
* 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 *
{
cnt > 0;
sizeof(struct _Property));
}
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
{
cnt > 0;
*lp,
0);
}
}
/*
* 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
{
/* Return if we are already checking this target */
if (target->checking_subtree) {
return false;
}
target->checking_subtree = true;
target->checking_subtree = false;
return false;
}
/* Check each dependency of the target for conflicts */
/* Ignore .WAIT dependency */
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.
*/
if (pending_line == NULL) {
continue;
}
for(pending_dp = pending_line->
pending_dp != NULL;
= false;
return true;
}
}
}
}
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
{
int status;
int waiterr;
nohang = false;
for ( ; ; ) {
if (!nohang) {
(void) alarm((int) update_delay);
}
&status,
if (!nohang) {
(void) alarm(0);
}
if (pid <= 0) {
if (waitflg) {
continue;
} else {
return;
}
} else {
return;
}
}
for (rp = running_list;
;
}
} else {
}
nohang = true;
if (job_adjust_mode == ADJUST_M2) {
if (m2_release_job()) {
}
}
}
}
/*
* 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
{
int cmds_length;
/*
* If the state is ok or failed, then this target has
* finished building.
* Read the auto dependency stuff, handle a failed build,
* update the target, then finish the doname process for
* that target.
*/
}
}
/*
* Check if there were any job output
* from the parallel build.
*/
}
cmds_length = 0;
}
output_mode == txt2_mode) {
}
}
}
}
}
}
}
}
if (continue_after_error ||
!docheck) {
build_failed_seen = true;
} else {
/*
* XXX??? - DMake needs to exit(),
* but shouldn't call fatal().
*/
#ifdef PRINT_EXIT_STATUS
warning("I'm in finish_children. rp->state == build_failed.");
#endif
}
}
if (!docheck) {
break;
} else {
goto bypass_for_loop_inc_4;
}
}
break;
} else {
goto bypass_for_loop_inc_4;
}
} else {
}
}
}
/*
* 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
{
int chars_read;
int fd;
}
err ?
gettext("%s --> Job errors\n") :
gettext("%s --> Job output\n"),
}
chars_read > 0;
/*
* Read buffers from the source file until end or error.
*/
}
}
}
/*
* 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
{
if (true_target == NULL) {
fatal("Internal error: NULL true_target in finish_doname");
}
/* If all went OK, set a nice timestamp */
}
}
/* Propagate the timestamp from the member file to the member */
/*
exists(member->body.member.member);
*/
}
}
/*
* Check if we found any new auto dependencies when we
* built the target.
*/
automatics)) {
if (debug_level > 0) {
(void) printf(gettext("%*sTarget `%s' acquired new dependencies from build, checking all dependencies\n"),
"",
}
target->rechecking_target = true;
/* [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'.
*/
true);
}
}
/*
* new_running_struct()
*
* Constructor for Running struct. Creates a structure and initializes
* its fields.
*
*/
{
rp->auto_count = 0;
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)
{
Name *p;
rp = new_running_struct();
if (auto_count > 0) {
*p++ = *automatics++;
}
} else {
}
{
process_running = -1;
childPid = -1;
}
stdout_file = NULL;
stderr_file = NULL;
current_line = NULL;
*running_tail = rp;
}
/*
* 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
{
rp = new_running_struct();
*running_tail = rp;
}
/*
* 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
{
rp = new_running_struct();
*running_tail = rp;
}
/*
* 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
{
rp = new_running_struct();
*running_tail = rp;
}
/*
* 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
{
int cnt;
if (conditional_targets == NULL) {
rp->conditional_cnt = 0;
return;
}
cnt = 0;
for (cond_name = conditional_targets;
cnt++;
}
for (cond_name = conditional_targets;
}
}
/*
* 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
*/
{
return false;
}
assign = true;
make_refd = true;
}
}
}
if (assign) {
return false;
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
*/
{
return false;
}
for (rp = running_list;
return false;
} else {
}
}
/*
* This function replaces the makesh binary.
*/
static pid_t
{
int length;
wchar_t *tmp_wcs_buffer;
switch (childPid) {
case -1: /* Error */
break;
case 0: /* Child */
/* To control the processed targets list is not the child's business */
running_list = NULL;
if(out_err_same) {
} else {
}
commands++) {
ignore = false;
always_exec = false;
(**commands == (int) hyphen_char) ||
silent_flag = true;
}
if (**commands == (int) hyphen_char) {
ignore = true;
}
always_exec = true;
}
(*commands)++;
}
} else {
}
!silent_flag) {
}
false,
false, /* bugs #4085164 & #4990057 */
/* BOOLEAN(silent_flag && ignore), */
if (result == build_failed) {
if (silent_flag) {
}
if (!ignore) {
_exit(1);
}
}
}
_exit(0);
break;
default:
break;
}
return childPid;
}
static void
maybe_reread_make_state(void)
{
/* Copying dosys()... */
if (report_dependencies_level == 0) {
(void) exists(make_state);
return;
}
if (read_trace_level > 1) {
trace_reader = true;
}
(void) read_simple_file(make_state,
false,
false,
false,
false,
false,
true);
trace_reader = false;
}
}
static void
{
if ((rp->conditional_cnt > 0) &&
}
/**/
if ((rp->auto_count > 0) &&
}
/**/
if(rp->sprodep_value) {
}
if(rp->sprodep_env) {
}
}