/* * 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 (c) 2016 by Delphix. All rights reserved. * Copyright 2019 RackTop Systems. */ /* * doname.c * * Figure out which targets are out of date and rebuild them */ /* * Included files */ #include /* alloca() */ #include #include #include /* get_char_semantics_value() */ #include /* getvar(), expand_value() */ #include /* getmem() */ #include #include #include #include #include #include #include #include /* uname() */ #include #include /* close() */ /* * 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(Name target, Boolean do_get, Boolean implicit, Boolean automatic); extern Doname doname(Name target, Boolean do_get, Boolean implicit, 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(Property line, Boolean print_machine); extern Doname execute_serial(Property line); extern Name vpath_translation(Name cmd); extern void check_state(Name temp_file_name); static void read_dependency_file(Name filename); static void check_read_state_file(void); static void do_assign(Name line, Name target); static void build_command_strings(Name target, Property line); static Doname touch_command(Property line, Name target, Doname result); extern void update_target(Property line, Doname result); static Doname sccs_get(Name target, Property *command); extern void read_directory_of_file(Name file); static void add_pattern_conditionals(Name target); extern void set_locals(Name target, Property old_locals); extern void reset_locals(Name target, Property old_locals, Property conditional, 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(wchar_t *name_string, 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(Name target, Boolean do_get, Boolean implicit, Boolean automatic) { int first_time = 1; Doname rv = build_failed; (void) fflush(stdout); try_again: switch (doname(target, do_get, implicit, automatic)) { case build_ok: second_pass = 0; rv = build_ok; break; case build_running: second_pass = 0; rv = build_running; break; 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; rv = build_failed; break; 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; rv = build_dont_know; break; } 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; rv = build_failed; break; } fatal(gettext("Don't know how to make target `%s'"), target->string_mb); break; } return (rv); } 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(Name target, Boolean do_get, Boolean implicit, Boolean automatic) { Doname result = build_dont_know; Chain out_of_date_list = NULL; Chain target_group; Property old_locals = NULL; Property line; Property command = NULL; Dependency dependency; Name less = NULL; Name true_target = target; Name *automatics = NULL; 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; 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; wchar_t *target_end; Dependency suffix; 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]; wchar_t *p; Property line; Dependency dependency; Dependency *remove; String_rec string; wchar_t buffer[MAXPATHLEN]; Boolean set_at = false; wchar_t *start; Dependency new_depe; 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(Property line, Boolean) { Doname result = build_ok; Boolean remember_only = false; 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(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(Name filename) { 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; Makefile_type save_makefile_type; 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(Name line, Name target) { Wstring wcb(line); wchar_t *string = wcb.get_string(); wchar_t *equal; Name name; 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, Property line) { String_rec command_line; Cmd_line command_template = line->body.line.command_template; Cmd_line *insert = &line->body.line.command_used; Cmd_line used = *insert; wchar_t buffer[STRING_BUFFER_LENGTH]; wchar_t *start; Name new_command_line; Boolean new_command_longer = false; 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(Property line, Name target, Doname result) { Name name; 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(Name target, Property *command) { int result; char link[MAXPATHLEN]; String_rec string; wchar_t name[MAXPATHLEN]; wchar_t *p; timestruc_t sccs_time; 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; /* FALLTHROUGH */ 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(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]; Name directory = dot; wchar_t *p = (wchar_t *) wcsrchr(wcb, (int) slash_char); 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(Name target) { 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(Name target, Property old_locals) { Property conditional; int i; 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(Name target, Property old_locals, Property conditional, int index) { 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(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; }