diff options
author | fjoe <fjoe@FreeBSD.org> | 2007-03-08 09:16:11 +0000 |
---|---|---|
committer | fjoe <fjoe@FreeBSD.org> | 2007-03-08 09:16:11 +0000 |
commit | 19d858e266534f79d31f05bb4f1b0810dae9b0e4 (patch) | |
tree | 4df26b7f4601246e5adacdf07dd59f9d468d6c03 /usr.bin/make | |
parent | f6d48b2630c435828501c18fc4f28e9a6df9adfd (diff) | |
download | FreeBSD-src-19d858e266534f79d31f05bb4f1b0810dae9b0e4.zip FreeBSD-src-19d858e266534f79d31f05bb4f1b0810dae9b0e4.tar.gz |
Implement "Remaking Makefiles" feature:
After reading Makefile and all the files that are included using .include
or .sinclude directives (source Makefiles) make considers each source
Makefile as a target and tries to rebuild it. Both explicit and implicit
rules are checked and all source Makefiles are updated if necessary. If
any of the source Makefiles were rebuilt, make restarts from clean state.
To prevent infinite loops the following source Makefile targets are
ignored:
- :: targets that have no prerequisites but have commands
- ! targets
- targets that have .PHONY or .EXEC attributes
- targets without prerequisites and without commands
When remaking a source Makefile options -t (touch target), -q (query
mode), and -n (no exec) do not take effect, unless source Makefile is
specified explicitly as a target in make command line.
Additionally, system makefiles and .depend are not considered as a
Makefiles that can be rebuilt.
Reviewed by: harti
Diffstat (limited to 'usr.bin/make')
-rw-r--r-- | usr.bin/make/globals.h | 1 | ||||
-rw-r--r-- | usr.bin/make/job.c | 49 | ||||
-rw-r--r-- | usr.bin/make/job.h | 2 | ||||
-rw-r--r-- | usr.bin/make/main.c | 202 | ||||
-rw-r--r-- | usr.bin/make/make.1 | 45 | ||||
-rw-r--r-- | usr.bin/make/make.h | 1 | ||||
-rw-r--r-- | usr.bin/make/parse.c | 6 |
7 files changed, 279 insertions, 27 deletions
diff --git a/usr.bin/make/globals.h b/usr.bin/make/globals.h index 4982a9f..465c4f3 100644 --- a/usr.bin/make/globals.h +++ b/usr.bin/make/globals.h @@ -77,6 +77,7 @@ extern Boolean beSilent; /* True if should print no commands */ extern Boolean beVerbose; /* True if should print extra cruft */ extern Boolean noExecute; /* True if should execute nothing */ extern Boolean allPrecious; /* True if every target is precious */ +extern Boolean is_posix; /* .POSIX target seen */ /* True if should continue on unaffected portions of the graph * when have an error in one portion */ diff --git a/usr.bin/make/job.c b/usr.bin/make/job.c index 7bb31cd..6136c6f 100644 --- a/usr.bin/make/job.c +++ b/usr.bin/make/job.c @@ -3070,7 +3070,7 @@ Compat_RunCommand(char *cmd, GNode *gn) /*- *----------------------------------------------------------------------- - * CompatMake -- + * Compat_Make -- * Make a target, given the parent, to abort if necessary. * * Side Effects: @@ -3078,8 +3078,8 @@ Compat_RunCommand(char *cmd, GNode *gn) * *----------------------------------------------------------------------- */ -static int -CompatMake(GNode *gn, GNode *pgn) +int +Compat_Make(GNode *gn, GNode *pgn) { LstNode *ln; @@ -3099,7 +3099,7 @@ CompatMake(GNode *gn, GNode *pgn) gn->made = BEINGMADE; Suff_FindDeps(gn); LST_FOREACH(ln, &gn->children) - CompatMake(Lst_Datum(ln), gn); + Compat_Make(Lst_Datum(ln), gn); if (!gn->make) { gn->made = ABORTED; pgn->make = FALSE; @@ -3289,6 +3289,27 @@ CompatMake(GNode *gn, GNode *pgn) } /*- + * Install signal handlers for Compat_Run + */ +void +Compat_InstallSignalHandlers(void) +{ + + if (signal(SIGINT, SIG_IGN) != SIG_IGN) { + signal(SIGINT, CompatCatchSig); + } + if (signal(SIGTERM, SIG_IGN) != SIG_IGN) { + signal(SIGTERM, CompatCatchSig); + } + if (signal(SIGHUP, SIG_IGN) != SIG_IGN) { + signal(SIGHUP, CompatCatchSig); + } + if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) { + signal(SIGQUIT, CompatCatchSig); + } +} + +/*- *----------------------------------------------------------------------- * Compat_Run -- * Start making again, given a list of target nodes. @@ -3308,19 +3329,7 @@ Compat_Run(Lst *targs) int error_cnt; /* Number of targets not remade due to errors */ LstNode *ln; - if (signal(SIGINT, SIG_IGN) != SIG_IGN) { - signal(SIGINT, CompatCatchSig); - } - if (signal(SIGTERM, SIG_IGN) != SIG_IGN) { - signal(SIGTERM, CompatCatchSig); - } - if (signal(SIGHUP, SIG_IGN) != SIG_IGN) { - signal(SIGHUP, CompatCatchSig); - } - if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) { - signal(SIGQUIT, CompatCatchSig); - } - + Compat_InstallSignalHandlers(); ENDNode = Targ_FindNode(".END", TARG_CREATE); /* * If the user has defined a .BEGIN target, execute the commands @@ -3341,8 +3350,8 @@ Compat_Run(Lst *targs) } /* - * For each entry in the list of targets to create, call CompatMake on - * it to create the thing. CompatMake will leave the 'made' field of gn + * For each entry in the list of targets to create, call Compat_Make on + * it to create the thing. Compat_Make will leave the 'made' field of gn * in one of several states: * UPTODATE gn was already up-to-date * MADE gn was recreated successfully @@ -3353,7 +3362,7 @@ Compat_Run(Lst *targs) error_cnt = 0; while (!Lst_IsEmpty(targs)) { gn = Lst_DeQueue(targs); - CompatMake(gn, gn); + Compat_Make(gn, gn); if (gn->made == UPTODATE) { printf("`%s' is up to date.\n", gn->name); diff --git a/usr.bin/make/job.h b/usr.bin/make/job.h index 1ef4b66..a4c6620 100644 --- a/usr.bin/make/job.h +++ b/usr.bin/make/job.h @@ -72,6 +72,8 @@ void Proc_Init(void); struct Buffer *Cmd_Exec(const char *, const char **); +int Compat_Make(struct GNode *gn, struct GNode *pgn); +void Compat_InstallSignalHandlers(void); void Compat_Run(struct Lst *); #endif /* job_h_4678dfd1 */ diff --git a/usr.bin/make/main.c b/usr.bin/make/main.c index 1c8acdc..8eceedf 100644 --- a/usr.bin/make/main.c +++ b/usr.bin/make/main.c @@ -80,6 +80,7 @@ __FBSDID("$FreeBSD$"); #include "config.h" #include "dir.h" #include "globals.h" +#include "GNode.h" #include "job.h" #include "make.h" #include "parse.h" @@ -100,6 +101,9 @@ extern char **environ; /* XXX what header declares this variable? */ /* ordered list of makefiles to read */ static Lst makefiles = Lst_Initializer(makefiles); +/* ordered list of source makefiles */ +static Lst source_makefiles = Lst_Initializer(source_makefiles); + /* list of variables to print */ static Lst variables = Lst_Initializer(variables); @@ -108,6 +112,8 @@ static Boolean noBuiltins; /* -r flag */ static Boolean forceJobs; /* -j argument given */ static char *curdir; /* startup directory */ static char *objdir; /* where we chdir'ed to */ +static char **save_argv; /* saved argv */ +static char *save_makeflags;/* saved MAKEFLAGS */ /* (-E) vars to override from env */ Lst envFirstVars = Lst_Initializer(envFirstVars); @@ -116,6 +122,7 @@ Lst envFirstVars = Lst_Initializer(envFirstVars); Lst create = Lst_Initializer(create); Boolean allPrecious; /* .PRECIOUS given on line by itself */ +Boolean is_posix; /* .POSIX target seen */ Boolean beSilent; /* -s flag */ Boolean beVerbose; /* -v flag */ Boolean compatMake; /* -B argument */ @@ -316,6 +323,31 @@ found: } /** + * Open and parse the given makefile. + * If open is successful add it to the list of makefiles. + * + * Results: + * TRUE if ok. FALSE if couldn't open file. + */ +static Boolean +TryReadMakefile(const char p[]) +{ + char *data; + LstNode *last = Lst_Last(&source_makefiles); + + if (!ReadMakefile(p)) + return (FALSE); + + data = estrdup(p); + if (last == NULL) { + LstNode *first = Lst_First(&source_makefiles); + Lst_Insert(&source_makefiles, first, data); + } else + Lst_Append(&source_makefiles, last, estrdup(p)); + return (TRUE); +} + +/** * MainParseArgs * Parse a given argument vector. Called from main() and from * Main_ParseArgLine() when the .MAKEFLAGS target is used. @@ -502,7 +534,7 @@ rearg: if (Main_ParseWarn(optarg, 1) != -1) MFLAGS_append("-x", optarg); break; - + default: case '?': usage(); @@ -523,10 +555,12 @@ rearg: for (; *argv != NULL; ++argv, --argc) { if (Parse_IsVar(*argv)) { char *ptr = MAKEFLAGS_quote(*argv); + char *v = estrdup(*argv); Var_Append(".MAKEFLAGS", ptr, VAR_GLOBAL); - Parse_DoVar(*argv, VAR_CMD); + Parse_DoVar(v, VAR_CMD); free(ptr); + free(v); } else if ((*argv)[0] == '-') { if ((*argv)[1] == '\0') { @@ -640,6 +674,149 @@ check_make_level(void) } /** + * Main_AddSourceMakefile + * Add a file to the list of source makefiles + */ +void +Main_AddSourceMakefile(const char *name) +{ + + Lst_AtEnd(&source_makefiles, estrdup(name)); +} + +/** + * Remake_Makefiles + * Remake all the makefiles + */ +static void +Remake_Makefiles(void) +{ + LstNode *ln; + int error_cnt = 0; + int remade_cnt = 0; + + Compat_InstallSignalHandlers(); + + LST_FOREACH(ln, &source_makefiles) { + LstNode *ln2; + struct GNode *gn; + const char *name = Lst_Datum(ln); + Boolean saveTouchFlag = touchFlag; + Boolean saveQueryFlag = queryFlag; + Boolean saveNoExecute = noExecute; + + /* + * Create node + */ + gn = Targ_FindNode(name, TARG_CREATE); + DEBUGF(MAKE, ("Checking %s...", gn->name)); + Suff_FindDeps(gn); + + /* + * ! dependencies as well as + * dependencies with .FORCE, .EXEC and .PHONY attributes + * are skipped to prevent infinite loops + */ + if (gn->type & (OP_FORCE | OP_EXEC | OP_PHONY)) { + DEBUGF(MAKE, ("skipping (force, exec or phony).\n", + gn->name)); + continue; + } + + /* + * Skip :: targets that have commands and no children + * because such targets are always out-of-date + */ + if ((gn->type & OP_DOUBLEDEP) && + !Lst_IsEmpty(&gn->commands) && + Lst_IsEmpty(&gn->children)) { + DEBUGF(MAKE, ("skipping (doubledep, no sources " + "and has commands).\n")); + continue; + } + + /* + * Skip targets without sources and without commands + */ + if (Lst_IsEmpty(&gn->commands) && + Lst_IsEmpty(&gn->children)) { + DEBUGF(MAKE, + ("skipping (no sources and no commands).\n")); + continue; + } + + DEBUGF(MAKE, ("\n")); + + /* + * -t, -q and -n has no effect unless the makefile is + * specified as one of the targets explicitly in the + * command line + */ + LST_FOREACH(ln2, &create) { + if (!strcmp(gn->name, Lst_Datum(ln2))) { + /* found as a target */ + break; + } + } + if (ln2 == NULL) { + touchFlag = FALSE; + queryFlag = FALSE; + noExecute = FALSE; + } + + /* + * Check and remake the makefile + */ + Compat_Make(gn, gn); + + /* + * Restore -t, -q and -n behaviour + */ + touchFlag = saveTouchFlag; + queryFlag = saveQueryFlag; + noExecute = saveNoExecute; + + /* + * Compat_Make will leave the 'made' field of gn + * in one of the following states: + * UPTODATE gn was already up-to-date + * MADE gn was recreated successfully + * ERROR An error occurred while gn was being created + * ABORTED gn was not remade because one of its inferiors + * could not be made due to errors. + */ + if (gn->made == MADE) + remade_cnt++; + else if (gn->made == ERROR) + error_cnt++; + else if (gn->made == ABORTED) { + printf("`%s' not remade because of errors.\n", + gn->name); + error_cnt++; + } + } + + if (error_cnt > 0) + Fatal("Failed to remake Makefiles."); + if (remade_cnt > 0) { + DEBUGF(MAKE, ("Restarting `%s'.\n", save_argv[0])); + + /* + * Some of makefiles were remade -- restart from clean state + */ + if (save_makeflags != NULL) + setenv("MAKEFLAGS", save_makeflags, 1); + else + unsetenv("MAKEFLAGS"); + chdir(curdir); + if (execvp(save_argv[0], save_argv) < 0) { + Fatal("Can't restart `%s': %s.", + save_argv[0], strerror(errno)); + } + } +} + +/** * main * The main function, for obvious reasons. Initializes variables * and a few modules, then parses the arguments give it in the @@ -671,6 +848,11 @@ main(int argc, char **argv) char cdpath[MAXPATHLEN]; char *cp = NULL, *start; + save_argv = argv; + save_makeflags = getenv("MAKEFLAGS"); + if (save_makeflags != NULL) + save_makeflags = estrdup(save_makeflags); + /* * Initialize file global variables. */ @@ -958,14 +1140,14 @@ main(int argc, char **argv) LstNode *ln; LST_FOREACH(ln, &makefiles) { - if (!ReadMakefile(Lst_Datum(ln))) + if (!TryReadMakefile(Lst_Datum(ln))) break; } if (ln != NULL) Fatal("make: cannot open %s.", (char *)Lst_Datum(ln)); - } else if (!ReadMakefile("BSDmakefile")) - if (!ReadMakefile("makefile")) - ReadMakefile("Makefile"); + } else if (!TryReadMakefile("BSDmakefile")) + if (!TryReadMakefile("makefile")) + TryReadMakefile("Makefile"); ReadMakefile(".depend"); @@ -1034,6 +1216,13 @@ main(int argc, char **argv) */ Lst targs = Lst_Initializer(targs); + if (!is_posix) { + /* + * Check if any of the makefiles are out-of-date. + */ + Remake_Makefiles(); + } + if (Lst_IsEmpty(&create)) Parse_MainName(&targs); else @@ -1072,6 +1261,7 @@ main(int argc, char **argv) Lst_Destroy(&variables, free); Lst_Destroy(&makefiles, free); + Lst_Destroy(&source_makefiles, free); Lst_Destroy(&create, free); /* print the graph now it's been processed if the user requested it */ diff --git a/usr.bin/make/make.1 b/usr.bin/make/make.1 index e77ec19..393e4f5 100644 --- a/usr.bin/make/make.1 +++ b/usr.bin/make/make.1 @@ -1465,6 +1465,51 @@ Several flags can be specified on a single .Ic .WARN target by seperating them with blanks. .El +.Sh REMAKING MAKEFILES +After reading Makefile and all the files that are included using +.Ic .include +or +.Ic .sinclude +directives (source Makefiles) +.Nm +considers each source Makefile as a target and tries to rebuild it. +Both explicit and implicit rules are checked and all source Makefiles +are updated if necessary. If any of the source Makefiles were rebuilt, +.Nm +restarts from clean state. +.Pp +To prevent infinite loops the following source Makefile targets are ignored: +.Bl -bullet +.It +.Ic :: +targets that have no prerequisites but have commands +.It +.Ic ! +targets +.It +targets that have +.Ic .PHONY +or +.Ic .EXEC +attributes +.It +targets without prerequisites and without commands +.El +.Pp +When remaking a source Makefile options +.Ic -t +(touch target), +.Ic -q +(query mode), and +.Ic -n +(no exec) do not take effect, unless source Makefile is specified +explicitly as a target in +.Nm +command line. +.Pp +Additionally, system makefiles and +.Ic .depend +are not considered as a Makefiles that can be rebuilt. .Sh ENVIRONMENT The .Nm diff --git a/usr.bin/make/make.h b/usr.bin/make/make.h index e8d5660..2386cd7 100644 --- a/usr.bin/make/make.h +++ b/usr.bin/make/make.h @@ -68,5 +68,6 @@ void Make_DoAllVar(struct GNode *); Boolean Make_Run(struct Lst *); void Main_ParseArgLine(char *, int); int Main_ParseWarn(const char *, int); +void Main_AddSourceMakefile(const char *); #endif /* make_h_a91074b9 */ diff --git a/usr.bin/make/parse.c b/usr.bin/make/parse.c index c9c1dbf..f125b50 100644 --- a/usr.bin/make/parse.c +++ b/usr.bin/make/parse.c @@ -1070,6 +1070,7 @@ ParseDoDependency(char *line) Path_Clear(Lst_Datum(ln)); break; case Posix: + is_posix = TRUE; Var_Set("%POSIX", "1003.2", VAR_GLOBAL); break; default: @@ -2059,7 +2060,7 @@ ParseFinishLine(void) } /** - * parse_include + * xparse_include * Parse an .include directive and push the file onto the input stack. * The input is the line minus the .include. A file spec is a string * enclosed in <> or "". The former is looked for only in sysIncPath. @@ -2184,9 +2185,12 @@ xparse_include(char *file, int sinclude) *cp = endc; if (!sinclude) Parse_Error(PARSE_FATAL, "Could not find %s", file); + else + Main_AddSourceMakefile(file); free(file); return; } + Main_AddSourceMakefile(fullname); free(file); /* |