3215 lines
89 KiB
C++
3215 lines
89 KiB
C++
/*
|
|
* 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.
|
|
*
|
|
* Copyright 2016 RackTop Systems.
|
|
*/
|
|
|
|
/*
|
|
* doname.c
|
|
*
|
|
* Figure out which targets are out of date and rebuild them
|
|
*/
|
|
|
|
/*
|
|
* Included files
|
|
*/
|
|
#include <alloca.h> /* alloca() */
|
|
#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>
|
|
#include <libintl.h>
|
|
#include <signal.h>
|
|
#ifdef __sun
|
|
// XXX really needed, even on Solaris?
|
|
#include <stropts.h>
|
|
#else
|
|
#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() */
|
|
#include <comp/namelen.h>
|
|
|
|
/*
|
|
* Defined macros
|
|
*/
|
|
# define LOCALHOST "localhost"
|
|
|
|
#define MAXRULES 100
|
|
|
|
// 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] = "";
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
/*
|
|
* 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(gettext("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(gettext("Don't know how to make target `%s'"),
|
|
target->string_mb);
|
|
build_failed_seen = true;
|
|
return build_failed;
|
|
}
|
|
fatal(gettext("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;
|
|
Chain target_group;
|
|
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;
|
|
Boolean doing_subtree = false;
|
|
|
|
Boolean recheck_conditionals = false;
|
|
|
|
if (target->state == build_running) {
|
|
return build_running;
|
|
}
|
|
line = get_prop(target->prop, line_prop);
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* 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;
|
|
/*
|
|
} 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;
|
|
}
|
|
*/
|
|
}
|
|
/*
|
|
* 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("%*sdoname(%s)\n",
|
|
recursion_level,
|
|
"",
|
|
target->string_mb);
|
|
}
|
|
recursion_level++;
|
|
/* Avoid infinite loops */
|
|
if (target->state == build_in_progress) {
|
|
warning(gettext("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)
|
|
{
|
|
dynamic_dependencies(target);
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
|
|
/*
|
|
* 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;
|
|
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;
|
|
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;
|
|
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;
|
|
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;
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
/* 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;
|
|
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;
|
|
}
|
|
}
|
|
/* 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(gettext("%*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));
|
|
}
|
|
switch (result) {
|
|
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;
|
|
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(gettext("%*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
|
|
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)
|
|
{
|
|
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;
|
|
}
|
|
} 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;
|
|
} 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 {
|
|
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(gettext("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(gettext("%*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(gettext("%*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(gettext("%*sBuilding %s because %s does not exist\n"),
|
|
recursion_level,
|
|
"",
|
|
true_target->string_mb,
|
|
dependency->name->string_mb);
|
|
} else {
|
|
(void) printf(gettext("%*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;
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
/*
|
|
* 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(gettext("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(gettext("%*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(gettext("%*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) {
|
|
wcb.init(dependency->name);
|
|
if ((start = (wchar_t *) wcschr(wcb.get_string(),
|
|
(int) parenleft_char)) != NULL) {
|
|
/* lib(*) type pattern */
|
|
library = buffer;
|
|
(void) wcsncpy(buffer,
|
|
wcb.get_string(),
|
|
start - wcb.get_string());
|
|
buffer[start-wcb.get_string()] =
|
|
(int) nul_char;
|
|
(void) wcsncpy(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) wcsncpy(pattern,
|
|
wcb.get_string(),
|
|
(int) dependency->name->hash.length);
|
|
pattern[dependency->name->hash.length] =
|
|
(int) nul_char;
|
|
}
|
|
start = (wchar_t *) wcsrchr(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) {
|
|
result = execute_parallel(line, true);
|
|
}
|
|
exit_status = 1;
|
|
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(gettext("%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,
|
|
"%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) {
|
|
result = execute_parallel(line, true);
|
|
}
|
|
} else {
|
|
/*
|
|
* If this is not a touch run, we need to execute the
|
|
* proper command(s) for the target.
|
|
*/
|
|
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) {
|
|
result = execute_parallel(line, true, target->localhost);
|
|
} 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) {
|
|
result = execute_parallel(line, true, target->localhost);
|
|
} else {
|
|
current_target = NULL;
|
|
current_line = NULL;
|
|
target->parallel = false;
|
|
line->body.line.command_used =
|
|
NULL;
|
|
return build_serial;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
result = execute_parallel(line, true, target->localhost);
|
|
}
|
|
}
|
|
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;
|
|
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;
|
|
|
|
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);
|
|
}
|
|
if (rule->command_line->hash.length > 0) {
|
|
/* 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. */
|
|
setvar_envvar();
|
|
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);
|
|
check_state(temp_file_name);
|
|
}
|
|
} else {
|
|
result = build_ok;
|
|
}
|
|
if (result == build_failed) {
|
|
if (silent || rule->silent) {
|
|
(void) printf(gettext("The following command caused the error:\n%s\n"),
|
|
rule->command_line->string_mb);
|
|
}
|
|
if (!rule->ignore_error && !ignore_errors) {
|
|
if (!continue_after_error) {
|
|
fatal(gettext("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;
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* 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(gettext("= 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(gettext("%*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(gettext("%*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(gettext("%*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(gettext("%*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;
|
|
|
|
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, "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);
|
|
}
|
|
/* Run the touch command, or simulate it */
|
|
if (!do_not_exec_rule) {
|
|
result = dosys(touch_cmd,
|
|
false,
|
|
false,
|
|
false,
|
|
false,
|
|
name);
|
|
} 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;
|
|
}
|
|
}
|
|
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(gettext("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(gettext("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 *) wcsrchr(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(gettext("%*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 *) wcsrchr(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, "/usr/include");
|
|
usr_include = GETNAME(usr_include_buf, FIND_LENGTH);
|
|
MBSTOWCS(usr_include_sys_buf, "/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 *) wcschr(pattern, (int) percent_char);
|
|
/* Check for possible buffer under-read */
|
|
if ((length = wcb.length()-wcslen(percent+1)) <= 0) {
|
|
continue;
|
|
}
|
|
if (!wcb.equaln(pattern, percent-pattern) ||
|
|
!IS_WEQUAL(wcb.get_string(length), 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;
|
|
|
|
if (target->dont_activate_cond_values) {
|
|
return;
|
|
}
|
|
|
|
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(gettext("%*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;
|
|
|
|
if (target->dont_activate_cond_values) {
|
|
return;
|
|
}
|
|
|
|
/* 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(gettext("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(gettext("%*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(gettext("%*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;
|
|
}
|
|
}
|
|
|
|
|
|
// 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;
|
|
}
|