From c01222c0181f9bb4297459663d13444ebfe3ad97 Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Fri, 27 Nov 2020 11:37:29 -0500 Subject: [PATCH] [SV 35711] Check for special targets earlier GNU make must recognize some special targets as they are defined. Because of the way targets are defined, we were not recognizing these special targets until we were handling the NEXT statement. However that's too late for some special targets such as .POSIX etc. which can change the behavior of make during parsing. Check for special targets earlier, as soon as we've finished parsing the target introduction line (before we've even parsed the recipe). * NEWS: Mention the change. * src/read.c (check_specials): New function to look for special targets. Move checks from eval() and record_files() to this new function. (eval): Call check_specials() after we've completed parsing the target introduction line. Move default goal detection to check_specials(). (record_files): Move handling of .POSIX, .SECONDEXPANSION, and .ONESHELL to check_specials(). * tests/scripts/misc/bs-nl: Remove workaround for late .POSIX issue. * tests/scripts/targets/POSIX: Add a comment. --- NEWS | 3 + src/read.c | 199 +++++++++++++++++++----------------- tests/scripts/misc/bs-nl | 2 - tests/scripts/targets/POSIX | 2 + 4 files changed, 108 insertions(+), 98 deletions(-) diff --git a/NEWS b/NEWS index 95909582..bf6857f2 100644 --- a/NEWS +++ b/NEWS @@ -34,6 +34,9 @@ https://sv.gnu.org/bugs/index.php?group=make&report_id=111&fix_release_id=109&se https://www.gnu.org/software/gnulib/manual/html_node/C99-features-assumed.html The configure script should verify the compiler has these features. +* Special targets like .POSIX are detected upon definition, ensuring that any + change in behavior takes effect immediately, before the next line is parsed. + * GNU Make can now be built for MS-Windows using the Tiny C tcc compiler. diff --git a/src/read.c b/src/read.c index c55998c3..57903bc8 100644 --- a/src/read.c +++ b/src/read.c @@ -142,6 +142,7 @@ static void do_undefine (char *name, enum variable_origin origin, static struct variable *do_define (char *name, enum variable_origin origin, struct ebuffer *ebuf); static int conditional_line (char *line, size_t len, const floc *flocp); +static void check_specials (const struct nameseq *file, int set_default); static void record_files (struct nameseq *filenames, int are_also_makes, const char *pattern, const char *pattern_percent, char *depstr, @@ -1313,79 +1314,7 @@ eval (struct ebuffer *ebuf, int set_default) commands[commands_idx++] = '\n'; } - /* Determine if this target should be made default. We used to do - this in record_files() but because of the delayed target recording - and because preprocessor directives are legal in target's commands - it is too late. Consider this fragment for example: - - foo: - - ifeq ($(.DEFAULT_GOAL),foo) - ... - endif - - Because the target is not recorded until after ifeq directive is - evaluated the .DEFAULT_GOAL does not contain foo yet as one - would expect. Because of this we have to move the logic here. */ - - if (set_default && default_goal_var->value[0] == '\0') - { - struct dep *d; - struct nameseq *t = filenames; - - for (; t != 0; t = t->next) - { - int reject = 0; - const char *name = t->name; - - /* We have nothing to do if this is an implicit rule. */ - if (strchr (name, '%') != 0) - break; - - /* See if this target's name does not start with a '.', - unless it contains a slash. */ - if (*name == '.' && strchr (name, '/') == 0 -#ifdef HAVE_DOS_PATHS - && strchr (name, '\\') == 0 -#endif - ) - continue; - - - /* If this file is a suffix, don't let it be - the default goal file. */ - for (d = suffix_file->deps; d != 0; d = d->next) - { - struct dep *d2; - if (*dep_name (d) != '.' && streq (name, dep_name (d))) - { - reject = 1; - break; - } - for (d2 = suffix_file->deps; d2 != 0; d2 = d2->next) - { - size_t l = strlen (dep_name (d2)); - if (!strneq (name, dep_name (d2), l)) - continue; - if (streq (name + l, dep_name (d))) - { - reject = 1; - break; - } - } - - if (reject) - break; - } - - if (!reject) - { - define_variable_global (".DEFAULT_GOAL", 13, t->name, - o_file, 0, NILF); - break; - } - } - } + check_specials (filenames, set_default); } } @@ -1926,6 +1855,107 @@ record_target_var (struct nameseq *filenames, char *defn, } } + +/* Check for special targets. We used to do this in record_files() but that's + too late: by the time we get there we'll have already parsed the next line + and it have been mis-parsed because these special targets haven't been + considered yet. */ + +static void check_specials (const struct nameseq* files, int set_default) +{ + const struct nameseq *t = files; + + /* Unlikely but ... */ + if (posix_pedantic && second_expansion && one_shell + && (!set_default || default_goal_var->value[0] == '\0')) + return; + + for (; t != 0; t = t->next) + { + const char* nm = t->name; + + if (!posix_pedantic && streq (nm, ".POSIX")) + { + posix_pedantic = 1; + define_variable_cname (".SHELLFLAGS", "-ec", o_default, 0); + /* These default values are based on IEEE Std 1003.1-2008. + It requires '-O 1' for [CF]FLAGS, but GCC doesn't allow + space between -O and the number so omit it here. */ + define_variable_cname ("ARFLAGS", "-rv", o_default, 0); + define_variable_cname ("CC", "c99", o_default, 0); + define_variable_cname ("CFLAGS", "-O1", o_default, 0); + define_variable_cname ("FC", "fort77", o_default, 0); + define_variable_cname ("FFLAGS", "-O1", o_default, 0); + define_variable_cname ("SCCSGETFLAGS", "-s", o_default, 0); + continue; + } + + if (!second_expansion && streq (nm, ".SECONDEXPANSION")) + { + second_expansion = 1; + continue; + } + +#if !defined (__MSDOS__) && !defined (__EMX__) + if (!one_shell && streq (nm, ".ONESHELL")) + { + one_shell = 1; + continue; + } +#endif + + /* Determine if this target should be made default. */ + + if (set_default && default_goal_var->value[0] == '\0') + { + struct dep *d; + int reject = 0; + + /* We have nothing to do if this is an implicit rule. */ + if (strchr (nm, '%') != 0) + break; + + /* See if this target's name does not start with a '.', + unless it contains a slash. */ + if (*nm == '.' && strchr (nm, '/') == 0 +#ifdef HAVE_DOS_PATHS + && strchr (nm, '\\') == 0 +#endif + ) + continue; + + /* If this file is a suffix, it can't be the default goal file. */ + for (d = suffix_file->deps; d != 0; d = d->next) + { + struct dep *d2; + if (*dep_name (d) != '.' && streq (nm, dep_name (d))) + { + reject = 1; + break; + } + for (d2 = suffix_file->deps; d2 != 0; d2 = d2->next) + { + size_t l = strlen (dep_name (d2)); + if (!strneq (nm, dep_name (d2), l)) + continue; + if (streq (nm + l, dep_name (d))) + { + reject = 1; + break; + } + } + + if (reject) + break; + } + + if (!reject) + define_variable_global (".DEFAULT_GOAL", 13, t->name, + o_file, 0, NILF); + } + } +} + /* Record a description line for files FILENAMES, with dependencies DEPS, commands to execute described by COMMANDS and COMMANDS_IDX, coming from FILENAME:COMMANDS_STARTED. @@ -2067,29 +2097,6 @@ record_files (struct nameseq *filenames, int are_also_makes, free_ns (filenames); - /* Check for special targets. Do it here instead of, say, snap_deps() - so that we can immediately use the value. */ - if (!posix_pedantic && streq (name, ".POSIX")) - { - posix_pedantic = 1; - define_variable_cname (".SHELLFLAGS", "-ec", o_default, 0); - /* These default values are based on IEEE Std 1003.1-2008. - It requires '-O 1' for [CF]FLAGS, but GCC doesn't allow space - between -O and the number so omit it here. */ - define_variable_cname ("ARFLAGS", "-rv", o_default, 0); - define_variable_cname ("CC", "c99", o_default, 0); - define_variable_cname ("CFLAGS", "-O1", o_default, 0); - define_variable_cname ("FC", "fort77", o_default, 0); - define_variable_cname ("FFLAGS", "-O1", o_default, 0); - define_variable_cname ("SCCSGETFLAGS", "-s", o_default, 0); - } - else if (!second_expansion && streq (name, ".SECONDEXPANSION")) - second_expansion = 1; -#if !defined (__MSDOS__) && !defined (__EMX__) - else if (!one_shell && streq (name, ".ONESHELL")) - one_shell = 1; -#endif - /* If this is a static pattern rule: 'targets: target%pattern: prereq%pattern; recipe', make sure the pattern matches this target name. */ diff --git a/tests/scripts/misc/bs-nl b/tests/scripts/misc/bs-nl index fdf4aabc..e8ba0467 100644 --- a/tests/scripts/misc/bs-nl +++ b/tests/scripts/misc/bs-nl @@ -88,7 +88,6 @@ var:;@echo '|$(var)|'!, # POSIX: Preserve trailing space run_make_test(q! .POSIX: -x = y var = he \ llo var:;@echo '|$(var)|'!, @@ -97,7 +96,6 @@ var:;@echo '|$(var)|'!, # POSIX: One space per bs-nl run_make_test(q! .POSIX: -x = y var = he\ \ \ diff --git a/tests/scripts/targets/POSIX b/tests/scripts/targets/POSIX index d69b8bbd..c40b8dbe 100644 --- a/tests/scripts/targets/POSIX +++ b/tests/scripts/targets/POSIX @@ -20,6 +20,8 @@ all: ; \@$script '', "#MAKE#: *** [#MAKEFILE#:3: all] Error $err\n", 512); # User settings must override .POSIX +# In the standard .POSIX must be the first thing in the makefile +# but we relax that rule in GNU make. $flags = '-xc'; $out = `$sh_name $flags '$script' 2>&1`; run_make_test(qq!