Files
seta75D 2e8a93c394 Init
2021-10-11 18:20:23 -03:00

2399 lines
66 KiB
C

#ident "@(#)doname.c 1.1 92/07/30 Copyright 1986,1987,1988 Sun Micro"
/*
* doname.c
*
* Figure out which targets are out of date and rebuild them
*/
/*
* Included files
*/
#include "defs.h"
#include <sys/file.h>
/*
* Defined macros
*/
/*
* typedefs & structs
*/
/*
* Static variables
*/
/*
* File table of contents
*/
extern Doname doname_check();
extern Doname doname();
extern Boolean check_dependencies();
extern void dynamic_dependencies();
extern Doname run_command();
extern Doname execute_serial();
extern Name vpath_translation();
extern void check_state();
extern void read_dependency_file();
extern void check_read_state_file();
extern void do_assign();
extern void build_command_strings();
extern Doname touch_command();
extern void update_target();
extern Doname sccs_get();
extern void read_directory_of_file();
extern void add_pattern_conditionals();
extern void set_locals();
extern void reset_locals();
extern Boolean check_auto_dependencies();
/*
* 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(target, do_get, implicit, automatic)
register Name target;
register Boolean do_get;
register Boolean implicit;
register Boolean automatic;
{
(void) fflush(stdout);
switch (doname(target, do_get, implicit)) {
case build_ok:
return build_ok;
case build_running:
return build_running;
case build_failed:
if (!continue_after_error) {
fatal("Target `%s' not remade because of errors",
target->string);
}
build_failed_seen = true;
return build_failed;
case build_dont_know:
/* If we cant 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_only) {
return build_dont_know;
}
if (continue_after_error) {
warning("Don't know how to make target `%s'",
target->string);
build_failed_seen = true;
return build_failed;
}
fatal("Don't know how to make target `%s'", target->string);
break;
}
#ifdef lint
return build_failed;
#endif
}
/*
* 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(target, do_get, implicit)
register Name target;
register Boolean do_get;
register Boolean implicit;
{
Doname result = build_dont_know;
Chain out_of_date_list = NULL;
Chain target_group;
Property old_locals;
register Property line;
Property command = NULL;
register Dependency dependency;
Name less = NULL;
Name true_target = target;
Name *automatics;
register int auto_count;
Boolean rechecking_target = false;
Boolean saved_commands_done;
Boolean restart = false;
Boolean save_parallel = parallel;
Boolean doing_subtree = false;
if (target->state == build_running) {
return build_running;
}
line = get_prop(target->prop, line_prop);
#ifdef PARALLEL
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;
}
}
}
#endif PARALLEL
/* 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 dont need to do it again */
/* Unless it depends on conditional macros or a delayed assignment */
/* has been done when KEEP_STATE is on */
if ((target->state == build_ok) &&
(!keep_state ||
(!target->depends_on_conditional &&
!assign_done))) {
return build_ok;
}
if (target->state == build_subtree) {
/* A dynamic macro subtree is being built (in parallel) */
target->state = build_dont_know;
doing_subtree = true;
if (!target->checking_subtree) {
/*
* This target has been started before and therefore
* not all dependencies have to be built.
*/
restart = true;
}
} else if (target->state == build_pending) {
target->state = build_dont_know;
restart = true;
#ifdef PARALLEL
} else if (parallel &&
keep_state &&
(target->conditional_cnt > 0)) {
add_subtree(target, recursion_level, do_get, implicit);
target->state = build_running;
return build_running;
#endif PARALLEL
}
/* 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) {
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) {
*p++ = dependency->name;
}
}
}
if (debug_level > 1) {
(void) printf("%*sdoname(%s)\n",
recursion_level,
"",
true_target->string);
}
recursion_level++;
/* avoid infinite loops */
if (target->state == build_in_progress) {
warning("Infinite loop: Target `%s' depends on itself",
target->string);
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);
}
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)) {
return build_running;
}
line->body.line.query = out_of_date_list;
}
if (doing_subtree) {
parallel = false;
}
/* Do not try to find rule for target if target is :: type */
/* All actions will be taken by separate branches */
/* Else we try to find an implicit rule using various methods */
/* We quit doing that as soon as one is found */
if (target->colon_splits == 0) {
/* Look for percent matched rule */
if ((result == build_dont_know) && (command == NULL)) {
switch (find_percent_rule(target, &command)) {
case build_failed:
result = build_failed;
break;
#ifdef PARALLEL
case build_running:
target->state = build_running;
add_pending(target,
--recursion_level,
do_get,
implicit,
false);
if (target->conditional_cnt > 0) {
reset_locals(target,
old_locals,
get_prop(target->prop,
conditional_prop),
0);
}
return build_running;
#endif PARALLEL
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)) {
case build_failed:
result = build_failed;
break;
#ifdef PARALLEL
case build_running:
target->state = build_running;
add_pending(target,
--recursion_level,
do_get,
implicit,
false);
if (target->conditional_cnt > 0) {
reset_locals(target,
old_locals,
get_prop(target->prop,
conditional_prop),
0);
}
return build_running;
#endif PARALLEL
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)) {
case build_failed:
result = build_failed;
break;
#ifdef PARALLEL
case build_running:
target->state = build_running;
add_pending(target,
--recursion_level,
do_get,
implicit,
false);
if (target->conditional_cnt > 0) {
reset_locals(target,
old_locals,
get_prop(target->
prop,
conditional_prop),
0);
}
return build_running;
#endif PARALLEL
}
}
}
/* Look for single suffix rule */
if ((result == build_dont_know) && !implicit &&
((line == NULL) ||
((line->body.line.target != NULL) &&
!line->body.line.target->has_regular_dependency))) {
switch (find_suffix_rule(target,
target,
empty_name,
&command)) {
case build_failed:
result = build_failed;
break;
#ifdef PARALLEL
case build_running:
target->state = build_running;
add_pending(target,
--recursion_level,
do_get,
implicit,
false);
if (target->conditional_cnt > 0) {
reset_locals(target,
old_locals,
get_prop(target->prop,
conditional_prop),
0);
}
return build_running;
#endif PARALLEL
}
}
/* 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;
if (OUT_OF_DATE(true_target->stat.time,
line->body.line.dependency_time)) {
line->body.line.is_out_of_date = true;
if (debug_level > 0) {
(void) printf("%*sBuilding %s using .DEFAULT because it is out of date\n",
recursion_level,
"",
true_target->string);
}
}
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;
}
/* Run commands if any. */
if ((command != NULL) &&
(command->body.line.command_template != NULL)) {
if (!report_dependencies_only) {
if (result != build_failed) {
result = run_command(command);
}
switch (result) {
#ifdef PARALLEL
case build_running:
add_running(target,
true_target,
--recursion_level,
auto_count,
automatics,
do_get,
implicit);
target->state = build_running;
if ((line = get_prop(target->prop,
line_prop)) != NULL) {
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) {
line->body.line.query = NULL;
}
if (target->conditional_cnt > 0) {
reset_locals(target,
old_locals,
get_prop(target->prop,
conditional_prop),
0);
}
return build_running;
#endif PARALLEL
case build_ok:
/* If all went OK set a nice timestamp */
if (true_target->stat.time ==
(int) file_doesnt_exist) {
true_target->stat.time =
(int) file_max_time;
}
break;
}
} else {
result = build_ok;
}
} else {
/* If no command was found for the target and it doesnt */
/* 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 == (int) file_doesnt_exist){
true_target->stat.time = (int) file_max_time;
}
result = build_ok;
}
/* If the file exists it is OK that we couldnt figure */
/* out how to build it */
(void) exists(target);
if ((target->stat.time != (int) 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) {
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 != (int) file_max_time) &&
((member = get_prop(target->prop, member_prop)) != NULL) &&
(exists(member->body.member.member) >
(int) 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("%*sTarget `%s' acquired new dependencies from build, rechecking all dependencies\n",
recursion_level,
"",
true_target->string);
}
rechecking_target = true;
saved_commands_done = commands_done;
goto recheck_target;
}
if (rechecking_target && !commands_done) {
commands_done = saved_commands_done;
}
return result;
}
/*
* 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(result, line, do_get,
target, true_target, doing_subtree, out_of_date_tail,
old_locals, implicit, command, less, rechecking_target)
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 dependencies_running;
register Dependency dependency;
Doname dep_result;
Boolean dependency_changed = false;
line->body.line.dependency_time = (int) file_doesnt_exist;
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 them */
for (dependency = line->body.line.dependencies;
dependency != NULL;
dependency = dependency->next) {
Boolean this_dependency_changed = false;
if (!dependency->automatic && 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 {
time_t depe_time = 0;
if (true_target->is_member) {
depe_time = exists(dependency->name);
}
if (dependency->built ||
(dependency->name->state == build_failed)) {
dep_result = dependency->name->state;
} else {
dep_result = doname_check(dependency->name,
do_get,
false,
dependency->
automatic);
}
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 cant 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 makeing of the target will fail */
/* This will only happen for automatic (hidden) dependencies */
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("Target %s rebuilt because dependency %s does not exist\n",
true_target->string,
dependency->name->string);
}
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);
if ((dependency->name->stat.time >
line->body.line.dependency_time) &&
(debug_level > 1)) {
(void) printf("%*sDate(%s)=%s Date-dependencies(%s)=%s\n",
recursion_level,
"",
dependency->name->string,
time_to_string(dependency->name->
stat.time),
true_target->string,
time_to_string(line->body.line.
dependency_time));
}
/* Collect the timestamp of the youngest dependency */
line->body.line.dependency_time =
MAX(dependency->name->stat.time,
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 - 1;
} else {
true_target->stat.time =
(time_t) file_no_time;
(void) exists(true_target);
}
} else {
(void) exists(true_target);
}
if ((true_target->stat.time <=
dependency->name->stat.time) &&
(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 ==
(time_t) file_max_time) {
(void) printf("%*sBuilding %s because %s does not exist\n",
recursion_level,
"",
true_target->string,
dependency->name->string);
} else {
(void) printf("%*sBuilding %s because it is out of date relative to %s\n",
recursion_level,
"",
true_target->string,
dependency->name->string);
}
}
}
if (dependency->name == force) {
force->stat.time =
(int) file_max_time;
force->state = build_dont_know;
}
}
}
#ifdef PARALLEL
if (dependencies_running) {
if (doing_subtree) {
if (target->conditional_cnt > 0) {
reset_locals(target,
old_locals,
get_prop(target->prop,
conditional_prop),
0);
}
return true;
} else {
target->state = build_running;
add_pending(target,
--recursion_level,
do_get,
implicit,
false);
if (target->conditional_cnt > 0) {
reset_locals(target,
old_locals,
get_prop(target->prop,
conditional_prop),
0);
}
return true;
}
}
#endif PARALLEL
if ((true_target->is_member) && (dependency_changed == true)) {
true_target->stat.time = (time_t) 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("Too many rules defined for target %s",
target->string);
}
*command = line;
/* Check if the target is out of date */
if (OUT_OF_DATE(true_target->stat.time,
line->body.line.dependency_time)){
line->body.line.is_out_of_date = true;
}
line->body.line.sccs_command = false;
line->body.line.target = true_target;
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
*/
static void
dynamic_dependencies(target)
Name target;
{
char pattern[MAXPATHLEN];
register char *p;
Property line;
register Dependency dependency;
register Dependency *remove;
String_rec string;
char buffer[MAXPATHLEN];
register Boolean set_at = false;
register char *start;
Dependency new_depe;
register Boolean reuse_cell;
Dependency first_member;
Name directory;
Name lib;
Name member;
Property prop;
Name true_target = target;
char *library;
target->has_depe_list_expanded = true;
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;
}
/* 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 (isspace(*start)) {
start++;
}
/* Remove the cell (later) if the macro was empty */
if (start[0] == (int) nul_char) {
dependency->name = NULL;
}
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 (isspace(*start)) {
start++;
}
break;
}
}
do {
/* First skip whitespace */
for (p = start; *p != (int) nul_char; p++) {
if ((*p == (int) nul_char) ||
isspace(*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 */
dependency->name = GETNAME(start, p - start);
if ((debug_level > 0) &&
(first_member == NULL)) {
(void) printf("%*sDynamic dependency `%s' for target `%s'\n",
recursion_level,
"",
dependency->name->string,
true_target->string);
}
for (start = p; isspace(*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_string(lib->string,
&string,
(int) lib->hash.length);
append_char((int) parenleft_char, &string);
append_string(first_member->name->string,
&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("%*sDynamic dependency `%s' for target `%s'\n",
recursion_level,
"",
first_member->name->
string,
true_target->string);
}
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;
}
}
}
}
/* 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) {
if ((start = strchr(dependency->name->string,
(int) parenleft_char)) != NULL) {
/* lib(*) type pattern */
library = buffer;
(void) strncpy(buffer,
dependency->name->string,
start-dependency->name->string);
buffer[start-dependency->name->string] =
(int) nul_char;
(void) strncpy(pattern,
start+1,
(int) (dependency->name->
hash.length -
(start-dependency->name->
string)-2));
pattern[dependency->name->hash.length -
(start-dependency->name->string) - 2] =
(int) nul_char;
} else {
library = NULL;
(void) strncpy(pattern,
dependency->name->string,
(int) dependency->name->
hash.length);
pattern[dependency->name->hash.length] =
(int) nul_char;
}
start = strrchr(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);
}
/*
* 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(line)
register Property line;
{
register Doname result = build_ok;
register Boolean remember_only = false;
register Name target = line->body.line.target;
char string[MAXPATHLEN];
/* 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) {
build_command_strings(target, line);
}
/* Never mind */
if (!line->body.line.is_out_of_date) {
return build_ok;
}
/* If quest is on we just need to know that something is out of date */
if (quest) {
exit(1);
}
/* We actually had to do something this time */
rewrite_statefile = commands_done = true;
/* If this is a sccs command we have do do some extra checking and */
/* possibly complain. If the file cannt be gotten because it is checked out */
/* we complain and then behave as if the command was executed even when we */
/* ignore the command */
if (!touch &&
line->body.line.sccs_command &&
(target->stat.time != (int) file_doesnt_exist) &&
((target->stat.mode & 0222) != 0)) {
fatal("%s is writable so it cannot be sccs gotten",
target->string);
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);
(void) sprintf(string,
"%s/.make.dependency.%d.%d %s",
temp_file_directory,
getpid(),
file_number++,
target->string);
(void) SETVAR(sunpro_dependencies,
GETNAME(string, FIND_LENGTH),
false);
*strchr(string, (int) space_char) = (int) nul_char;
temp_file_name = GETNAME(string, FIND_LENGTH);
temp_file_name->stat.is_file = true;
} 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 this is an sccs get command we need to ignore we skip all the */
/* command execution stuff */
if (remember_only) {
/* Empty block!!! */
} else if (touch) {
result = touch_command(line, target, result);
} else {
/* If this is not a touch run we need to execute the */
/* proper command for the target */
#ifdef PARALLEL
if (parallel) {
if (!parallel_ok(target)) {
/*
* 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_serial(line);
} else {
current_target = NULL;
current_line = NULL;
line->body.line.command_used = NULL;
return build_serial;
}
} else {
result = execute_parallel(line);
switch (result) {
case build_running:
return build_running;
case build_serial:
if (parallel_process_cnt == 0) {
result = execute_serial(line);
} else {
current_target = NULL;
current_line = NULL;
target->parallel = false;
line->body.line.command_used =
NULL;
return build_serial;
}
}
}
} else {
#endif PARALLEL
result = execute_serial(line);
#ifdef PARALLEL
}
#endif PARALLEL
}
temp_file_name = NULL;
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(line)
Property line;
{
Cmd_line rule;
Name target = line->body.line.target;
Doname result = build_ok;
target->has_recursive_dependency = false;
for (rule = line->body.line.command_used;
rule != NULL;
rule = rule->next) {
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)) {
(void) printf("%s\n", rule->command_line->string);
}
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_only) {
/* Execute command line */
setvar_envvar();
result = dosys(rule->command_line,
rule->ignore_error,
rule->make_refd,
BOOLEAN(rule->silent &&
rule->ignore_error),
target);
check_state(result, temp_file_name);
}
} else {
result = build_ok;
}
if (result == build_failed) {
if (silent) {
(void) printf("The following command caused the error:\n%s\n",
rule->command_line->string);
}
if (!rule->ignore_error) {
if (!continue_after_error) {
fatal("Command failed for target `%s'",
target->string);
}
/* Make sure a failing command is not saved */
/* in .make.state */
line->body.line.command_used = NULL;
break;
} else {
result = build_ok;
}
}
}
temp_file_name = 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(cmd)
register Name cmd;
{
char buffer[STRING_BUFFER_LENGTH];
String_rec new_cmd;
char *p;
char *start;
if ((cmd == NULL) || (cmd->hash.length == 0)) {
return cmd;
}
INIT_STRING_FROM_STACK(new_cmd, buffer);
p = cmd->string;
while (*p != (int) nul_char) {
while (isspace(*p) && (*p != (int) nul_char)) {
append_char(*p++, &new_cmd);
}
start = p;
while (!isspace(*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_string(cmd->string,
&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(result, temp_file_name)
*
* Reads and checks the state changed by the previously executed command.
*
* Parameters:
* result Result of the build
* temp_file_name The auto dependency temp file
*
* Global variables used:
*/
void
check_state(result, temp_file_name)
Doname result;
Name temp_file_name;
{
if (result == build_ok) {
/* 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);
}
}
/*
* 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(filename)
register Name filename;
{
register Makefile_type save_makefile_type;
if (filename == NULL) {
return;
}
filename->stat.time = (int) file_no_time;
if (exists(filename) > (int) 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()
{
register time_t previous = make_state->stat.time;
register Makefile_type save_makefile_type;
register Property makefile;
make_state->stat.time = (int) file_no_time;
if ((exists(make_state) == (int) 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;
}
(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(line, target)
register Name line;
register Name target;
{
register char *equal;
register char *string = line->string;
register Boolean append = false;
register Name name;
/* 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 (isspace(*string)) {
string++;
}
equal = string;
/* Find "+=" or "=" */
while (!isspace(*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("= expected in rule `%s' for target `%s'",
line->string,
target->string);
case plus_char:
append = true;
equal++;
break;
}
equal++;
/* Skip over whitespace in front of value */
while (isspace(*equal)) {
equal++;
}
/* Enter new macro value */
enter_equal(name,
GETNAME(equal, line->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(target, line)
Name target;
register Property line;
{
String_rec command_line;
register Cmd_line template = line->body.line.command_template;
register Cmd_line *insert = &line->body.line.command_used;
register Cmd_line used = *insert;
char buffer[STRING_BUFFER_LENGTH];
char *start;
Name new;
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;
if (less_name == NULL) {
less_name = GETNAME("<", FIND_LENGTH);
percent_name = GETNAME("%", FIND_LENGTH);
star = GETNAME("*", 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 */
(void) SETVAR(c_at, target, false);
(void) SETVAR(star, line->body.line.star, false);
(void) SETVAR(less_name, line->body.line.less, false);
(void) SETVAR(percent_name, line->body.line.percent, false);
/* $? is seldom used and it is expensive to build */
/* so we store the list form and build the string on demand */
(void) setvar_daemon(query,
(Name) line->body.line.query,
false,
chain_daemon);
/* 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 (;
template != NULL;
template = 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;
/* Expand the macros in the command line */
INIT_STRING_FROM_STACK(command_line, buffer);
make_word_mentioned =
query_mentioned =
false;
expand_value(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 (;
isspace(*start) ||
(char_semantics[*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;
}
}
/* 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 = GETNAME(start, FIND_LENGTH);
/* Compare it with the old one for command consistency */
if (used->command_line != new) {
if (keep_state &&
!used->ignore_command_dependency) {
if (debug_level > 0) {
if (used->command_line != NULL
&& *used->command_line->string !=
(int) nul_char) {
(void) printf("%*sBuilding %s because new command \n\t%s\n%*sdifferent from old\n\t%s\n",
recursion_level,
"",
target->string,
new->string,
recursion_level,
"",
used->
command_line->
string);
} else {
(void) printf("%*sBuilding %s because new command \n\t%s\n%*sdifferent from empty old command\n",
recursion_level,
"",
target->string,
new->string,
recursion_level,
"");
}
}
command_changed =
line->body.line.is_out_of_date =
true;
}
used->command_line = new;
}
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("%*sBuilding %s because new command shorter than old\n",
recursion_level,
"",
target->string);
}
command_changed =
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("%*sBuilding %s because new command longer than old\n",
recursion_level,
"",
target->string);
}
command_changed = 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 (conditional_macro_used) {
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(line, target, result)
register Property line;
register Name target;
Doname result;
{
Name name;
register Chain target_group;
String_rec touch_string;
char buffer[MAXPATHLEN];
Name touch_cmd;
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);
append_string("touch ", &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_string(touch_cmd->string,
&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);
}
/* Run the touch command, or simulate it */
if (!do_not_exec_rule) {
result = dosys(touch_cmd,
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(line, result)
Property line;
Doname result;
{
Name target = line->body.line.target;
Chain target_group;
Property line2;
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[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 = (int) file_max_time;
} else {
/* If we really built the target we read the */
/* new timestamp */
target->stat.time = (int) file_no_time;
(void) exists(target);
}
/* 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(target, command)
register Name target;
register Property *command;
{
register time_t sccs_time;
register Property line;
/* 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 */
/* thing are OK */
if (target->stat.time > (int) 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 != (int) file_doesnt_exist) &&
(sccs_get_rule != NULL)) {
/* 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("%*sSccs getting %s because s. file is younger than source file\n",
recursion_level,
"",
target->string);
}
}
line->body.line.sccs_command = true;
line->body.line.command_template = sccs_get_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(file)
register Name file;
{
register Name directory = dot;
register char *p = strrchr(file->string, (int) slash_char);
register int length = p - file->string;
static Name usr_include;
static Name usr_include_sys;
if (usr_include == NULL) {
usr_include = GETNAME("/usr/include", FIND_LENGTH);
usr_include_sys = GETNAME("/usr/include/sys",
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_EQUALN(usr_include->string,
file->string,
length)) {
directory = usr_include;
} else
if ((usr_include_sys->hash.length == length) &&
IS_EQUALN(usr_include_sys->string,
file->string,
length)) {
directory = usr_include_sys;
} else {
directory = GETNAME(file->string, length);
}
}
(void) read_dir(directory,
(char *) NULL,
(Property) NULL,
(char *) 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(target)
register Name target;
{
register Property conditional;
Property new_prop;
Property *previous;
Name_rec dummy;
char *pattern;
char *percent;
int length;
length = strlen(target->string);
for (conditional = get_prop(conditionals->prop, conditional_prop);
conditional != NULL;
conditional = get_prop(conditional->next, conditional_prop)) {
pattern = conditional->body.conditional.target->string;
if (pattern[1] != 0) {
percent = strchr(pattern, (int) percent_char);
if (!IS_EQUALN(target->string,
pattern,
percent-pattern) ||
!IS_EQUAL(target->string+length-strlen(percent+1),
percent+1)) {
continue;
}
}
for (previous = &target->prop;
*previous != NULL;
previous = &(*previous)->next) {
if (((*previous)->type == conditional_prop) &&
((*previous)->body.conditional.sequence >
conditional->body.conditional.sequence)) {
break;
}
}
if (*previous == NULL) {
new_prop = append_prop(target, conditional_prop);
} else {
dummy.prop = NULL;
new_prop = append_prop(&dummy, conditional_prop);
new_prop->next = *previous;
*previous = new_prop;
}
target->conditional_cnt++;
new_prop->body.conditional = conditional->body.conditional;
}
}
/*
* set_locals(target, old_locals)
*
* Sets any conditional macros for the target.
* Each target carries a possibly empty set of conditional properties.
*
* Parameters:
* target The target to set conditional macros for
* old_locals Space to store old values in
*
* Global variables used:
* debug_level Should we trace activity?
* is_conditional We need to preserve this value
* recursion_level Used for tracing
*/
void
set_locals(target, old_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;
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("%*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,
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(target, old_locals, conditional, index)
register Name target;
register Property old_locals;
register Property conditional;
register int index;
{
register Property this_conditional;
Chain cond_chain;
/* 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("Internal error: reset target not at head of condtional_targets chain");
} else {
cond_chain = conditional_targets->next;
retmem((char *) 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("%*sdeactivating conditional value: %s= %s\n",
recursion_level,
"",
conditional->body.conditional.name->
string,
old_locals[index].body.macro.value->
string);
} else {
(void) printf("%*sdeactivating conditional value: %s =\n",
recursion_level,
"",
conditional->body.conditional.name->
string);
}
}
}
/*
* 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(target, auto_count, automatics)
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) {
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;
}
}