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/doname.cc | |
download | sunmake-orig.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/doname.cc')
-rw-r--r-- | usr/src/make_src/Make/bin/make/common/doname.cc | 3786 |
1 files changed, 3786 insertions, 0 deletions
diff --git a/usr/src/make_src/Make/bin/make/common/doname.cc b/usr/src/make_src/Make/bin/make/common/doname.cc new file mode 100644 index 0000000..c06ba2f --- /dev/null +++ b/usr/src/make_src/Make/bin/make/common/doname.cc @@ -0,0 +1,3786 @@ +/* + * 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 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * @(#)doname.cc 1.115 06/12/12 + */ + +#pragma ident "@(#)doname.cc 1.115 06/12/12" + +/* + * doname.c + * + * Figure out which targets are out of date and rebuild them + */ + +/* + * Included files + */ +#include <avo/avo_alloca.h> /* alloca() */ +#if defined(TEAMWARE_MAKE_CMN) +#include <avo/util.h> /* avo_get_user(), avo_hostname() */ +#endif + +#if defined(DISTRIBUTED) || defined(MAKETOOL) /* tolik */ +# include <avo/strings.h> /* AVO_STRDUP() */ +# include <dm/Avo_MToolJobResultMsg.h> +# include <dm/Avo_MToolJobStartMsg.h> +# include <dm/Avo_MToolRsrcInfoMsg.h> +# include <dm/Avo_macro_defs.h> /* AVO_BLOCK_INTERUPTS & AVO_UNBLOCK_INTERUPTS */ +# include <dmthread/Avo_ServerState.h> +# include <rw/pstream.h> +# include <rw/xdrstrea.h> +#endif + +#include <fcntl.h> +#include <mk/defs.h> +#include <mksh/i18n.h> /* get_char_semantics_value() */ +#include <mksh/macro.h> /* getvar(), expand_value() */ +#include <mksh/misc.h> /* getmem() */ +#include <poll.h> + +#ifdef PARALLEL +# include <rx/api.h> +#endif + +#include <signal.h> + +#ifndef HP_UX +# include <stropts.h> +#endif + +#include <sys/errno.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/utsname.h> /* uname() */ +#include <sys/wait.h> +#include <unistd.h> /* close() */ + +/* + * Defined macros + */ +#ifndef PARALLEL +# define LOCALHOST "localhost" +#endif + +#define MAXRULES 100 + +#if defined(DISTRIBUTED) || defined(MAKETOOL) /* tolik */ +#define SEND_MTOOL_MSG(cmds) \ + if (send_mtool_msgs) { \ + cmds \ + } +#else +#define SEND_MTOOL_MSG(cmds) +#endif + +// Sleep for .1 seconds between stat()'s +const int STAT_RETRY_SLEEP_TIME = 100000; + +/* + * typedefs & structs + */ + +/* + * Static variables + */ +static char hostName[MAXNAMELEN] = ""; +static char userName[MAXNAMELEN] = ""; + +#if defined(DISTRIBUTED) || defined(MAKETOOL) /* tolik */ + static FILE *mtool_msgs_fp; + static XDR xdrs; + static int sent_rsrc_info_msg = 0; +#endif + +static int second_pass = 0; + +/* + * File table of contents + */ +extern Doname doname_check(register Name target, register Boolean do_get, register Boolean implicit, register Boolean automatic); +extern Doname doname(register Name target, register Boolean do_get, register Boolean implicit, register Boolean automatic); +static Boolean check_dependencies(Doname *result, Property line, Boolean do_get, Name target, Name true_target, Boolean doing_subtree, Chain *out_of_date_tail, Property old_locals, Boolean implicit, Property *command, Name less, Boolean rechecking_target, Boolean recheck_conditionals); +void dynamic_dependencies(Name target); +static Doname run_command(register Property line, Boolean print_machine); +extern Doname execute_serial(Property line); +extern Name vpath_translation(register Name cmd); +extern void check_state(Name temp_file_name); +static void read_dependency_file(register Name filename); +static void check_read_state_file(void); +static void do_assign(register Name line, register Name target); +static void build_command_strings(Name target, register Property line); +static Doname touch_command(register Property line, register Name target, Doname result); +extern void update_target(Property line, Doname result); +static Doname sccs_get(register Name target, register Property *command); +extern void read_directory_of_file(register Name file); +static void add_pattern_conditionals(register Name target); +extern void set_locals(register Name target, register Property old_locals); +extern void reset_locals(register Name target, register Property old_locals, register Property conditional, register int index); +extern Boolean check_auto_dependencies(Name target, int auto_count, Name *automatics); +static void delete_query_chain(Chain ch); + +// From read2.cc +extern Name normalize_name(register wchar_t *name_string, register int length); + + +#if defined(DISTRIBUTED) || defined(MAKETOOL) /* tolik */ + static void append_job_result_msg(Avo_MToolJobResultMsg *job_result_msg); + static int pollResults(char *outFn, char *errFn, char *hostNm); + static void pollResultsAction(char *outFn, char *errFn); + static void rxmGetNextResultsBlock(int fd); + static int us_sleep(unsigned int nusecs); + extern "C" void Avo_PollResultsAction_Sigusr1Handler(int foo); +#endif + +/* + * DONE. + * + * doname_check(target, do_get, implicit, automatic) + * + * Will call doname() and then inspect the return value + * + * Return value: + * Indication if the build failed or not + * + * Parameters: + * target The target to build + * do_get Passed thru to doname() + * implicit Passed thru to doname() + * automatic Are we building a hidden dependency? + * + * Global variables used: + * build_failed_seen Set if -k is on and error occurs + * continue_after_error Indicates that -k is on + * report_dependencies No error msg if -P is on + */ +Doname +doname_check(register Name target, register Boolean do_get, register Boolean implicit, register Boolean automatic) +{ + int first_time = 1; + (void) fflush(stdout); +try_again: + switch (doname(target, do_get, implicit, automatic)) { + case build_ok: + second_pass = 0; + return build_ok; + case build_running: + second_pass = 0; + return build_running; + case build_failed: + if (!continue_after_error) { + fatal(catgets(catd, 1, 13, "Target `%s' not remade because of errors"), + target->string_mb); + } + build_failed_seen = true; + second_pass = 0; + return build_failed; + case build_dont_know: + /* + * If we can't figure out how to build an automatic + * (hidden) dependency, we just ignore it. + * We later declare the target to be out of date just in + * case something changed. + * Also, don't complain if just reporting the dependencies + * and not building anything. + */ + if (automatic || (report_dependencies_level > 0)) { + second_pass = 0; + return build_dont_know; + } + if(first_time) { + first_time = 0; + second_pass = 1; + goto try_again; + } + second_pass = 0; + if (continue_after_error && !svr4) { + warning(catgets(catd, 1, 14, "Don't know how to make target `%s'"), + target->string_mb); + build_failed_seen = true; + return build_failed; + } + fatal(catgets(catd, 1, 15, "Don't know how to make target `%s'"), target->string_mb); + break; + } +#ifdef lint + return build_failed; +#endif +} + + +void +enter_explicit_rule_from_dynamic_rule(Name target, Name source) +{ + Property line, source_line; + Dependency dependency; + + source_line = get_prop(source->prop, line_prop); + line = maybe_append_prop(target, line_prop); + line->body.line.sccs_command = false; + line->body.line.target = target; + if (line->body.line.command_template == NULL) { + line->body.line.command_template = source_line->body.line.command_template; + for (dependency = source_line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + enter_dependency(line, dependency->name, false); + } + line->body.line.less = target; + } + line->body.line.percent = NULL; +} + + + +Name +find_dyntarget(Name target) +{ + Dyntarget p; + int i; + String_rec string; + wchar_t buffer[STRING_BUFFER_LENGTH]; + wchar_t *pp, * bufend; + wchar_t tbuffer[MAXPATHLEN]; + Wstring wcb(target); + + for (p = dyntarget_list; p != NULL; p = p->next) { + INIT_STRING_FROM_STACK(string, buffer); + expand_value(p->name, &string, false); + i = 0; + pp = string.buffer.start; + bufend = pp + STRING_BUFFER_LENGTH; + while((*pp != nul_char) && (pp < bufend)) { + if(iswspace(*pp)) { + tbuffer[i] = nul_char; + if(i > 0) { + if (wcb.equal(tbuffer)) { + enter_explicit_rule_from_dynamic_rule(target, p->name); + return(target); + } + } + pp++; + i = 0; + continue; + } + tbuffer[i] = *pp; + i++; + pp++; + if(*pp == nul_char) { + tbuffer[i] = nul_char; + if(i > 0) { + if (wcb.equal(tbuffer)) { + enter_explicit_rule_from_dynamic_rule(target, p->name); + return(target); + } + } + break; + } + } + } + return(NULL); +} + +/* + * DONE. + * + * doname(target, do_get, implicit) + * + * Chases all files the target depends on and builds any that + * are out of date. If the target is out of date it is then rebuilt. + * + * Return value: + * Indiates if build failed or nt + * + * Parameters: + * target Target to build + * do_get Run sccs get is nessecary + * implicit doname is trying to find an implicit rule + * + * Global variables used: + * assign_done True if command line assgnment has happened + * commands_done Preserved for the case that we need local value + * debug_level Should we trace make's actions? + * default_rule The rule for ".DEFAULT", used as last resort + * empty_name The Name "", used when looking for single sfx + * keep_state Indicates that .KEEP_STATE is on + * parallel True if building in parallel + * recursion_level Used for tracing + * report_dependencies make -P is on + */ +Doname +doname(register Name target, register Boolean do_get, register Boolean implicit, register Boolean automatic) +{ + Doname result = build_dont_know; + Chain out_of_date_list = NULL; +#ifdef TEAMWARE_MAKE_CMN + Chain target_group; +#endif + Property old_locals = NULL; + register Property line; + Property command = NULL; + register Dependency dependency; + Name less = NULL; + Name true_target = target; + Name *automatics = NULL; + register int auto_count; + Boolean rechecking_target = false; + Boolean saved_commands_done; + Boolean restart = false; + Boolean save_parallel = parallel; +#ifdef NSE + Boolean save_readdep; +#endif + Boolean doing_subtree = false; + + Boolean recheck_conditionals = false; + + if (target->state == build_running) { + return build_running; + } + line = get_prop(target->prop, line_prop); +#ifdef TEAMWARE_MAKE_CMN + if (line != NULL) { + /* + * If this target is a member of target group and one of the + * other members of the group is running, mark this target + * as running. + */ + for (target_group = line->body.line.target_group; + target_group != NULL; + target_group = target_group->next) { + if (is_running(target_group->name)) { + target->state = build_running; + add_pending(target, + recursion_level, + do_get, + implicit, + false); + return build_running; + } + } + } +#ifdef NSE + nse_check_file_backquotes(target->string); +#endif +#endif + /* + * If the target is a constructed one for a "::" target, + * we need to consider that. + */ + if (target->has_target_prop) { + true_target = get_prop(target->prop, + target_prop)->body.target.target; + if (true_target->colon_splits > 0) { + /* Make sure we have a valid time for :: targets */ + Property time; + + time = get_prop(true_target->prop, time_prop); + if (time != NULL) { + true_target->stat.time = time->body.time.time; + } + } + } + (void) exists(true_target); + /* + * If the target has been processed, we don't need to do it again, + * unless it depends on conditional macros or a delayed assignment, + * or it has been done when KEEP_STATE is on. + */ + if (target->state == build_ok) { + if((!keep_state || (!target->depends_on_conditional && !assign_done))) { + return build_ok; + } else { + recheck_conditionals = true; + } + } + if (target->state == build_subtree) { + /* A dynamic macro subtree is being built */ + target->state = build_dont_know; + doing_subtree = true; + if (!target->checking_subtree) { + /* + * This target has been started before and therefore + * not all dependencies have to be built. + */ + restart = true; + } + } else if (target->state == build_pending) { + target->state = build_dont_know; + restart = true; +/* +#ifdef TEAMWARE_MAKE_CMN + } else if (parallel && + keep_state && + (target->conditional_cnt > 0)) { + if (!parallel_ok(target, false)) { + add_subtree(target, recursion_level, do_get, implicit); + target->state = build_running; + return build_running; + } +#endif + */ + } + /* + * If KEEP_STATE is on, we have to rebuild the target if the + * building of it caused new automatic dependencies to be reported. + * This is where we restart the build. + */ + if (line != NULL) { + line->body.line.percent = NULL; + } +recheck_target: + /* Init all local variables */ + result = build_dont_know; + out_of_date_list = NULL; + command = NULL; + less = NULL; + auto_count = 0; + if (!restart && line != NULL) { + /* + * If this target has never been built before, mark all + * of the dependencies as never built. + */ + for (dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + dependency->built = false; + } + } + /* Save the set of automatic depes defined for this target */ + if (keep_state && + (line != NULL) && + (line->body.line.dependencies != NULL)) { + Name *p; + + /* + * First run thru the dependency list to see how many + * autos there are. + */ + for (dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + if (dependency->automatic && !dependency->stale) { + auto_count++; + } + } + /* Create vector to hold the current autos */ + automatics = + (Name *) alloca((int) (auto_count * sizeof (Name))); + /* Copy them */ + for (p = automatics, dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + if (dependency->automatic && !dependency->stale) { + *p++ = dependency->name; + } + } + } + if (debug_level > 1) { + (void) printf(NOCATGETS("%*sdoname(%s)\n"), + recursion_level, + "", + target->string_mb); + } + recursion_level++; + /* Avoid infinite loops */ + if (target->state == build_in_progress) { + warning(catgets(catd, 1, 16, "Infinite loop: Target `%s' depends on itself"), + target->string_mb); + return build_ok; + } + target->state = build_in_progress; + + /* Activate conditional macros for the target */ + if (!target->added_pattern_conditionals) { + add_pattern_conditionals(target); + target->added_pattern_conditionals = true; + } + if (target->conditional_cnt > 0) { + old_locals = (Property) alloca(target->conditional_cnt * + sizeof (Property_rec)); + set_locals(target, old_locals); + } + +/* + * after making the call to dynamic_dependecies unconditional we can handle + * target names that are same as file name. In this case $$@ in the + * dependencies did not mean anything. WIth this change it expands it + * as expected. + */ + if (!target->has_depe_list_expanded) + { +#ifdef NSE + save_readdep = reading_dependencies; + reading_dependencies= true; +#endif + dynamic_dependencies(target); +#ifdef NSE + reading_dependencies= save_readdep; +#endif + } + +/* + * FIRST SECTION -- GO THROUGH DEPENDENCIES AND COLLECT EXPLICIT + * COMMANDS TO RUN + */ + if ((line = get_prop(target->prop, line_prop)) != NULL) { + if (check_dependencies(&result, + line, + do_get, + target, + true_target, + doing_subtree, + &out_of_date_list, + old_locals, + implicit, + &command, + less, + rechecking_target, + recheck_conditionals)) { + return build_running; + } + if (line->body.line.query != NULL) { + delete_query_chain(line->body.line.query); + } + line->body.line.query = out_of_date_list; + } + +#ifdef PARALLEL + if (doing_subtree) { + parallel = false; + } +#endif + +/* + * If the target is a :: type, do not try to find the rule for the target, + * all actions will be taken by separate branches. + * Else, we try to find an implicit rule using various methods, + * we quit as soon as one is found. + * + * [tolik, 12 Sep 2002] Do not try to find implicit rule for the target + * being rechecked - the target is being rechecked means that it already + * has explicit dependencies derived from an implicit rule found + * in previous step. + */ + if (target->colon_splits == 0 && !rechecking_target) { + /* Look for percent matched rule */ + if ((result == build_dont_know) && + (command == NULL)) { + switch (find_percent_rule( + target, + &command, + recheck_conditionals)) { + case build_failed: + result = build_failed; + break; +#ifdef TEAMWARE_MAKE_CMN + case build_running: + target->state = build_running; + add_pending(target, + --recursion_level, + do_get, + implicit, + false); + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target->prop, + conditional_prop), + 0); + } + return build_running; +#endif + case build_ok: + result = build_ok; + break; + } + } + /* Look for double suffix rule */ + if (result == build_dont_know) { + Property member; + + if (target->is_member && + ((member = get_prop(target->prop, member_prop)) != + NULL)) { + switch (find_ar_suffix_rule(target, + member->body. + member.member, + &command, + recheck_conditionals)) { + case build_failed: + result = build_failed; + break; +#ifdef TEAMWARE_MAKE_CMN + case build_running: + target->state = build_running; + add_pending(target, + --recursion_level, + do_get, + implicit, + false); + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target->prop, + conditional_prop), + 0); + } + return build_running; +#endif + default: + /* ALWAYS bind $% for old style */ + /* ar rules */ + if (line == NULL) { + line = + maybe_append_prop(target, + line_prop); + } + line->body.line.percent = + member->body.member.member; + break; + } + } else { + switch (find_double_suffix_rule(target, + &command, + recheck_conditionals)) { + case build_failed: + result = build_failed; + break; +#ifdef TEAMWARE_MAKE_CMN + case build_running: + target->state = build_running; + add_pending(target, + --recursion_level, + do_get, + implicit, + false); + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target-> + prop, + conditional_prop), + 0); + } + return build_running; +#endif + } + } + } + /* Look for single suffix rule */ + +/* /tolik/ + * I commented !implicit to fix bug 1247448: Suffix Rules failed when combine with Pattern Matching Rules. + * This caused problem with SVR4 tilde rules (infinite recursion). So I made some changes in "implicit.cc" + */ +/* /tolik, 06.21.96/ + * Regression! See BugId 1255360 + * If more than one percent rules are defined for the same target then + * the behaviour of 'make' with my previous fix may be different from one + * of the 'old make'. + * The global variable second_pass (maybe it should be an argument to doname()) + * is intended to avoid this regression. It is set in doname_check(). + * First, 'make' will work as it worked before. Only when it is + * going to say "don't know how to make target" it sets second_pass to true and + * run 'doname' again but now trying to use Single Suffix Rules. + */ + if ((result == build_dont_know) && !automatic && (!implicit || second_pass) && + ((line == NULL) || + ((line->body.line.target != NULL) && + !line->body.line.target->has_regular_dependency))) { + switch (find_suffix_rule(target, + target, + empty_name, + &command, + recheck_conditionals)) { + case build_failed: + result = build_failed; + break; +#ifdef TEAMWARE_MAKE_CMN + case build_running: + target->state = build_running; + add_pending(target, + --recursion_level, + do_get, + implicit, + false); + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target->prop, + conditional_prop), + 0); + } + return build_running; +#endif + } + } + /* Try to sccs get */ + if ((command == NULL) && + (result == build_dont_know) && + do_get) { + result = sccs_get(target, &command); + } + + /* Use .DEFAULT rule if it is defined. */ + if ((command == NULL) && + (result == build_dont_know) && + (true_target->colons == no_colon) && + default_rule && + !implicit) { + /* Make sure we have a line prop */ + line = maybe_append_prop(target, line_prop); + command = line; + Boolean out_of_date; + if (true_target->is_member) { + out_of_date = (Boolean) OUT_OF_DATE_SEC(true_target->stat.time, + line->body.line.dependency_time); + } else { + out_of_date = (Boolean) OUT_OF_DATE(true_target->stat.time, + line->body.line.dependency_time); + } + if (build_unconditional || out_of_date) { + line->body.line.is_out_of_date = true; + if (debug_level > 0) { + (void) printf(catgets(catd, 1, 17, "%*sBuilding %s using .DEFAULT because it is out of date\n"), + recursion_level, + "", + true_target->string_mb); + } + } + line->body.line.sccs_command = false; + line->body.line.command_template = default_rule; + line->body.line.target = true_target; + line->body.line.star = NULL; + line->body.line.less = true_target; + line->body.line.percent = NULL; + } + } + + /* We say "target up to date" if no cmd were executed for the target */ + if (!target->is_double_colon_parent) { + commands_done = false; + } + + silent = silent_all; + ignore_errors = ignore_errors_all; + if (posix) + { + if (!silent) + { + silent = (Boolean) target->silent_mode; + } + if (!ignore_errors) + { + ignore_errors = (Boolean) target->ignore_error_mode; + } + } + + int doname_dyntarget = 0; +r_command: + /* Run commands if any. */ + if ((command != NULL) && + (command->body.line.command_template != NULL)) { + if (result != build_failed) { + result = run_command(command, + (Boolean) ((parallel || save_parallel) && !silent)); +#ifdef NSE + nse_check_no_deps_no_rule(target, + get_prop(target->prop, line_prop), command); +#endif + } + switch (result) { +#ifdef TEAMWARE_MAKE_CMN + case build_running: + add_running(target, + true_target, + command, + --recursion_level, + auto_count, + automatics, + do_get, + implicit); + target->state = build_running; + if ((line = get_prop(target->prop, + line_prop)) != NULL) { + if (line->body.line.query != NULL) { + delete_query_chain(line->body.line.query); + } + line->body.line.query = NULL; + } + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target->prop, + conditional_prop), + 0); + } + return build_running; + case build_serial: + add_serial(target, + --recursion_level, + do_get, + implicit); + target->state = build_running; + line = get_prop(target->prop, line_prop); + if (line != NULL) { + if (line->body.line.query != NULL) { + delete_query_chain(line->body.line.query); + } + line->body.line.query = NULL; + } + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target->prop, + conditional_prop), + 0); + } + return build_running; +#endif + case build_ok: + /* If all went OK set a nice timestamp */ + if (true_target->stat.time == file_doesnt_exist) { + true_target->stat.time = file_max_time; + } + break; + } + } else { + /* + * If no command was found for the target, and it doesn't + * exist, and it is mentioned as a target in the makefile, + * we say it is extremely new and that it is OK. + */ + if (target->colons != no_colon) { + if (true_target->stat.time == file_doesnt_exist){ + true_target->stat.time = file_max_time; + } + result = build_ok; + } + /* + * Trying dynamic targets. + */ + if(!doname_dyntarget) { + doname_dyntarget = 1; + Name dtarg = find_dyntarget(target); + if(dtarg!=NULL) { + if (!target->has_depe_list_expanded) { + dynamic_dependencies(target); + } + if ((line = get_prop(target->prop, line_prop)) != NULL) { + if (check_dependencies(&result, + line, + do_get, + target, + true_target, + doing_subtree, + &out_of_date_list, + old_locals, + implicit, + &command, + less, + rechecking_target, + recheck_conditionals)) + { + return build_running; + } + if (line->body.line.query != NULL) { + delete_query_chain(line->body.line.query); + } + line->body.line.query = out_of_date_list; + } + goto r_command; + } + } + /* + * If the file exists, it is OK that we couldnt figure + * out how to build it. + */ + (void) exists(target); + if ((target->stat.time != file_doesnt_exist) && + (result == build_dont_know)) { + result = build_ok; + } + } + + /* + * Some of the following is duplicated in the function finish_doname. + * If anything is changed here, check to see if it needs to be + * changed there. + */ + if ((line = get_prop(target->prop, line_prop)) != NULL) { + if (line->body.line.query != NULL) { + delete_query_chain(line->body.line.query); + } + line->body.line.query = NULL; + } + target->state = result; + parallel = save_parallel; + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target->prop, conditional_prop), + 0); + } + recursion_level--; + 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 = + 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, 18, "%*sTarget `%s' acquired new dependencies from build, rechecking all dependencies\n"), + recursion_level, + "", + true_target->string_mb); + } + rechecking_target = true; + saved_commands_done = commands_done; + goto recheck_target; + } + + if (rechecking_target && !commands_done) { + commands_done = saved_commands_done; + } + + return result; +} + +/* + * DONE. + * + * check_dependencies(result, line, do_get, + * target, true_target, doing_subtree, out_of_date_tail, + * old_locals, implicit, command, less, rechecking_target) + * + * Return value: + * True returned if some dependencies left running + * + * Parameters: + * result Pointer to cell we update if build failed + * line We get the dependencies from here + * do_get Allow use of sccs get in recursive doname() + * target The target to chase dependencies for + * true_target The real one for :: and lib(member) + * doing_subtree True if building a conditional macro subtree + * out_of_date_tail Used to set the $? list + * old_locals Used for resetting the local macros + * implicit Called when scanning for implicit rules? + * command Place to stuff command + * less Set to $< value + * + * Global variables used: + * command_changed Set if we suspect .make.state needs rewrite + * debug_level Should we trace actions? + * force The Name " FORCE", compared against + * recursion_level Used for tracing + * rewrite_statefile Set if .make.state needs rewriting + * wait_name The Name ".WAIT", compared against + */ +static Boolean +#ifdef TEAMWARE_MAKE_CMN +check_dependencies(Doname *result, Property line, Boolean do_get, Name target, Name true_target, Boolean doing_subtree, Chain *out_of_date_tail, Property old_locals, Boolean implicit, Property *command, Name less, Boolean rechecking_target, Boolean recheck_conditionals) +#else +check_dependencies(Doname *result, Property line, Boolean do_get, Name target, Name true_target, Boolean, Chain *out_of_date_tail, Property, Boolean, Property *command, Name less, Boolean rechecking_target, Boolean recheck_conditionals) +#endif +{ + Boolean dependencies_running; + register Dependency dependency; + Doname dep_result; + Boolean dependency_changed = false; + + line->body.line.dependency_time = file_doesnt_exist; + if (line->body.line.query != NULL) { + delete_query_chain(line->body.line.query); + } + line->body.line.query = NULL; + line->body.line.is_out_of_date = false; + dependencies_running = false; + /* + * Run thru all the dependencies and call doname() recursively + * on each of them. + */ + for (dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + Boolean this_dependency_changed = false; + + if (!dependency->automatic && + (rechecking_target || target->rechecking_target)) { + /* + * We only bother with the autos when rechecking + */ + continue; + } + + if (dependency->name == wait_name) { + /* + * The special target .WAIT means finish all of + * the prior dependencies before continuing. + */ + if (dependencies_running) { + break; + } +#ifdef DISTRIBUTED + } else if ((!parallel_ok(dependency->name, false)) && + (dependencies_running)) { + /* + * If we can't execute the current dependency in + * parallel, hold off the dependency processing + * to preserve the order of the dependencies. + */ + break; +#endif + } else { + timestruc_t depe_time = file_doesnt_exist; + + + if (true_target->is_member) { + depe_time = exists(dependency->name); + } + if (dependency->built || + (dependency->name->state == build_failed)) { + dep_result = (Doname) dependency->name->state; + } else { +#ifdef NSE + nse_check_sccs(target->string, + dependency->name->string); + nse_check_derived_src(target, + dependency->name->string, + line->body.line.command_template); +#endif + dep_result = doname_check(dependency->name, + do_get, + false, + (Boolean) dependency->automatic); + } + if (true_target->is_member || dependency->name->is_member) { + /* should compare only secs, cause lib members does not have nsec time resolution */ + if (depe_time.tv_sec != dependency->name->stat.time.tv_sec) { + this_dependency_changed = + dependency_changed = + true; + } + } else { + if (depe_time != dependency->name->stat.time) { + this_dependency_changed = + dependency_changed = + true; + } + } + dependency->built = true; + switch (dep_result) { + case build_running: + dependencies_running = true; + continue; + case build_failed: + *result = build_failed; + break; + case build_dont_know: +/* + * If make can't figure out how to make a dependency, maybe the dependency + * is out of date. In this case, we just declare the target out of date + * and go on. If we really need the dependency, the make'ing of the target + * will fail. This will only happen for automatic (hidden) dependencies. + */ + if(!recheck_conditionals) { + line->body.line.is_out_of_date = true; + } + /* + * Make sure the dependency is not saved + * in the state file. + */ + dependency->stale = true; + rewrite_statefile = + command_changed = + true; + if (debug_level > 0) { + (void) printf(catgets(catd, 1, 19, "Target %s rebuilt because dependency %s does not exist\n"), + true_target->string_mb, + dependency->name->string_mb); + } + break; + } + if (dependency->name->depends_on_conditional) { + target->depends_on_conditional = true; + } + if (dependency->name == force) { + target->stat.time = + dependency->name->stat.time; + } + /* + * Propagate new timestamp from "member" to + * "lib.a(member)". + */ + (void) exists(dependency->name); + + /* Collect the timestamp of the youngest dependency */ + line->body.line.dependency_time = + MAX(dependency->name->stat.time, + line->body.line.dependency_time); + + /* Correction: do not consider nanosecs for members */ + if(true_target->is_member || dependency->name->is_member) { + line->body.line.dependency_time.tv_nsec = 0; + } + + if (debug_level > 1) { + (void) printf(catgets(catd, 1, 20, "%*sDate(%s)=%s \n"), + recursion_level, + "", + dependency->name->string_mb, + time_to_string(dependency->name-> + stat.time)); + if (dependency->name->stat.time > line->body.line.dependency_time) { + (void) printf(catgets(catd, 1, 21, "%*sDate-dependencies(%s) set to %s\n"), + recursion_level, + "", + true_target->string_mb, + time_to_string(line->body.line. + dependency_time)); + } + } + + /* Build the $? list */ + if (true_target->is_member) { + if (this_dependency_changed == true) { + true_target->stat.time = dependency->name->stat.time; + true_target->stat.time.tv_sec--; + } else { + /* Dina: + * The next statement is commented + * out as a fix for bug #1051032. + * if dependency hasn't changed + * then there's no need to invalidate + * true_target. This statemnt causes + * make to take much longer to process + * an already-built archive. Soren + * said it was a quick fix for some + * problem he doesn't remember. + true_target->stat.time = file_no_time; + */ + (void) exists(true_target); + } + } else { + (void) exists(true_target); + } + Boolean out_of_date; + if (true_target->is_member || dependency->name->is_member) { + out_of_date = (Boolean) OUT_OF_DATE_SEC(true_target->stat.time, + dependency->name->stat.time); + } else { + out_of_date = (Boolean) OUT_OF_DATE(true_target->stat.time, + dependency->name->stat.time); + } + if ((build_unconditional || out_of_date) && + (dependency->name != force) && + (dependency->stale == false)) { + *out_of_date_tail = ALLOC(Chain); + if (dependency->name->is_member && + (get_prop(dependency->name->prop, + member_prop) != NULL)) { + (*out_of_date_tail)->name = + get_prop(dependency->name->prop, + member_prop)-> + body.member.member; + } else { + (*out_of_date_tail)->name = + dependency->name; + } + (*out_of_date_tail)->next = NULL; + out_of_date_tail = &(*out_of_date_tail)->next; + if (debug_level > 0) { + if (dependency->name->stat.time == file_max_time) { + (void) printf(catgets(catd, 1, 22, "%*sBuilding %s because %s does not exist\n"), + recursion_level, + "", + true_target->string_mb, + dependency->name->string_mb); + } else { + (void) printf(catgets(catd, 1, 23, "%*sBuilding %s because it is out of date relative to %s\n"), + recursion_level, + "", + true_target->string_mb, + dependency->name->string_mb); + } + } + } + if (dependency->name == force) { + force->stat.time = + file_max_time; + force->state = build_dont_know; + } + } + } +#ifdef TEAMWARE_MAKE_CMN + if (dependencies_running) { + if (doing_subtree) { + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target->prop, + conditional_prop), + 0); + } + return true; + } else { + target->state = build_running; + add_pending(target, + --recursion_level, + do_get, + implicit, + false); + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target->prop, + conditional_prop), + 0); + } + return true; + } + } +#endif + /* + * Collect the timestamp of the youngest double colon target + * dependency. + */ + if (target->is_double_colon_parent) { + for (dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + Property tmp_line; + + if ((tmp_line = get_prop(dependency->name->prop, line_prop)) != NULL) { + if(tmp_line->body.line.dependency_time != file_max_time) { + target->stat.time = + MAX(tmp_line->body.line.dependency_time, + target->stat.time); + } + } + } + } + if ((true_target->is_member) && (dependency_changed == true)) { + true_target->stat.time = file_no_time; + } + /* + * After scanning all the dependencies, we check the rule + * if we found one. + */ + if (line->body.line.command_template != NULL) { + if (line->body.line.command_template_redefined) { + warning(catgets(catd, 1, 24, "Too many rules defined for target %s"), + target->string_mb); + } + *command = line; + /* Check if the target is out of date */ + Boolean out_of_date; + if (true_target->is_member) { + out_of_date = (Boolean) OUT_OF_DATE_SEC(true_target->stat.time, + line->body.line.dependency_time); + } else { + out_of_date = (Boolean) OUT_OF_DATE(true_target->stat.time, + line->body.line.dependency_time); + } + if (build_unconditional || out_of_date){ + if(!recheck_conditionals) { + line->body.line.is_out_of_date = true; + } + } + line->body.line.sccs_command = false; + line->body.line.target = true_target; + if(gnu_style) { + + // set $< for explicit rule + if(line->body.line.dependencies != NULL) { + less = line->body.line.dependencies->name; + } + + // set $* for explicit rule + Name target_body; + Name tt = true_target; + Property member; + register wchar_t *target_end; + register Dependency suffix; + register int suffix_length; + Wstring targ_string; + Wstring suf_string; + + if (true_target->is_member && + ((member = get_prop(target->prop, member_prop)) != + NULL)) { + tt = member->body.member.member; + } + targ_string.init(tt); + target_end = targ_string.get_string() + tt->hash.length; + for (suffix = suffixes; suffix != NULL; suffix = suffix->next) { + suffix_length = suffix->name->hash.length; + suf_string.init(suffix->name); + if (tt->hash.length < suffix_length) { + continue; + } else if (!IS_WEQUALN(suf_string.get_string(), + (target_end - suffix_length), + suffix_length)) { + continue; + } + target_body = GETNAME( + targ_string.get_string(), + (int)(tt->hash.length - suffix_length) + ); + line->body.line.star = target_body; + } + + // set result = build_ok so that implicit rules are not used. + if(*result == build_dont_know) { + *result = build_ok; + } + } + if (less != NULL) { + line->body.line.less = less; + } + } + + return false; +} + +/* + * dynamic_dependencies(target) + * + * Checks if any dependency contains a macro ref + * If so, it replaces the dependency with the expanded version. + * Here, "$@" gets translated to target->string. That is + * the current name on the left of the colon in the + * makefile. Thus, + * xyz: s.$@.c + * translates into + * xyz: s.xyz.c + * + * Also, "$(@F)" translates to the same thing without a preceeding + * directory path (if one exists). + * Note, to enter "$@" on a dependency line in a makefile + * "$$@" must be typed. This is because make expands + * macros in dependency lists upon reading them. + * dynamic_dependencies() also expands file wildcards. + * If there are any Shell meta characters in the name, + * search the directory, and replace the dependency + * with the set of files the pattern matches + * + * Parameters: + * target Target to sanitize dependencies for + * + * Global variables used: + * c_at The Name "@", used to set macro value + * debug_level Should we trace actions? + * dot The Name ".", used to read directory + * recursion_level Used for tracing + */ +void +dynamic_dependencies(Name target) +{ + wchar_t pattern[MAXPATHLEN]; + register wchar_t *p; + Property line; + register Dependency dependency; + register Dependency *remove; + String_rec string; + wchar_t buffer[MAXPATHLEN]; + register Boolean set_at = false; + register wchar_t *start; + Dependency new_depe; + register Boolean reuse_cell; + Dependency first_member; + Name directory; + Name lib; + Name member; + Property prop; + Name true_target = target; + wchar_t *library; + + if ((line = get_prop(target->prop, line_prop)) == NULL) { + return; + } + /* If the target is constructed from a "::" target we consider that */ + if (target->has_target_prop) { + true_target = get_prop(target->prop, + target_prop)->body.target.target; + } + /* Scan all dependencies and process the ones that contain "$" chars */ + for (dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + if (!dependency->name->dollar) { + continue; + } + target->has_depe_list_expanded = true; + + /* The make macro $@ is bound to the target name once per */ + /* invocation of dynamic_dependencies() */ + if (!set_at) { + (void) SETVAR(c_at, true_target, false); + set_at = true; + } + /* Expand this dependency string */ + INIT_STRING_FROM_STACK(string, buffer); + expand_value(dependency->name, &string, false); + /* Scan the expanded string. It could contain whitespace */ + /* which mean it expands to several dependencies */ + start = string.buffer.start; + while (iswspace(*start)) { + start++; + } + /* Remove the cell (later) if the macro was empty */ + if (start[0] == (int) nul_char) { + dependency->name = NULL; + } + +/* azv 10/26/95 to fix bug BID_1170218 */ + if ((start[0] == (int) period_char) && + (start[1] == (int) slash_char)) { + start += 2; + } +/* azv */ + + first_member = NULL; + /* We use the original dependency cell for the first */ + /* dependency from the expansion */ + reuse_cell = true; + /* We also have to deal with dependencies that expand to */ + /* lib.a(members) notation */ + for (p = start; *p != (int) nul_char; p++) { + if ((*p == (int) parenleft_char)) { + lib = GETNAME(start, p - start); + lib->is_member = true; + first_member = dependency; + start = p + 1; + while (iswspace(*start)) { + start++; + } + break; + } + } + do { + /* First skip whitespace */ + for (p = start; *p != (int) nul_char; p++) { + if ((*p == (int) nul_char) || + iswspace(*p) || + (*p == (int) parenright_char)) { + break; + } + } + /* Enter dependency from expansion */ + if (p != start) { + /* Create new dependency cell if */ + /* this is not the first dependency */ + /* picked from the expansion */ + if (!reuse_cell) { + new_depe = ALLOC(Dependency); + new_depe->next = dependency->next; + new_depe->automatic = false; + new_depe->stale = false; + new_depe->built = false; + dependency->next = new_depe; + dependency = new_depe; + } + reuse_cell = false; + /* Internalize the dependency name */ + // tolik. Fix for bug 4110429: inconsistent expansion for macros that + // include "//" and "/./" + //dependency->name = GETNAME(start, p - start); + dependency->name = normalize_name(start, p - start); + if ((debug_level > 0) && + (first_member == NULL)) { + (void) printf(catgets(catd, 1, 25, "%*sDynamic dependency `%s' for target `%s'\n"), + recursion_level, + "", + dependency->name->string_mb, + true_target->string_mb); + } + for (start = p; iswspace(*start); start++); + p = start; + } + } while ((*p != (int) nul_char) && + (*p != (int) parenright_char)); + /* If the expansion was of lib.a(members) format we now */ + /* enter the proper member cells */ + if (first_member != NULL) { + /* Scan the new dependencies and transform them from */ + /* "foo" to "lib.a(foo)" */ + for (; 1; first_member = first_member->next) { + /* Build "lib.a(foo)" name */ + INIT_STRING_FROM_STACK(string, buffer); + APPEND_NAME(lib, + &string, + (int) lib->hash.length); + append_char((int) parenleft_char, &string); + APPEND_NAME(first_member->name, + &string, + FIND_LENGTH); + append_char((int) parenright_char, &string); + member = first_member->name; + /* Replace "foo" with "lib.a(foo)" */ + first_member->name = + GETNAME(string.buffer.start, FIND_LENGTH); + if (string.free_after_use) { + retmem(string.buffer.start); + } + if (debug_level > 0) { + (void) printf(catgets(catd, 1, 26, "%*sDynamic dependency `%s' for target `%s'\n"), + recursion_level, + "", + first_member->name-> + string_mb, + true_target->string_mb); + } + first_member->name->is_member = lib->is_member; + /* Add member property to member */ + prop = maybe_append_prop(first_member->name, + member_prop); + prop->body.member.library = lib; + prop->body.member.entry = NULL; + prop->body.member.member = member; + if (first_member == dependency) { + break; + } + } + } + } + Wstring wcb; + /* Then scan all the dependencies again. This time we want to expand */ + /* shell file wildcards */ + for (remove = &line->body.line.dependencies, dependency = *remove; + dependency != NULL; + dependency = *remove) { + if (dependency->name == NULL) { + dependency = *remove = (*remove)->next; + continue; + } + /* If dependency name string contains shell wildcards */ + /* replace the name with the expansion */ + if (dependency->name->wildcard) { +#ifdef NSE + nse_wildcard(target->string, dependency->name->string); +#endif + wcb.init(dependency->name); + if ((start = (wchar_t *) wschr(wcb.get_string(), + (int) parenleft_char)) != NULL) { + /* lib(*) type pattern */ + library = buffer; + (void) wsncpy(buffer, + wcb.get_string(), + start - wcb.get_string()); + buffer[start-wcb.get_string()] = + (int) nul_char; + (void) wsncpy(pattern, + start + 1, +(int) (dependency->name->hash.length-(start-wcb.get_string())-2)); + pattern[dependency->name->hash.length - + (start-wcb.get_string()) - 2] = + (int) nul_char; + } else { + library = NULL; + (void) wsncpy(pattern, + wcb.get_string(), + (int) dependency->name->hash.length); + pattern[dependency->name->hash.length] = + (int) nul_char; + } + start = (wchar_t *) wsrchr(pattern, (int) slash_char); + if (start == NULL) { + directory = dot; + p = pattern; + } else { + directory = GETNAME(pattern, start-pattern); + p = start+1; + } + /* The expansion is handled by the read_dir() routine*/ + if (read_dir(directory, p, line, library)) { + *remove = (*remove)->next; + } else { + remove = &dependency->next; + } + } else { + remove = &dependency->next; + } + } + + /* Then unbind $@ */ + (void) SETVAR(c_at, (Name) NULL, false); +} + +/* + * DONE. + * + * run_command(line) + * + * Takes one Cmd_line and runs the commands from it. + * + * Return value: + * Indicates if the command failed or not + * + * Parameters: + * line The command line to run + * + * Global variables used: + * commands_done Set if we do run command + * current_line Set to the line we run a command from + * current_target Set to the target we run a command for + * file_number Used to form temp file name + * keep_state Indicates that .KEEP_STATE is on + * make_state The Name ".make.state", used to check timestamp + * parallel True if currently building in parallel + * parallel_process_cnt Count of parallel processes running + * quest Indicates that make -q is on + * rewrite_statefile Set if we do run a command + * sunpro_dependencies The Name "SUNPRO_DEPENDENCIES", set value + * temp_file_directory Used to form temp fie name + * temp_file_name Set to the name of the temp file + * touch Indicates that make -t is on + */ +static Doname +run_command(register Property line, Boolean) +{ + register Doname result = build_ok; + register Boolean remember_only = false; + register Name target = line->body.line.target; + wchar_t *string; + char tmp_file_path[MAXPATHLEN]; + + if (!line->body.line.is_out_of_date && target->rechecking_target) { + target->rechecking_target = false; + return build_ok; + } + + /* + * Build the command if we know the target is out of date, + * or if we want to check cmd consistency. + */ + if (line->body.line.is_out_of_date || keep_state) { + /* Hack for handling conditional macros in DMake. */ + if (!line->body.line.dont_rebuild_command_used) { + build_command_strings(target, line); + } + } + /* Never mind */ + if (!line->body.line.is_out_of_date) { + return build_ok; + } + /* If quest, then exit(1) because the target is out of date */ + if (quest) { + if (posix) { +#ifdef TEAMWARE_MAKE_CMN + result = execute_parallel(line, true); +#else + result = execute_serial(line); +#endif + } +#if defined(SUN5_0) || defined(HP_UX) || defined(linux) + exit_status = 1; +#endif + exit(1); + } + /* We actually had to do something this time */ + rewrite_statefile = commands_done = true; + /* + * If this is an sccs command, we have to do some extra checking + * and possibly complain. If the file can't be gotten because it's + * checked out, we complain and behave as if the command was + * executed eventhough we ignored the command. + */ + if (!touch && + line->body.line.sccs_command && + (target->stat.time != file_doesnt_exist) && + ((target->stat.mode & 0222) != 0)) { + fatal(catgets(catd, 1, 27, "%s is writable so it cannot be sccs gotten"), + target->string_mb); + target->has_complained = remember_only = true; + } + /* + * If KEEP_STATE is on, we make sure we have the timestamp for + * .make.state. If .make.state changes during the command run, + * we reread .make.state after the command. We also setup the + * environment variable that asks utilities to report dependencies. + */ + if (!touch && + keep_state && + !remember_only) { + (void) exists(make_state); + if((strlen(temp_file_directory) == 1) && + (temp_file_directory[0] == '/')) { + tmp_file_path[0] = '\0'; + } else { + strcpy(tmp_file_path, temp_file_directory); + } + sprintf(mbs_buffer, + NOCATGETS("%s/.make.dependency.%08x.%d.%d"), + tmp_file_path, + hostid, + getpid(), + file_number++); + MBSTOWCS(wcs_buffer, mbs_buffer); + Boolean fnd; + temp_file_name = getname_fn(wcs_buffer, FIND_LENGTH, false, &fnd); + temp_file_name->stat.is_file = true; + int len = 2*MAXPATHLEN + strlen(target->string_mb) + 2; + wchar_t *to = string = ALLOC_WC(len); + for (wchar_t *from = wcs_buffer; *from != (int) nul_char; ) { + if (*from == (int) space_char) { + *to++ = (int) backslash_char; + } + *to++ = *from++; + } + *to++ = (int) space_char; + MBSTOWCS(to, target->string_mb); + Name sprodep_name = getname_fn(string, FIND_LENGTH, false, &fnd); + (void) SETVAR(sunpro_dependencies, + sprodep_name, + false); + retmem(string); + } else { + temp_file_name = NULL; + } + + /* + * In case we are interrupted, we need to know what was going on. + */ + current_target = target; + /* + * We also need to be able to save an empty command instead of the + * interrupted one in .make.state. + */ + current_line = line; + if (remember_only) { + /* Empty block!!! */ + } else if (touch) { + result = touch_command(line, target, result); + if (posix) { +#ifdef TEAMWARE_MAKE_CMN + result = execute_parallel(line, true); +#else + result = execute_serial(line); +#endif + } + } else { + /* + * If this is not a touch run, we need to execute the + * proper command(s) for the target. + */ +#ifdef TEAMWARE_MAKE_CMN + if (parallel) { + if (!parallel_ok(target, true)) { + /* + * We are building in parallel, but + * this target must be built in serial. + */ + /* + * If nothing else is building, + * do this one, else wait. + */ + if (parallel_process_cnt == 0) { +#ifdef TEAMWARE_MAKE_CMN + result = execute_parallel(line, true, target->localhost); +#else + result = execute_serial(line); +#endif + } else { + current_target = NULL; + current_line = NULL; +/* + line->body.line.command_used = NULL; + */ + line->body.line.dont_rebuild_command_used = true; + return build_serial; + } + } else { + result = execute_parallel(line, false); + switch (result) { + case build_running: + return build_running; + case build_serial: + if (parallel_process_cnt == 0) { +#ifdef TEAMWARE_MAKE_CMN + result = execute_parallel(line, true, target->localhost); +#else + result = execute_serial(line); +#endif + } else { + current_target = NULL; + current_line = NULL; + target->parallel = false; + line->body.line.command_used = + NULL; + return build_serial; + } + } + } + } else { +#endif +#ifdef TEAMWARE_MAKE_CMN + result = execute_parallel(line, true, target->localhost); +#else + result = execute_serial(line); +#endif +#ifdef TEAMWARE_MAKE_CMN + } +#endif + } + temp_file_name = NULL; + if (report_dependencies_level == 0){ + update_target(line, result); + } + current_target = NULL; + current_line = NULL; + return result; +} + +/* + * execute_serial(line) + * + * Runs thru the command line for the target and + * executes the rules one by one. + * + * Return value: + * The result of the command build + * + * Parameters: + * line The command to execute + * + * Static variables used: + * + * Global variables used: + * continue_after_error -k flag + * do_not_exec_rule -n flag + * report_dependencies -P flag + * silent Don't echo commands before executing + * temp_file_name Temp file for auto dependencies + * vpath_defined If true, translate path for command + */ +Doname +execute_serial(Property line) +{ + int child_pid = 0; +#if defined(DISTRIBUTED) || defined(MAKETOOL) /* tolik */ + Avo_MToolJobResultMsg *job_result_msg; + RWCollectable *xdr_msg; +#endif + Boolean printed_serial; + Doname result = build_ok; + Cmd_line rule, cmd_tail, command = NULL; + char mbstring[MAXPATHLEN]; + int filed; + Name target = line->body.line.target; + + SEND_MTOOL_MSG( + if (!sent_rsrc_info_msg) { + if (userName[0] == '\0') { + avo_get_user(userName, NULL); + } + if (hostName[0] == '\0') { + strcpy(hostName, avo_hostname()); + } + send_rsrc_info_msg(1, hostName, userName); + sent_rsrc_info_msg = 1; + } + send_job_start_msg(line); + job_result_msg = new Avo_MToolJobResultMsg(); + ); + + target->has_recursive_dependency = false; + // We have to create a copy of the rules chain for processing because + // the original one can be destroyed during .make.state file rereading. + for (rule = line->body.line.command_used; + rule != NULL; + rule = rule->next) { + if (command == NULL) { + command = cmd_tail = ALLOC(Cmd_line); + } else { + cmd_tail->next = ALLOC(Cmd_line); + cmd_tail = cmd_tail->next; + } + *cmd_tail = *rule; + } + if (command) { + cmd_tail->next = NULL; + } + for (rule = command; rule != NULL; rule = rule->next) { + if (posix && (touch || quest) && !rule->always_exec) { + continue; + } + if (vpath_defined) { + rule->command_line = + vpath_translation(rule->command_line); + } + /* Echo command line, maybe. */ + if ((rule->command_line->hash.length > 0) && + !silent && + (!rule->silent || do_not_exec_rule) && + (report_dependencies_level == 0)) { + (void) printf("%s\n", rule->command_line->string_mb); + SEND_MTOOL_MSG( + job_result_msg->appendOutput(AVO_STRDUP(rule->command_line->string_mb)); + ); + } + if (rule->command_line->hash.length > 0) { + SEND_MTOOL_MSG( + (void) sprintf(mbstring, + NOCATGETS("%s/make.stdout.%d.%d.XXXXXX"), + tmpdir, + getpid(), + file_number++); + + int tmp_fd = mkstemp(mbstring); + if(tmp_fd) { + (void) close(tmp_fd); + } + + stdout_file = strdup(mbstring); + stderr_file = NULL; + child_pid = pollResults(stdout_file, + (char *)NULL, + (char *)NULL); + ); + /* Do assignment if command line prefixed with "=" */ + if (rule->assign) { + result = build_ok; + do_assign(rule->command_line, target); + } else if (report_dependencies_level == 0) { + /* Execute command line. */ +#ifdef DISTRIBUTED + setvar_envvar((Avo_DoJobMsg *)NULL); +#else + setvar_envvar(); +#endif + result = dosys(rule->command_line, + (Boolean) rule->ignore_error, + (Boolean) rule->make_refd, + /* ds 98.04.23 bug #4085164. make should always show error messages */ + false, + /* BOOLEAN(rule->silent && + rule->ignore_error), */ + (Boolean) rule->always_exec, + target, + send_mtool_msgs); +#ifdef NSE + nse_did_recursion= false; +#endif + check_state(temp_file_name); +#ifdef NSE + nse_check_cd(line); +#endif + } + SEND_MTOOL_MSG( + append_job_result_msg(job_result_msg); + if (child_pid > 0) { + kill(child_pid, SIGUSR1); + while (!((waitpid(child_pid, 0, 0) == -1) + && (errno == ECHILD))); + } + child_pid = 0; + (void) unlink(stdout_file); + retmem_mb(stdout_file); + stdout_file = NULL; + ); + } else { + result = build_ok; + } + if (result == build_failed) { + if (silent || rule->silent) { + (void) printf(catgets(catd, 1, 242, "The following command caused the error:\n%s\n"), + rule->command_line->string_mb); + SEND_MTOOL_MSG( + job_result_msg->appendOutput(AVO_STRDUP(catgets(catd, 1, 243, "The following command caused the error:"))); + job_result_msg->appendOutput(AVO_STRDUP(rule->command_line->string_mb)); + ); + } + if (!rule->ignore_error && !ignore_errors) { + if (!continue_after_error) { + SEND_MTOOL_MSG( + job_result_msg->setResult(job_msg_id, (result == build_ok) ? 0 : 1, DONE); + xdr_msg = (RWCollectable*) + job_result_msg; + xdr(&xdrs, xdr_msg); + (void) fflush(mtool_msgs_fp); + delete job_result_msg; + ); + fatal(catgets(catd, 1, 244, "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; + break; + } else { + result = build_ok; + } + } + } + for (rule = command; rule != NULL; rule = cmd_tail) { + cmd_tail = rule->next; + free(rule); + } + command = NULL; + SEND_MTOOL_MSG( + job_result_msg->setResult(job_msg_id, (result == build_ok) ? 0 : 1, DONE); + xdr_msg = (RWCollectable*) job_result_msg; + xdr(&xdrs, xdr_msg); + (void) fflush(mtool_msgs_fp); + + delete job_result_msg; + ); + 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) { + /* + * Do not return memory allocated for SUNPRO_DEPENDENCIES + * It will be returned in setvar_daemon() in macro.cc + */ + // retmem_mb(val); + spro->body.env_mem.value = NULL; + } + } + + return result; +} + + +#if defined(DISTRIBUTED) || defined(MAKETOOL) /* tolik */ + +/* + * Create and send an Avo_MToolRsrcInfoMsg. + */ +void +send_rsrc_info_msg(int max_jobs, char *hostname, char *username) +{ + static int first = 1; + Avo_MToolRsrcInfoMsg *msg; + RWSlistCollectables server_list; + Avo_ServerState *server_state; + RWCollectable *xdr_msg; + + if (!first) { + return; + } + first = 0; + + create_xdrs_ptr(); + + server_state = new Avo_ServerState(max_jobs, hostname, username); + server_list.append(server_state); + msg = new Avo_MToolRsrcInfoMsg(&server_list); + + xdr_msg = (RWCollectable *)msg; + xdr(get_xdrs_ptr(), xdr_msg); + (void) fflush(get_mtool_msgs_fp()); + + delete server_state; + delete msg; +} + +/* + * Create and send an Avo_MToolJobStartMsg. + */ +void +send_job_start_msg(Property line) +{ + int cmd_options = 0; + Avo_MToolJobStartMsg *msg; + Cmd_line rule; + Name target = line->body.line.target; + RWCollectable *xdr_msg; + + if (userName[0] == '\0') { + avo_get_user(userName, NULL); + } + if (hostName[0] == '\0') { + strcpy(hostName, avo_hostname()); + } + + msg = new Avo_MToolJobStartMsg(); + msg->setJobId(++job_msg_id); + msg->setTarget(AVO_STRDUP(target->string_mb)); + msg->setHost(AVO_STRDUP(hostName)); + msg->setUser(AVO_STRDUP(userName)); + + 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); + } + cmd_options = 0; + if (rule->ignore_error || ignore_errors) { + cmd_options |= ignore_mask; + } + if (rule->silent || silent) { + cmd_options |= silent_mask; + } + if (rule->command_line->meta) { + cmd_options |= meta_mask; + } + if (!touch && (rule->command_line->hash.length > 0)) { + msg->appendCmd(new Avo_DmakeCommand(rule->command_line->string_mb, cmd_options)); + } + } + + xdr_msg = (RWCollectable*) msg; + xdr(&xdrs, xdr_msg); + (void) fflush(mtool_msgs_fp); + +/* tolik, 08/39/2002. + I commented out this code because it causes using unallocated memory. + delete msg; +*/ +} + +/* + * Append the stdout/err to Avo_MToolJobResultMsg. + */ +static void +append_job_result_msg(Avo_MToolJobResultMsg *job_result_msg) +{ + FILE *fp; + char line[MAXPATHLEN]; + char stdout_file2[MAXPATHLEN]; + + if (stdout_file != NULL) { + fp = fopen(stdout_file, "r"); + if (fp == NULL) { + /* Hmmm... what should we do here? */ + warning(catgets(catd, 1, 326, "fopen() of stdout_file failed. Output may be lost")); + return; + } + while (fgets(line, MAXPATHLEN, fp) != NULL) { + if (line[strlen(line) - 1] == '\n') { + line[strlen(line) - 1] = '\0'; + } + job_result_msg->appendOutput(AVO_STRDUP(line)); + } + (void) fclose(fp); + us_sleep(STAT_RETRY_SLEEP_TIME); + } else { + /* Hmmm... stdout_file shouldn't be NULL */ + warning(catgets(catd, 1, 327, "Internal stdout_file variable shouldn't be NULL. Output may be lost")); + } +} +#endif /* TEAMWARE_MAKE_CMN */ + +/* + * vpath_translation(cmd) + * + * Translates one command line by + * checking each word. If the word has an alias it is translated. + * + * Return value: + * The translated command + * + * Parameters: + * cmd Command to translate + * + * Global variables used: + */ +Name +vpath_translation(register Name cmd) +{ + wchar_t buffer[STRING_BUFFER_LENGTH]; + String_rec new_cmd; + wchar_t *p; + wchar_t *start; + + if (!vpath_defined || (cmd == NULL) || (cmd->hash.length == 0)) { + return cmd; + } + INIT_STRING_FROM_STACK(new_cmd, buffer); + + Wstring wcb(cmd); + p = wcb.get_string(); + + while (*p != (int) nul_char) { + while (iswspace(*p) && (*p != (int) nul_char)) { + append_char(*p++, &new_cmd); + } + start = p; + while (!iswspace(*p) && (*p != (int) nul_char)) { + p++; + } + cmd = GETNAME(start, p - start); + if (cmd->has_vpath_alias_prop) { + cmd = get_prop(cmd->prop, vpath_alias_prop)-> + body.vpath_alias.alias; + APPEND_NAME(cmd, + &new_cmd, + (int) cmd->hash.length); + } else { + append_string(start, &new_cmd, p - start); + } + } + cmd = GETNAME(new_cmd.buffer.start, FIND_LENGTH); + if (new_cmd.free_after_use) { + retmem(new_cmd.buffer.start); + } + return cmd; +} + +/* + * check_state(temp_file_name) + * + * Reads and checks the state changed by the previously executed command. + * + * Parameters: + * temp_file_name The auto dependency temp file + * + * Global variables used: + */ +void +check_state(Name temp_file_name) +{ + if (!keep_state) { + return; + } + + /* + * Then read the temp file that now might + * contain dependency reports from utilities + */ + read_dependency_file(temp_file_name); + + /* + * And reread .make.state if it + * changed (the command ran recursive makes) + */ + check_read_state_file(); + if (temp_file_name != NULL) { + (void) unlink(temp_file_name->string_mb); + } +} + +/* + * read_dependency_file(filename) + * + * Read the temp file used for reporting dependencies to make + * + * Parameters: + * filename The name of the file with the state info + * + * Global variables used: + * makefile_type The type of makefile being read + * read_trace_level Debug flag + * temp_file_number The always increasing number for unique files + * trace_reader Debug flag + */ +static void +read_dependency_file(register Name filename) +{ + register Makefile_type save_makefile_type; + + if (filename == NULL) { + return; + } + filename->stat.time = file_no_time; + if (exists(filename) > file_doesnt_exist) { + save_makefile_type = makefile_type; + makefile_type = reading_cpp_file; + if (read_trace_level > 1) { + trace_reader = true; + } + temp_file_number++; + (void) read_simple_file(filename, + false, + false, + false, + false, + false, + false); + trace_reader = false; + makefile_type = save_makefile_type; + } +} + +/* + * check_read_state_file() + * + * Check if .make.state has changed + * If it has we reread it + * + * Parameters: + * + * Global variables used: + * make_state Make state file name + * makefile_type Type of makefile being read + * read_trace_level Debug flag + * trace_reader Debug flag + */ +static void +check_read_state_file(void) +{ + timestruc_t previous = make_state->stat.time; + register Makefile_type save_makefile_type; + register Property makefile; + + make_state->stat.time = file_no_time; + if ((exists(make_state) == file_doesnt_exist) || + (make_state->stat.time == previous)) { + return; + } + save_makefile_type = makefile_type; + makefile_type = rereading_statefile; + /* Make sure we clear the old cached contents of .make.state */ + makefile = maybe_append_prop(make_state, makefile_prop); + if (makefile->body.makefile.contents != NULL) { + retmem(makefile->body.makefile.contents); + makefile->body.makefile.contents = NULL; + } + 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; + makefile_type = save_makefile_type; +} + +/* + * do_assign(line, target) + * + * Handles runtime assignments for command lines prefixed with "=". + * + * Parameters: + * line The command that contains an assignment + * target The Name of the target, used for error reports + * + * Global variables used: + * assign_done Set to indicate doname needs to reprocess + */ +static void +do_assign(register Name line, register Name target) +{ + Wstring wcb(line); + register wchar_t *string = wcb.get_string(); + register wchar_t *equal; + register Name name; + register Boolean append = false; + + /* + * If any runtime assignments are done, doname() must reprocess all + * targets in the future since the macro values used to build the + * command lines for the targets might have changed. + */ + assign_done = true; + /* Skip white space. */ + while (iswspace(*string)) { + string++; + } + equal = string; + /* Find "+=" or "=". */ + while (!iswspace(*equal) && + (*equal != (int) plus_char) && + (*equal != (int) equal_char)) { + equal++; + } + /* Internalize macro name. */ + name = GETNAME(string, equal - string); + /* Skip over "+=" "=". */ + while (!((*equal == (int) nul_char) || + (*equal == (int) equal_char) || + (*equal == (int) plus_char))) { + equal++; + } + switch (*equal) { + case nul_char: + fatal(catgets(catd, 1, 31, "= expected in rule `%s' for target `%s'"), + line->string_mb, + target->string_mb); + case plus_char: + append = true; + equal++; + break; + } + equal++; + /* Skip over whitespace in front of value. */ + while (iswspace(*equal)) { + equal++; + } + /* Enter new macro value. */ + enter_equal(name, + GETNAME(equal, wcb.get_string() + line->hash.length - equal), + append); +} + +/* + * build_command_strings(target, line) + * + * Builds the command string to used when + * building a target. If the string is different from the previous one + * is_out_of_date is set. + * + * Parameters: + * target Target to build commands for + * line Where to stuff result + * + * Global variables used: + * c_at The Name "@", used to set macro value + * command_changed Set if command is different from old + * debug_level Should we trace activities? + * do_not_exec_rule Always echo when running -n + * empty_name The Name "", used for empty rule + * funny Semantics of characters + * ignore_errors Used to init field for line + * is_conditional Set to false befor evaling macro, checked + * after expanding macros + * keep_state Indicates that .KEEP_STATE is on + * make_word_mentioned Set by macro eval, inits field for cmd + * query The Name "?", used to set macro value + * query_mentioned Set by macro eval, inits field for cmd + * recursion_level Used for tracing + * silent Used to init field for line + */ +static void +build_command_strings(Name target, register Property line) +{ + String_rec command_line; + register Cmd_line command_template = line->body.line.command_template; + register Cmd_line *insert = &line->body.line.command_used; + register Cmd_line used = *insert; + wchar_t buffer[STRING_BUFFER_LENGTH]; + wchar_t *start; + Name new_command_line; + register Boolean new_command_longer = false; + register Boolean ignore_all_command_dependency = true; + Property member; + static Name less_name; + static Name percent_name; + static Name star; + Name tmp_name; + + if (less_name == NULL) { + MBSTOWCS(wcs_buffer, "<"); + less_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "%"); + percent_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "*"); + star = GETNAME(wcs_buffer, FIND_LENGTH); + } + + /* We have to check if a target depends on conditional macros */ + /* Targets that do must be reprocessed by doname() each time around */ + /* since the macro values used when building the target might have */ + /* changed */ + conditional_macro_used = false; + /* If we are building a lib.a(member) target $@ should be bound */ + /* to lib.a */ + if (target->is_member && + ((member = get_prop(target->prop, member_prop)) != NULL)) { + target = member->body.member.library; + } + /* If we are building a "::" help target $@ should be bound to */ + /* the real target name */ + /* A lib.a(member) target is never :: */ + if (target->has_target_prop) { + target = get_prop(target->prop, target_prop)-> + body.target.target; + } + /* Bind the magic macros that make supplies */ + tmp_name = target; + if(tmp_name != NULL) { + if (tmp_name->has_vpath_alias_prop) { + tmp_name = get_prop(tmp_name->prop, vpath_alias_prop)-> + body.vpath_alias.alias; + } + } + (void) SETVAR(c_at, tmp_name, false); + + tmp_name = line->body.line.star; + if(tmp_name != NULL) { + if (tmp_name->has_vpath_alias_prop) { + tmp_name = get_prop(tmp_name->prop, vpath_alias_prop)-> + body.vpath_alias.alias; + } + } + (void) SETVAR(star, tmp_name, false); + + tmp_name = line->body.line.less; + if(tmp_name != NULL) { + if (tmp_name->has_vpath_alias_prop) { + tmp_name = get_prop(tmp_name->prop, vpath_alias_prop)-> + body.vpath_alias.alias; + } + } + (void) SETVAR(less_name, tmp_name, false); + + tmp_name = line->body.line.percent; + if(tmp_name != NULL) { + if (tmp_name->has_vpath_alias_prop) { + tmp_name = get_prop(tmp_name->prop, vpath_alias_prop)-> + body.vpath_alias.alias; + } + } + (void) SETVAR(percent_name, tmp_name, false); + + /* $? is seldom used and it is expensive to build */ + /* so we store the list form and build the string on demand */ + Chain query_list = NULL; + Chain *query_list_tail = &query_list; + + for (Chain ch = line->body.line.query; ch != NULL; ch = ch->next) { + *query_list_tail = ALLOC(Chain); + (*query_list_tail)->name = ch->name; + if ((*query_list_tail)->name->has_vpath_alias_prop) { + (*query_list_tail)->name = + get_prop((*query_list_tail)->name->prop, + vpath_alias_prop)->body.vpath_alias.alias; + } + (*query_list_tail)->next = NULL; + query_list_tail = &(*query_list_tail)->next; + } + (void) setvar_daemon(query, + (Name) query_list, + false, + chain_daemon, + false, + debug_level); + + /* build $^ */ + Chain hat_list = NULL; + Chain *hat_list_tail = &hat_list; + + for (Dependency dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + /* skip automatic dependencies */ + if (!dependency->automatic) { + if ((dependency->name != force) && + (dependency->stale == false)) { + *hat_list_tail = ALLOC(Chain); + + if (dependency->name->is_member && + (get_prop(dependency->name->prop, member_prop) != NULL)) { + (*hat_list_tail)->name = + get_prop(dependency->name->prop, + member_prop)->body.member.member; + } else { + (*hat_list_tail)->name = dependency->name; + } + + if((*hat_list_tail)->name != NULL) { + if ((*hat_list_tail)->name->has_vpath_alias_prop) { + (*hat_list_tail)->name = + get_prop((*hat_list_tail)->name->prop, + vpath_alias_prop)->body.vpath_alias.alias; + } + } + + (*hat_list_tail)->next = NULL; + hat_list_tail = &(*hat_list_tail)->next; + } + } + } + (void) setvar_daemon(hat, + (Name) hat_list, + false, + chain_daemon, + false, + debug_level); + +/* We have two command sequences we need to handle */ +/* The old one that we probably read from .make.state */ +/* and the new one we are building that will replace the old one */ +/* Even when KEEP_STATE is not on we build a new command sequence and store */ +/* it in the line prop. This command sequence is then executed by */ +/* run_command(). If KEEP_STATE is on it is also later written to */ +/* .make.state. The routine replaces the old command line by line with the */ +/* new one trying to reuse Cmd_lines */ + + /* If there is no old command_used we have to start creating */ + /* Cmd_lines to keep the new cmd in */ + if (used == NULL) { + new_command_longer = true; + *insert = used = ALLOC(Cmd_line); + used->next = NULL; + used->command_line = NULL; + insert = &used->next; + } + /* Run thru the template for the new command and build the expanded */ + /* new command lines */ + for (; + command_template != NULL; + command_template = command_template->next, insert = &used->next, used = *insert) { + /* If there is no old command_used Cmd_line we need to */ + /* create one and say that cmd consistency failed */ + if (used == NULL) { + new_command_longer = true; + *insert = used = ALLOC(Cmd_line); + used->next = NULL; + used->command_line = empty_name; + } + /* Prepare the Cmd_line for the processing */ + /* The command line prefixes "@-=?" are stripped and that */ + /* information is saved in the Cmd_line */ + used->assign = false; + used->ignore_error = ignore_errors; + used->silent = silent; + used->always_exec = false; + /* Expand the macros in the command line */ + INIT_STRING_FROM_STACK(command_line, buffer); + make_word_mentioned = + query_mentioned = + false; + expand_value(command_template->command_line, &command_line, true); + /* If the macro $(MAKE) is mentioned in the command */ + /* "make -n" runs actually execute the command */ + used->make_refd = make_word_mentioned; + used->ignore_command_dependency = query_mentioned; + /* Strip the prefixes */ + start = command_line.buffer.start; + for (; + iswspace(*start) || + (get_char_semantics_value(*start) & (int) command_prefix_sem); + start++) { + switch (*start) { + case question_char: + used->ignore_command_dependency = true; + break; + case exclam_char: + used->ignore_command_dependency = false; + break; + case equal_char: + used->assign = true; + break; + case hyphen_char: + used->ignore_error = true; + break; + case at_char: + if (!do_not_exec_rule) { + used->silent = true; + } + break; + case plus_char: + if(posix) { + used->always_exec = true; + } + break; + } + } + /* If all command lines of the template are prefixed with "?"*/ + /* the VIRTUAL_ROOT is not used for cmd consistency checks */ + if (!used->ignore_command_dependency) { + ignore_all_command_dependency = false; + } + /* Internalize the expanded and stripped command line */ + new_command_line = GETNAME(start, FIND_LENGTH); + if ((used->command_line == NULL) && + (line->body.line.sccs_command)) { + used->command_line = new_command_line; + new_command_longer = false; + } + /* Compare it with the old one for command consistency */ + if (used->command_line != new_command_line) { + Name vpath_translated = vpath_translation(new_command_line); + if (keep_state && + !used->ignore_command_dependency && (vpath_translated != used->command_line)) { + if (debug_level > 0) { + if (used->command_line != NULL + && *used->command_line->string_mb != + '\0') { + (void) printf(catgets(catd, 1, 32, "%*sBuilding %s because new command \n\t%s\n%*sdifferent from old\n\t%s\n"), + recursion_level, + "", + target->string_mb, + vpath_translated->string_mb, + recursion_level, + "", + used-> + command_line-> + string_mb); + } else { + (void) printf(catgets(catd, 1, 33, "%*sBuilding %s because new command \n\t%s\n%*sdifferent from empty old command\n"), + recursion_level, + "", + target->string_mb, + vpath_translated->string_mb, + recursion_level, + ""); + } + } + command_changed = true; + line->body.line.is_out_of_date = true; + } + used->command_line = new_command_line; + } + if (command_line.free_after_use) { + retmem(command_line.buffer.start); + } + } + /* Check if the old command is longer than the new for */ + /* command consistency */ + if (used != NULL) { + *insert = NULL; + if (keep_state && + !ignore_all_command_dependency) { + if (debug_level > 0) { + (void) printf(catgets(catd, 1, 34, "%*sBuilding %s because new command shorter than old\n"), + recursion_level, + "", + target->string_mb); + } + command_changed = true; + line->body.line.is_out_of_date = true; + } + } + /* Check if the new command is longer than the old command for */ + /* command consistency */ + if (new_command_longer && + !ignore_all_command_dependency && + keep_state) { + if (debug_level > 0) { + (void) printf(catgets(catd, 1, 35, "%*sBuilding %s because new command longer than old\n"), + recursion_level, + "", + target->string_mb); + } + command_changed = true; + line->body.line.is_out_of_date = true; + } + /* Unbind the magic macros */ + (void) SETVAR(c_at, (Name) NULL, false); + (void) SETVAR(star, (Name) NULL, false); + (void) SETVAR(less_name, (Name) NULL, false); + (void) SETVAR(percent_name, (Name) NULL, false); + (void) SETVAR(query, (Name) NULL, false); + if (query_list != NULL) { + delete_query_chain(query_list); + } + (void) SETVAR(hat, (Name) NULL, false); + if (hat_list != NULL) { + delete_query_chain(hat_list); + } + + if (conditional_macro_used) { + target->conditional_macro_list = cond_macro_list; + cond_macro_list = NULL; + target->depends_on_conditional = true; + } +} + +/* + * touch_command(line, target, result) + * + * If this is an "make -t" run we do this. + * We touch all targets in the target group ("foo + fie:") if any. + * + * Return value: + * Indicates if the command failed or not + * + * Parameters: + * line The command line to update + * target The target we are touching + * result Initial value for the result we return + * + * Global variables used: + * do_not_exec_rule Indicates that -n is on + * silent Do not echo commands + */ +static Doname +touch_command(register Property line, register Name target, Doname result) +{ + Name name; + register Chain target_group; + String_rec touch_string; + wchar_t buffer[MAXPATHLEN]; + Name touch_cmd; + Cmd_line rule; + +#if defined(DISTRIBUTED) || defined(MAKETOOL) /* tolik */ + Avo_MToolJobResultMsg *job_result_msg; + RWCollectable *xdr_msg; + int child_pid = 0; + wchar_t string[MAXPATHLEN]; + char mbstring[MAXPATHLEN]; + int filed; +#endif + + SEND_MTOOL_MSG( + if (!sent_rsrc_info_msg) { + if (userName[0] == '\0') { + avo_get_user(userName, NULL); + } + if (hostName[0] == '\0') { + strcpy(hostName, avo_hostname()); + } + send_rsrc_info_msg(1, hostName, userName); + sent_rsrc_info_msg = 1; + } + send_job_start_msg(line); + job_result_msg = new Avo_MToolJobResultMsg(); + ); + for (name = target, target_group = NULL; name != NULL;) { + if (!name->is_member) { + /* + * Build a touch command that can be passed + * to dosys(). If KEEP_STATE is on, "make -t" + * will save the proper command, not the + * "touch" in .make.state. + */ + INIT_STRING_FROM_STACK(touch_string, buffer); + MBSTOWCS(wcs_buffer, NOCATGETS("touch ")); + append_string(wcs_buffer, &touch_string, FIND_LENGTH); + touch_cmd = name; + if (name->has_vpath_alias_prop) { + touch_cmd = get_prop(name->prop, + vpath_alias_prop)-> + body.vpath_alias.alias; + } + APPEND_NAME(touch_cmd, + &touch_string, + FIND_LENGTH); + touch_cmd = GETNAME(touch_string.buffer.start, + FIND_LENGTH); + if (touch_string.free_after_use) { + retmem(touch_string.buffer.start); + } + if (!silent || + do_not_exec_rule && + (target_group == NULL)) { + (void) printf("%s\n", touch_cmd->string_mb); + SEND_MTOOL_MSG( + job_result_msg->appendOutput(AVO_STRDUP(touch_cmd->string_mb)); + ); + } + /* Run the touch command, or simulate it */ + if (!do_not_exec_rule) { + + SEND_MTOOL_MSG( + (void) sprintf(mbstring, + NOCATGETS("%s/make.stdout.%d.%d.XXXXXX"), + tmpdir, + getpid(), + file_number++); + + int tmp_fd = mkstemp(mbstring); + if(tmp_fd) { + (void) close(tmp_fd); + } + + stdout_file = strdup(mbstring); + stderr_file = NULL; + child_pid = pollResults(stdout_file, + (char *)NULL, + (char *)NULL); + ); + + result = dosys(touch_cmd, + false, + false, + false, + false, + name, + send_mtool_msgs); + + SEND_MTOOL_MSG( + append_job_result_msg(job_result_msg); + if (child_pid > 0) { + kill(child_pid, SIGUSR1); + while (!((waitpid(child_pid, 0, 0) == -1) + && (errno == ECHILD))); + } + child_pid = 0; + (void) unlink(stdout_file); + retmem_mb(stdout_file); + stdout_file = NULL; + ); + + } else { + result = build_ok; + } + } else { + result = build_ok; + } + if (target_group == NULL) { + target_group = line->body.line.target_group; + } else { + target_group = target_group->next; + } + if (target_group != NULL) { + name = target_group->name; + } else { + name = NULL; + } + } + SEND_MTOOL_MSG( + job_result_msg->setResult(job_msg_id, (result == build_ok) ? 0 : 1, DONE); + xdr_msg = (RWCollectable*) job_result_msg; + xdr(&xdrs, xdr_msg); + (void) fflush(mtool_msgs_fp); + delete job_result_msg; + ); + return result; +} + +/* + * update_target(line, result) + * + * updates the status of a target after executing its commands. + * + * Parameters: + * line The command line block to update + * result Indicates that build is OK so can update + * + * Global variables used: + * do_not_exec_rule Indicates that -n is on + * touch Fake the new timestamp if we are just touching + */ +void +update_target(Property line, Doname result) +{ + Name target; + Chain target_group; + Property line2; + timestruc_t old_stat_time; + Property member; + + /* + * [tolik] Additional fix for bug 1063790. It was fixed + * for serial make long ago, but DMake dumps core when + * target is a symlink and sccs file is newer then target. + * In this case, finish_children() calls update_target() + * with line==NULL. + */ + if(line == NULL) { + /* XXX. Should we do anything here? */ + return; + } + + target = line->body.line.target; + + if ((result == build_ok) && (line->body.line.command_used != NULL)) { + if (do_not_exec_rule || + touch || + (target->is_member && + (line->body.line.command_template != NULL) && + (line->body.line.command_template->command_line->string_mb[0] == 0) && + (line->body.line.command_template->next == NULL))) { + /* If we are simulating execution we need to fake a */ + /* new timestamp for the target we didnt build */ + target->stat.time = file_max_time; + } else { + /* + * If we really built the target we read the new + * timestamp. + * Fix for bug #1110906: if .c file is newer than + * the corresponding .o file which is in an archive + * file, make will compile the .c file but it won't + * update the object in the .a file. + */ + old_stat_time = target->stat.time; + target->stat.time = file_no_time; + (void) exists(target); + if ((target->is_member) && + (target->stat.time == old_stat_time)) { + member = get_prop(target->prop, member_prop); + if (member != NULL) { + target->stat.time = member->body.member.library->stat.time; + target->stat.time.tv_sec++; + } + } + } + /* If the target is part of a group we need to propagate the */ + /* result of the run to all members */ + for (target_group = line->body.line.target_group; + target_group != NULL; + target_group = target_group->next) { + target_group->name->stat.time = target->stat.time; + line2 = maybe_append_prop(target_group->name, + line_prop); + line2->body.line.command_used = + line->body.line.command_used; + line2->body.line.target = target_group->name; + } + } + target->has_built = true; +} + +/* + * sccs_get(target, command) + * + * Figures out if it possible to sccs get a file + * and builds the command to do it if it is. + * + * Return value: + * Indicates if sccs get failed or not + * + * Parameters: + * target Target to get + * command Where to deposit command to use + * + * Global variables used: + * debug_level Should we trace activities? + * recursion_level Used for tracing + * sccs_get_rule The rule to used for sccs getting + */ +static Doname +sccs_get(register Name target, register Property *command) +{ + register int result; + char link[MAXPATHLEN]; + String_rec string; + wchar_t name[MAXPATHLEN]; + register wchar_t *p; + timestruc_t sccs_time; + register Property line; + int sym_link_depth = 0; + + /* For sccs, we need to chase symlinks. */ + while (target->stat.is_sym_link) { + if (sym_link_depth++ > 90) { + fatal(catgets(catd, 1, 95, "Can't read symbolic link `%s': Number of symbolic links encountered during path name traversal exceeds 90."), + target->string_mb); + } + /* Read the value of the link. */ + result = readlink_vroot(target->string_mb, + link, + sizeof(link), + NULL, + VROOT_DEFAULT); + if (result == -1) { + fatal(catgets(catd, 1, 36, "Can't read symbolic link `%s': %s"), + target->string_mb, errmsg(errno)); + } + link[result] = 0; + /* Use the value to build the proper filename. */ + INIT_STRING_FROM_STACK(string, name); + + Wstring wcb(target); + if ((link[0] != slash_char) && + ((p = (wchar_t *) wsrchr(wcb.get_string(), slash_char)) != NULL)) { + append_string(wcb.get_string(), &string, p - wcb.get_string() + 1); + } + append_string(link, &string, result); + /* Replace the old name with the translated name. */ + target = normalize_name(string.buffer.start, string.text.p - string.buffer.start); + (void) exists(target); + if (string.free_after_use) { + retmem(string.buffer.start); + } + } + + /* + * read_dir() also reads the ?/SCCS dir and saves information + * about which files have SCSC/s. files. + */ + if (target->stat.has_sccs == DONT_KNOW_SCCS) { + read_directory_of_file(target); + } + switch (target->stat.has_sccs) { + case DONT_KNOW_SCCS: + /* We dont know by now there is no SCCS/s.* */ + target->stat.has_sccs = NO_SCCS; + case NO_SCCS: + /* + * If there is no SCCS/s.* but the plain file exists, + * we say things are OK. + */ + if (target->stat.time > file_doesnt_exist) { + return build_ok; + } + /* If we cant find the plain file, we give up. */ + return build_dont_know; + case HAS_SCCS: + /* + * Pay dirt. We now need to figure out if the plain file + * is out of date relative to the SCCS/s.* file. + */ + sccs_time = exists(get_prop(target->prop, + sccs_prop)->body.sccs.file); + break; + } + + if ((!target->has_complained && + (sccs_time != file_doesnt_exist) && + (sccs_get_rule != NULL))) { + /* only checking */ + if (command == NULL) { + return build_ok; + } + /* + * We provide a command line for the target. The line is a + * "sccs get" command from default.mk. + */ + line = maybe_append_prop(target, line_prop); + *command = line; + if (sccs_time > target->stat.time) { + /* + * And only if the plain file is out of date do we + * request execution of the command. + */ + line->body.line.is_out_of_date = true; + if (debug_level > 0) { + (void) printf(catgets(catd, 1, 37, "%*sSccs getting %s because s. file is younger than source file\n"), + recursion_level, + "", + target->string_mb); + } + } + line->body.line.sccs_command = true; + line->body.line.command_template = sccs_get_rule; + if(!svr4 && (!allrules_read || posix)) { + if((target->prop) && + (target->prop->body.sccs.file) && + (target->prop->body.sccs.file->string_mb)) { + if((strlen(target->prop->body.sccs.file->string_mb) == + strlen(target->string_mb) + 2) && + (target->prop->body.sccs.file->string_mb[0] == 's') && + (target->prop->body.sccs.file->string_mb[1] == '.')) { + + line->body.line.command_template = get_posix_rule; + } + } + } + line->body.line.target = target; + /* + * Also make sure the rule is build with $* and $< + * bound properly. + */ + line->body.line.star = NULL; + line->body.line.less = NULL; + line->body.line.percent = NULL; + return build_ok; + } + return build_dont_know; +} + +/* + * read_directory_of_file(file) + * + * Reads the directory the specified file lives in. + * + * Parameters: + * file The file we need to read dir for + * + * Global variables used: + * dot The Name ".", used as the default dir + */ +void +read_directory_of_file(register Name file) +{ + + Wstring file_string(file); + wchar_t * wcb = file_string.get_string(); + wchar_t usr_include_buf[MAXPATHLEN]; + wchar_t usr_include_sys_buf[MAXPATHLEN]; + + register Name directory = dot; + register wchar_t *p = (wchar_t *) wsrchr(wcb, + (int) slash_char); + register int length = p - wcb; + static Name usr_include; + static Name usr_include_sys; + + if (usr_include == NULL) { + MBSTOWCS(usr_include_buf, NOCATGETS("/usr/include")); + usr_include = GETNAME(usr_include_buf, FIND_LENGTH); + MBSTOWCS(usr_include_sys_buf, NOCATGETS("/usr/include/sys")); + usr_include_sys = GETNAME(usr_include_sys_buf, FIND_LENGTH); + } + + /* + * If the filename contains a "/" we have to extract the path + * Else the path defaults to ".". + */ + if (p != NULL) { + /* + * Check some popular directories first to possibly + * save time. Compare string length first to gain speed. + */ + if ((usr_include->hash.length == length) && + IS_WEQUALN(usr_include_buf, + wcb, + length)) { + directory = usr_include; + } else if ((usr_include_sys->hash.length == length) && + IS_WEQUALN(usr_include_sys_buf, + wcb, + length)) { + directory = usr_include_sys; + } else { + directory = GETNAME(wcb, length); + } + } + (void) read_dir(directory, + (wchar_t *) NULL, + (Property) NULL, + (wchar_t *) NULL); +} + +/* + * add_pattern_conditionals(target) + * + * Scan the list of conditionals defined for pattern targets and add any + * that match this target to its list of conditionals. + * + * Parameters: + * target The target we should add conditionals for + * + * Global variables used: + * conditionals The list of pattern conditionals + */ +static void +add_pattern_conditionals(register Name target) +{ + register Property conditional; + Property new_prop; + Property *previous; + Name_rec dummy; + wchar_t *pattern; + wchar_t *percent; + int length; + + Wstring wcb(target); + Wstring wcb1; + + for (conditional = get_prop(conditionals->prop, conditional_prop); + conditional != NULL; + conditional = get_prop(conditional->next, conditional_prop)) { + wcb1.init(conditional->body.conditional.target); + pattern = wcb1.get_string(); + if (pattern[1] != 0) { + percent = (wchar_t *) wschr(pattern, (int) percent_char); + if (!wcb.equaln(pattern, percent-pattern) || + !IS_WEQUAL(wcb.get_string(wcb.length()-wslen(percent+1)), percent+1)) { + continue; + } + } + for (previous = &target->prop; + *previous != NULL; + previous = &(*previous)->next) { + if (((*previous)->type == conditional_prop) && + ((*previous)->body.conditional.sequence > + conditional->body.conditional.sequence)) { + break; + } + } + if (*previous == NULL) { + new_prop = append_prop(target, conditional_prop); + } else { + dummy.prop = NULL; + new_prop = append_prop(&dummy, conditional_prop); + new_prop->next = *previous; + *previous = new_prop; + } + target->conditional_cnt++; + new_prop->body.conditional = conditional->body.conditional; + } +} + +/* + * set_locals(target, old_locals) + * + * Sets any conditional macros for the target. + * Each target carries a possibly empty set of conditional properties. + * + * Parameters: + * target The target to set conditional macros for + * old_locals Space to store old values in + * + * Global variables used: + * debug_level Should we trace activity? + * is_conditional We need to preserve this value + * recursion_level Used for tracing + */ +void +set_locals(register Name target, register Property old_locals) +{ + register Property conditional; + register int i; + register Boolean saved_conditional_macro_used; + Chain cond_name; + Chain cond_chain; + +#ifdef DISTRIBUTED + if (target->dont_activate_cond_values) { + return; + } +#endif + + saved_conditional_macro_used = conditional_macro_used; + + /* Scan the list of conditional properties and apply each one */ + for (conditional = get_prop(target->prop, conditional_prop), i = 0; + conditional != NULL; + conditional = get_prop(conditional->next, conditional_prop), + i++) { + /* Save the old value */ + old_locals[i].body.macro = + maybe_append_prop(conditional->body.conditional.name, + macro_prop)->body.macro; + if (debug_level > 1) { + (void) printf(catgets(catd, 1, 38, "%*sActivating conditional value: "), + recursion_level, + ""); + } + /* Set the conditional value. Macros are expanded when the */ + /* macro is refd as usual */ + if ((conditional->body.conditional.name != virtual_root) || + (conditional->body.conditional.value != virtual_root)) { + (void) SETVAR(conditional->body.conditional.name, + conditional->body.conditional.value, + (Boolean) conditional->body.conditional.append); + } + cond_name = ALLOC(Chain); + cond_name->name = conditional->body.conditional.name; + } + /* Put this target on the front of the chain of conditional targets */ + cond_chain = ALLOC(Chain); + cond_chain->name = target; + cond_chain->next = conditional_targets; + conditional_targets = cond_chain; + conditional_macro_used = saved_conditional_macro_used; +} + +/* + * reset_locals(target, old_locals, conditional, index) + * + * Removes any conditional macros for the target. + * + * Parameters: + * target The target we are retoring values for + * old_locals The values to restore + * conditional The first conditional block for the target + * index into the old_locals vector + * Global variables used: + * debug_level Should we trace activities? + * recursion_level Used for tracing + */ +void +reset_locals(register Name target, register Property old_locals, register Property conditional, register int index) +{ + register Property this_conditional; + Chain cond_chain; + +#ifdef DISTRIBUTED + if (target->dont_activate_cond_values) { + return; + } +#endif + + /* Scan the list of conditional properties and restore the old value */ + /* to each one Reverse the order relative to when we assigned macros */ + this_conditional = get_prop(conditional->next, conditional_prop); + if (this_conditional != NULL) { + reset_locals(target, old_locals, this_conditional, index+1); + } else { + /* Remove conditional target from chain */ + if (conditional_targets == NULL || + conditional_targets->name != target) { + warning(catgets(catd, 1, 39, "Internal error: reset target not at head of condtional_targets chain")); + } else { + cond_chain = conditional_targets->next; + retmem_mb((caddr_t) conditional_targets); + conditional_targets = cond_chain; + } + } + get_prop(conditional->body.conditional.name->prop, + macro_prop)->body.macro = old_locals[index].body.macro; + if (conditional->body.conditional.name == virtual_root) { + (void) SETVAR(virtual_root, getvar(virtual_root), false); + } + if (debug_level > 1) { + if (old_locals[index].body.macro.value != NULL) { + (void) printf(catgets(catd, 1, 40, "%*sdeactivating conditional value: %s= %s\n"), + recursion_level, + "", + conditional->body.conditional.name-> + string_mb, + old_locals[index].body.macro.value-> + string_mb); + } else { + (void) printf(catgets(catd, 1, 41, "%*sdeactivating conditional value: %s =\n"), + recursion_level, + "", + conditional->body.conditional.name-> + string_mb); + } + } +} + +/* + * check_auto_dependencies(target, auto_count, automatics) + * + * Returns true if the target now has a dependency + * it didn't previously have (saved on automatics). + * + * Return value: + * true if new dependency found + * + * Parameters: + * target Target we check + * auto_count Number of old automatic vars + * automatics Saved old automatics + * + * Global variables used: + * keep_state Indicates that .KEEP_STATE is on + */ +Boolean +check_auto_dependencies(Name target, int auto_count, Name *automatics) +{ + Name *p; + int n; + Property line; + Dependency dependency; + + if (keep_state) { + if ((line = get_prop(target->prop, line_prop)) == NULL) { + return false; + } + /* Go thru new list of automatic depes */ + for (dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + /* And make sure that each one existed before we */ + /* built the target */ + if (dependency->automatic && !dependency->stale) { + for (n = auto_count, p = automatics; + n > 0; + n--) { + if (*p++ == dependency->name) { + /* If we can find it on the */ + /* saved list of autos we */ + /* are OK */ + goto not_new; + } + } + /* But if we scan over the old list */ + /* of auto. without finding it it is */ + /* new and we must check it */ + return true; + } + not_new:; + } + return false; + } else { + return false; + } +} + +#if defined(DISTRIBUTED) || defined(MAKETOOL) /* tolik */ +void +create_xdrs_ptr(void) +{ + static int xdrs_init = 0; + + if (!xdrs_init) { + xdrs_init = 1; + mtool_msgs_fp = fdopen(mtool_msgs_fd, "a"); + xdrstdio_create(&xdrs, + mtool_msgs_fp, + XDR_ENCODE); + } +} + +XDR * +get_xdrs_ptr(void) +{ + return &xdrs; +} + +FILE * +get_mtool_msgs_fp(void) +{ + return mtool_msgs_fp; +} + +int +get_job_msg_id(void) +{ + return job_msg_id; +} + +// Continuously poll and show the results of remotely executing a job, +// i.e., output the stdout and stderr files. + +static int +pollResults(char *outFn, char *errFn, char *hostNm) +{ + int child; + + child = fork(); + switch (child) { + case -1: + break; + case 0: + enable_interrupt((void (*) (int))SIG_DFL); +#ifdef linux + (void) signal(SIGUSR1, Avo_PollResultsAction_Sigusr1Handler); +#else + (void) sigset(SIGUSR1, Avo_PollResultsAction_Sigusr1Handler); +#endif + pollResultsAction(outFn, errFn); + + exit(0); + break; + default: + break; + } + return child; +} + +// This is the PollResultsAction SIGUSR1 handler. + +static bool_t pollResultsActionTimeToFinish = FALSE; + +extern "C" void +Avo_PollResultsAction_Sigusr1Handler(int foo) +{ + pollResultsActionTimeToFinish = TRUE; +} + +static void +pollResultsAction(char *outFn, char *errFn) +{ + int fd; + time_t file_time = 0; + long file_time_nsec = 0; + struct stat statbuf; + int stat_rc; + + // Keep stat'ing until file exists. + while (((stat_rc = stat(outFn, &statbuf)) != 0) && + (errno == ENOENT) && + !pollResultsActionTimeToFinish) { + us_sleep(STAT_RETRY_SLEEP_TIME); + } + // The previous stat() could be failed due to EINTR + // So one more try is needed + if (stat_rc != 0 && stat(outFn, &statbuf) != 0) { + // stat() failed + warning(NOCATGETS("Internal error: stat(\"%s\", ...) failed: %s\n"), + outFn, strerror(errno)); + exit(1); + } + + if ((fd = open(outFn, O_RDONLY)) < 0 + && (errno != EINTR || (fd = open(outFn, O_RDONLY)) < 0)) { + // open() failed + warning(NOCATGETS("Internal error: open(\"%s\", O_RDONLY) failed: %s\n"), + outFn, strerror(errno)); + exit(1); + } + + while (!pollResultsActionTimeToFinish && stat(outFn, &statbuf) == 0) { +#ifdef linux + if ((statbuf.st_mtime > file_time) + ) { + file_time = statbuf.st_mtime; + rxmGetNextResultsBlock(fd); + } +#else + if ((statbuf.st_mtim.tv_sec > file_time) || + ((statbuf.st_mtim.tv_sec == file_time) && + (statbuf.st_mtim.tv_nsec > file_time_nsec)) + ) { + file_time = statbuf.st_mtim.tv_sec; + file_time_nsec = statbuf.st_mtim.tv_nsec; + rxmGetNextResultsBlock(fd); + } +#endif + us_sleep(STAT_RETRY_SLEEP_TIME); + } + // Check for the rest of output + rxmGetNextResultsBlock(fd); + + (void) close(fd); +} + +static void +rxmGetNextResultsBlock(int fd) +{ + size_t to_read = 8 * 1024; + ssize_t bytes_read; + ssize_t bytes_written; + char results_buf[8 * 1024]; + sigset_t newset; + sigset_t oldset; + + // Read some more from the output/results file. + // Hopefully the kernel managed to prefetch the stuff. + bytes_read = read(fd, results_buf, to_read); + while (bytes_read > 0) { + AVO_BLOCK_INTERUPTS; + bytes_written = write(1, results_buf, bytes_read); + AVO_UNBLOCK_INTERUPTS; + if (bytes_written != bytes_read) { + // write() failed + warning(NOCATGETS("Internal error: write(1, ...) failed: %s\n"), + strerror(errno)); + exit(1); + } + bytes_read = read(fd, results_buf, to_read); + } +} + +// Generic, interruptable microsecond resolution sleep member function. + +static int +us_sleep(unsigned int nusecs) +{ + struct pollfd dummy; + int timeout; + + if ((timeout = nusecs/1000) <= 0) { + timeout = 1; + } + return poll(&dummy, 0, timeout); +} +#endif /* TEAMWARE_MAKE_CMN */ + +// Recursively delete each of the Chain struct on the chain. + +static void +delete_query_chain(Chain ch) +{ + if (ch == NULL) { + return; + } else { + delete_query_chain(ch->next); + retmem_mb((char *) ch); + } +} + +Doname +target_can_be_built(register Name target) { + Doname result = build_dont_know; + Name true_target = target; + Property line; + + if (target == wait_name) { + return(build_ok); + } + /* + * If the target is a constructed one for a "::" target, + * we need to consider that. + */ + if (target->has_target_prop) { + true_target = get_prop(target->prop, + target_prop)->body.target.target; + } + + (void) exists(true_target); + + if (true_target->state == build_running) { + return(build_running); + } + if (true_target->stat.time != file_doesnt_exist) { + result = build_ok; + } + + /* get line property for the target */ + line = get_prop(true_target->prop, line_prop); + + /* first check for explicit rule */ + if (line != NULL && line->body.line.command_template != NULL) { + result = build_ok; + } + /* try to find pattern rule */ + if (result == build_dont_know) { + result = find_percent_rule(target, NULL, false); + } + + /* try to find double suffix rule */ + if (result == build_dont_know) { + if (target->is_member) { + Property member = get_prop(target->prop, member_prop); + if (member != NULL && member->body.member.member != NULL) { + result = find_ar_suffix_rule(target, member->body.member.member, NULL, false); + } else { + result = find_double_suffix_rule(target, NULL, false); + } + } else { + result = find_double_suffix_rule(target, NULL, false); + } + } + + /* try to find suffix rule */ + if ((result == build_dont_know) && second_pass) { + result = find_suffix_rule(target, target, empty_name, NULL, false); + } + + /* check for sccs */ + if (result == build_dont_know) { + result = sccs_get(target, NULL); + } + + /* try to find dyn target */ + if (result == build_dont_know) { + Name dtarg = find_dyntarget(target); + if (dtarg != NULL) { + result = target_can_be_built(dtarg); + } + } + + /* check whether target was mentioned in makefile */ + if (result == build_dont_know) { + if (target->colons != no_colon) { + result = build_ok; + } + } + + /* result */ + return result; +} |