summaryrefslogtreecommitdiff
path: root/usr/src/make_src/Make/bin/make/common/doname.cc
diff options
context:
space:
mode:
authorIgor Pashev <igor.pashev@nexenta.com>2012-06-29 14:36:07 +0400
committerIgor Pashev <igor.pashev@nexenta.com>2012-06-29 14:36:07 +0400
commite0463df9c3d2ee6155221cc443c571d5da47098a (patch)
tree5c6b99e64c1b65d986e2722728c74f202a578be6 /usr/src/make_src/Make/bin/make/common/doname.cc
downloadsunmake-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.cc3786
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;
+}