* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
* Copyright (c) 1988, 1989 by Adam de Boor
* Copyright (c) 1989 by Berkeley Softworks
* This code is derived from software contributed to Berkeley by
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
"@(#) Copyright (c) 1989 The Regents of the University of California.\n\
static char sccsid
[] = "@(#)main.c 5.8 (Berkeley) %G%";
* The main file for this entire program. Exit routines etc
* Utility functions defined in this file:
* Main_ParseArgLine Takes a line of arguments, breaks them and
* treats them as if they were given when first
* invoked. Used by the parse module to implement
* Error Print a tagged error message. The global
* MAKE variable must have been defined. This
* takes a format string and two optional
* Fatal Print an error message and exit. Also takes
* a format string and two arguments.
* Punt Aborts all jobs and exits with a message. Also
* takes a format string and two arguments.
* Finish Finish things up by printing the number of
* errors which occured, as passed to it, and
#define DEFMAXLOCAL DEFMAXJOBS
#define MAKEFLAGS ".MAKEFLAGS"
static char *progName
; /* Our invocation name */
Lst create
; /* Targets to be made */
time_t now
; /* Time at start of make */
GNode
*DEFAULT
; /* .DEFAULT node */
Boolean allPrecious
; /* .PRECIOUS given on line by itself */
static int printGraph
; /* -p flag */
static Boolean noBuiltins
; /* -r flag */
static Lst makefiles
; /* List of makefiles to read (in
int maxJobs
; /* -J argument */
static int maxLocal
; /* -L argument */
Boolean debug
; /* -d flag */
Boolean amMake
; /* -M flag */
Boolean noWarnings
; /* -W flag */
Boolean noExecute
; /* -n flag */
Boolean keepgoing
; /* -k flag */
Boolean queryFlag
; /* -q flag */
Boolean touchFlag
; /* -t flag */
Boolean usePipes
; /* !-P flag */
Boolean backwards
; /* -B flag */
Boolean ignoreErrors
; /* -i flag */
Boolean beSilent
; /* -s flag */
Boolean sysVmake
; /* -v flag */
Boolean oldVars
; /* variable substitution style */
Boolean checkEnvFirst
; /* -e flag */
static Boolean jobsRunning
; /* TRUE if the jobs might be running */
static Boolean
ReadMakefile();
* Initial value for optind when parsing args. Different getopts start it
"-B Be as backwards-compatible with make as possible without\n\
"-C Cancel any current indications of compatibility.",
"-D<var> Define the variable <var> with value 1.",
"-I<dir> Specify another directory in which to search for included\n\
"-J<num> Specify maximum overall concurrency.",
"-L<num> Specify maximum local concurrency.",
"-M Be Make as closely as possible.",
"-P Don't use pipes to catch the output of jobs, use files.",
"-S Turn off the -k flag (see below).",
"-W Don't print warning messages.",
"-d<flags> Turn on debugging output.",
"-e Give environment variables precedence over those in the\n\
"-f<file> Specify a(nother) makefile to read",
"-i Ignore errors from executed commands.",
"-k On error, continue working on targets that do not depend on\n\
the one for which an error was detected.",
"-n Don't execute commands, just print them.",
"-p<num> Tell when to print the input graph: 1 (before processing),\n\
2 (after processing), or 3 (both).",
"-q See if anything needs to be done. Exits 1 if so.",
"-r Do not read the system makefile for pre-defined rules.",
"-s Don't print commands as they are executed.",
"-t Update targets by \"touching\" them (see touch(1)).",
"-v Be compatible with System V make. Implies -B.",
*----------------------------------------------------------------------
* Parse a given argument vector. Called from main() and from
* Main_ParseArgLine() when the .MAKEFLAGS target is used.
* XXX: Deal with command line overriding .MAKEFLAGS in makefile
* Various global and local flags will be set depending on the flags
*----------------------------------------------------------------------
MainParseArgs (argc
, argv
)
int argc
; /* Number of arguments in argv */
char **argv
; /* The arguments themselves */
while((c
= getopt(argc
, argv
, "BCD:I:J:L:MPSWd:ef:iknp:qrstvh")) != -1) {
Var_Append(MAKEFLAGS
, "-B", VAR_GLOBAL
);
backwards
= sysVmake
= amMake
= FALSE
;
Var_Append(MAKEFLAGS
, "-C", VAR_GLOBAL
);
Var_Set(optarg
, "1", VAR_GLOBAL
);
Var_Append(MAKEFLAGS
, "-D", VAR_GLOBAL
);
Var_Append(MAKEFLAGS
, optarg
, VAR_GLOBAL
);
Parse_AddIncludeDir(optarg
);
Var_Append(MAKEFLAGS
, "-I", VAR_GLOBAL
);
Var_Append(MAKEFLAGS
, optarg
, VAR_GLOBAL
);
Var_Append(MAKEFLAGS
, "-J", VAR_GLOBAL
);
Var_Append(MAKEFLAGS
, optarg
, VAR_GLOBAL
);
Var_Append(MAKEFLAGS
, "-L", VAR_GLOBAL
);
Var_Append(MAKEFLAGS
, optarg
, VAR_GLOBAL
);
Var_Append(MAKEFLAGS
, "-M", VAR_GLOBAL
);
Var_Append(MAKEFLAGS
, "-P", VAR_GLOBAL
);
Var_Append(MAKEFLAGS
, "-S", VAR_GLOBAL
);
Var_Append(MAKEFLAGS
, "-W", VAR_GLOBAL
);
Var_Append(MAKEFLAGS
, "-d", VAR_GLOBAL
);
Var_Append(MAKEFLAGS
, optarg
, VAR_GLOBAL
);
Var_Append(MAKEFLAGS
, "-e", VAR_GLOBAL
);
(void)Lst_AtEnd(makefiles
, (ClientData
)optarg
);
Var_Append(MAKEFLAGS
, "-i", VAR_GLOBAL
);
Var_Append(MAKEFLAGS
, "-k", VAR_GLOBAL
);
Var_Append(MAKEFLAGS
, "-n", VAR_GLOBAL
);
printGraph
= atoi(optarg
);
Var_Append(MAKEFLAGS
, "-q", VAR_GLOBAL
); /* Kind of
Var_Append(MAKEFLAGS
, "-r", VAR_GLOBAL
);
Var_Append(MAKEFLAGS
, "-s", VAR_GLOBAL
);
Var_Append(MAKEFLAGS
, "-t", VAR_GLOBAL
);
sysVmake
= backwards
= TRUE
;
Var_Append(MAKEFLAGS
, "-v", VAR_GLOBAL
);
for (i
= 0; i
< sizeof(help
)/sizeof(help
[0]); i
++) {
* Take care of encompassing compatibility levels...
* See if the rest of the arguments are variable assignments and perform
* them if so. Else take them to be targets and stuff them on the end
for (i
= optind
; i
< argc
; i
++) {
if (Parse_IsVar (argv
[i
])) {
Parse_DoVar(argv
[i
], VAR_CMD
);
Punt("Bogus argument in MainParseArgs");
(void)Lst_AtEnd (create
, (ClientData
)argv
[i
]);
*----------------------------------------------------------------------
* Used by the parse module when a .MFLAGS or .MAKEFLAGS target
* is encountered and by main() when reading the .MAKEFLAGS envariable.
* Takes a line of arguments and breaks it into its
* component words and passes those words and the number of them to the
* MainParseArgs function.
* The line should have all its leading whitespace removed.
* Only those that come from the various arguments.
*-----------------------------------------------------------------------
char *line
; /* Line to fracture */
char **argv
; /* Manufactured argument vector */
int argc
; /* Number of arguments in argv */
if (line
== NULL
) return;
while (*line
== ' ') line
++;
argv
= Str_BreakString (line
, " \t", "\n", &argc
);
MainParseArgs(argc
, argv
);
*----------------------------------------------------------------------
* The main function, for obvious reasons. Initializes variables
* and a few modules, then parses the arguments give it in the
* environment and on the command line. Reads the system makefile
* followed by either Makefile, makefile or the file given by the
* -f argument. Sets the .MAKEFLAGS PMake variable based on all the
* flags it has received by then uses either the Make or the Compat
* module to create the initial list of targets.
* If -q was given, exits -1 if anything was out-of-date. Else it exits
* The program exits when done. Targets are created. etc. etc. etc.
*----------------------------------------------------------------------
Lst targs
; /* list of target nodes to create. Passed to
Boolean outOfDate
; /* FALSE if all targets up to date */
create
= Lst_Init (FALSE
);
makefiles
= Lst_Init(FALSE
);
beSilent
= FALSE
; /* Print commands as executed */
ignoreErrors
= FALSE
; /* Pay attention to non-zero returns */
noExecute
= FALSE
; /* Execute all commands */
keepgoing
= FALSE
; /* Stop on error */
allPrecious
= FALSE
; /* Remove targets when interrupted */
queryFlag
= FALSE
; /* This is not just a check-run */
noBuiltins
= FALSE
; /* Read the built-in rules */
touchFlag
= FALSE
; /* Actually update targets */
usePipes
= TRUE
; /* Catch child output in pipes */
debug
= 0; /* No debug verbosity, please. */
noWarnings
= FALSE
; /* Print warning messages */
sysVmake
= FALSE
; /* Don't be System V compatible */
maxJobs
= DEFMAXJOBS
; /* Set the default maximum concurrency */
maxLocal
= DEFMAXLOCAL
; /* Set the default local max concurrency */
* Deal with disagreement between different getopt's as to what
* the initial value of optind should be by simply saving the
* See what the user calls us. If s/he calls us (yuck) "make", then
* act like it. Otherwise act like our normal, cheerful self.
cp
= rindex (argv
[0], '/');
if (cp
!= (char *)NULL
) {
if (strcmp (cp
, "make") == 0) {
amMake
= TRUE
; /* Be like make */
backwards
= TRUE
; /* Do things the old-fashioned way */
} else if (strcmp(cp
, "smake") == 0 || strcmp(cp
, "vmake") == 0) {
sysVmake
= backwards
= TRUE
;
backwards
= FALSE
; /* Do things MY way, not MAKE's */
* Initialize the parsing, directory and variable modules to prepare
* for the reading of inclusion paths and variable settings on the
Dir_Init (); /* Initialize directory structures so -I flags
* can be processed correctly */
Parse_Init (); /* Need to initialize the paths of #include
Var_Init (); /* As well as the lists of variables for
* Initialize various variables.
* .PMAKE gets how we were executed.
* MAKE also gets this name, for compatibility
* .MAKEFLAGS gets set to the empty string just in case.
* MFLAGS also gets initialized empty, for compatibility.
Var_Set (".PMAKE", argv
[0], VAR_GLOBAL
);
Var_Set ("MAKE", argv
[0], VAR_GLOBAL
);
Var_Set (MAKEFLAGS
, "", VAR_GLOBAL
);
Var_Set ("MFLAGS", "", VAR_GLOBAL
);
* First snag any flags out of the PMAKE environment variable.
* (Note this is *not* MAKEFLAGS since /bin/make uses that and it's in
Main_ParseArgLine(getenv("MAKEFLAGS"));
Main_ParseArgLine (getenv("PMAKE"));
MainParseArgs (argc
, argv
);
* Initialize archive, target and suffix modules in preparation for
* parsing the makefile(s)
* Set up the .TARGETS variable to contain the list of targets to be
* created. If none specified, make the variable empty -- the parser
* will fill the thing in with the default or .MAIN target.
if (!Lst_IsEmpty(create
)) {
for (ln
= Lst_First(create
); ln
!= NILLNODE
; ln
= Lst_Succ(ln
)) {
char *name
= (char *)Lst_Datum(ln
);
Var_Append(".TARGETS", name
, VAR_GLOBAL
);
Var_Set(".TARGETS", "", VAR_GLOBAL
);
* Read in the built-in rules first, followed by the specified makefile,
* if it was (makefile != (char *) NULL), or the default Makefile and
* makefile, in that order, if it wasn't.
if (!noBuiltins
&& !ReadMakefile (DEFSYSMK
)) {
Fatal ("Could not open system rules (%s)", DEFSYSMK
);
if (!Lst_IsEmpty(makefiles
)) {
LstNode ln
= Lst_Find(makefiles
, (ClientData
)NULL
, ReadMakefile
);
Fatal ("Cannot open %s", (char *)Lst_Datum(ln
));
if (!ReadMakefile("makefile")) {
(void)ReadMakefile("Makefile");
if (!ReadMakefile ((amMake
|| sysVmake
) ? "makefile" : "Makefile")) {
(void) ReadMakefile ((amMake
||sysVmake
) ? "Makefile" : "makefile");
Var_Append ("MFLAGS", Var_Value(MAKEFLAGS
, VAR_GLOBAL
), VAR_GLOBAL
);
* Install all the flags into the PMAKE envariable.
setenv("MAKEFLAGS", Var_Value(MAKEFLAGS
, VAR_GLOBAL
));
setenv("PMAKE", Var_Value(MAKEFLAGS
, VAR_GLOBAL
));
* For compatibility, look at the directories in the VPATH variable
* and add them to the search path, if the variable is defined. The
* variable's value is in the same format as the PATH envariable, i.e.
* <directory>:<directory>:<directory>...
if (Var_Exists ("VPATH", VAR_CMD
)) {
static char VPATH
[] = "${VPATH}"; /* GCC stores string constants in
* read-only memory, but Var_Subst
* will want to write this thing,
* so store it in an array */
vpath
= Var_Subst (VPATH
, VAR_CMD
, FALSE
);
* Skip to end of directory
for (cp
= path
; *cp
!= ':' && *cp
!= '\0'; cp
++) {
* Save terminator character to figure out when to stop
* Add directory to search path
Dir_AddDir (dirSearchPath
, path
);
* Now that all search paths have been read for suffixes et al, it's
* time to add the default search path to their lists...
* Print the initial graph, if the user requested it
* Have now read the entire graph and need to make a list of targets to
* create. If none was given on the command line, we consult the parsing
* module to find the main target(s) to create.
if (Lst_IsEmpty (create
)) {
targs
= Parse_MainName ();
targs
= Targ_FindList (create
, TARG_CREATE
);
* Initialize job module before traversing the graph, now that any
* .BEGIN and .END targets have been read. This is done only if the
* -q flag wasn't given (to prevent the .BEGIN from being executed
Job_Init (maxJobs
, maxLocal
);
* Traverse the graph, checking on all the targets
outOfDate
= Make_Run (targs
);
* Compat_Init will take care of creating all the targets as well
* as initializing the module.
* Print the graph now it's been processed if the user requested it
if (queryFlag
&& outOfDate
) {
*-----------------------------------------------------------------------
* Open and parse the given makefile.
* TRUE if ok. FALSE if couldn't open file.
*-----------------------------------------------------------------------
char *fname
; /* makefile to read */
if (strcmp (fname
, "-") == 0) {
Parse_File ("(stdin)", stdin
);
Var_Set("MAKEFILE", "", VAR_GLOBAL
);
extern Lst parseIncPath
, sysIncPath
;
stream
= fopen (fname
, "r");
if (stream
== (FILE *) NULL
) {
* Look in -I directories...
char *name
= Dir_FindFile(fname
, parseIncPath
);
* Last-ditch: look in system include directories.
name
= Dir_FindFile(fname
, sysIncPath
);
stream
= fopen(name
, "r");
if (stream
== (FILE *)NULL
) {
/* Better safe than sorry... */
* Set the MAKEFILE variable desired by System V fans -- the placement
* of the setting here means it gets set to the last makefile
* specified, as it is set by SysV make...
Var_Set("MAKEFILE", fname
, VAR_GLOBAL
);
Parse_File (fname
, stream
);
*-----------------------------------------------------------------------
* Print an error message given its format and 0, 1, 2 or 3 arguments.
* The message is printed.
*-----------------------------------------------------------------------
Error (fmt
, arg1
, arg2
, arg3
)
char *fmt
; /* Format string */
int arg1
, /* First optional argument */
arg2
, /* Second optional argument */
arg3
; /* Third optional argument */
static char estr
[BSIZE
]; /* output string */
sprintf (estr
, "%s: ", Var_Value(".PMAKE", VAR_GLOBAL
));
sprintf (&estr
[strlen (estr
)], fmt
, arg1
, arg2
, arg3
);
(void) strcat (estr
, "\n");
*-----------------------------------------------------------------------
* Produce a Fatal error message. If jobs are running, waits for them
*-----------------------------------------------------------------------
char *fmt
; /* format string */
int arg1
; /* first optional argument */
int arg2
; /* second optional argument */
exit (2); /* Not 1 so -q can distinguish error */
*-----------------------------------------------------------------------
* Major exception once jobs are being created. Kills all jobs, prints
* All children are killed indiscriminately and the program Lib_Exits
*-----------------------------------------------------------------------
char *fmt
; /* format string */
int arg1
; /* optional argument */
int arg2
; /* optional second argument */
*-----------------------------------------------------------------------
* Exit without giving a message.
*-----------------------------------------------------------------------
exit (2); /* Not 1, so -q can distinguish error */
*-----------------------------------------------------------------------
* Called when aborting due to errors in child shell to signal
* -----------------------------------------------------------------------
int errors
; /* number of errors encountered in Make_Make */
Fatal ("%d error%s", errors
, errors
== 1 ? "" : "s");