* 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
* %sccs.include.redist.c%
"@(#) Copyright (c) 1989 The Regents of the University of California.\n\
static char sccsid
[] = "@(#)main.c 5.24 (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"
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 Boolean noBuiltins
; /* -r flag */
static Lst makefiles
; /* ordered list of makefiles to read */
int maxJobs
; /* -J argument */
static int maxLocal
; /* -L argument */
Boolean debug
; /* -d flag */
Boolean noExecute
; /* -n flag */
Boolean keepgoing
; /* -k flag */
Boolean queryFlag
; /* -q flag */
Boolean touchFlag
; /* -t flag */
Boolean usePipes
; /* !-P flag */
Boolean ignoreErrors
; /* -i flag */
Boolean beSilent
; /* -s flag */
Boolean oldVars
; /* variable substitution style */
Boolean checkEnvFirst
; /* -e flag */
static Boolean jobsRunning
; /* TRUE if the jobs might be running */
static Boolean
ReadMakefile();
static char *curdir
; /* if chdir'd for an architecture */
* 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
)
optind
= 1; /* since we're called more than once */
rearg
: while((c
= getopt(argc
, argv
, "D:I:d:ef:ij:knqrst")) != EOF
) {
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
, "-L", VAR_GLOBAL
);
Var_Append(MAKEFLAGS
, optarg
, VAR_GLOBAL
);
Var_Append(MAKEFLAGS
, "-P", VAR_GLOBAL
);
Var_Append(MAKEFLAGS
, "-S", VAR_GLOBAL
);
for (; *modules
; ++modules
)
else if (modules
[1] == '2') {
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
, "-J", VAR_GLOBAL
);
Var_Append(MAKEFLAGS
, optarg
, VAR_GLOBAL
);
Var_Append(MAKEFLAGS
, "-k", VAR_GLOBAL
);
Var_Append(MAKEFLAGS
, "-n", VAR_GLOBAL
);
/* Kind of nonsensical, wot? */
Var_Append(MAKEFLAGS
, "-q", VAR_GLOBAL
);
Var_Append(MAKEFLAGS
, "-r", VAR_GLOBAL
);
Var_Append(MAKEFLAGS
, "-s", VAR_GLOBAL
);
Var_Append(MAKEFLAGS
, "-t", VAR_GLOBAL
);
* 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 of the "create" list.
for (argv
+= optind
, argc
-= optind
; *argv
; ++argv
, --argc
)
Parse_DoVar(*argv
, VAR_CMD
);
Punt("illegal (null) argument.");
(void)Lst_AtEnd(create
, (ClientData
)*argv
);
* 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 */
for (; *line
== ' '; ++line
);
argv
= brk_string(line
, &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
; /* target nodes to create -- passed to Make_Init */
Boolean outOfDate
; /* FALSE if all targets up to date */
char *p
, *path
, *getenv();
* if the MAKEOBJDIR (or by default, the _PATH_OBJDIR) directory
* exists, change into it and build there. Once things are
* initted, have to add the original directory to the search path,
* and modify the paths for the Makefiles apropriately. The
* current directory is also placed as a variable for make scripts.
if (!(path
= getenv("MAKEOBJDIR")))
curdir
= emalloc((u_int
)MAXPATHLEN
+ 1);
(void)fprintf(stderr
, "make: %s.\n", curdir
);
(void)fprintf(stderr
, "make: %s: %s.\n",
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. */
maxJobs
= DEFMAXJOBS
; /* Set default max concurrency */
maxLocal
= DEFMAXLOCAL
; /* Set default local max concurrency */
* 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
Dir_AddDir(dirSearchPath
, curdir
);
Var_Set(".CURDIR", curdir
, VAR_GLOBAL
);
Var_Set(".CURDIR", ".", VAR_GLOBAL
);
* Initialize various variables.
* 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("MAKE", argv
[0], VAR_GLOBAL
);
Var_Set(MAKEFLAGS
, "", VAR_GLOBAL
);
Var_Set("MFLAGS", "", VAR_GLOBAL
);
Var_Set("MACHINE", MACHINE
, VAR_GLOBAL
);
* First snag any flags out of the MAKE environment variable.
* (Note this is *not* MAKEFLAGS since /bin/make uses that and it's
* in a different format).
Main_ParseArgLine(getenv("MAKEFLAGS"));
Main_ParseArgLine(getenv("MAKE"));
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
;
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(_PATH_DEFSYSMK
))
Fatal("make: no system rules (%s).", _PATH_DEFSYSMK
);
if (!Lst_IsEmpty(makefiles
)) {
ln
= Lst_Find(makefiles
, (ClientData
)NULL
, ReadMakefile
);
Fatal("make: cannot open %s.", (char *)Lst_Datum(ln
));
} else if (!ReadMakefile("makefile"))
(void)ReadMakefile("Makefile");
(void)ReadMakefile(".depend");
Var_Append("MFLAGS", Var_Value(MAKEFLAGS
, VAR_GLOBAL
), VAR_GLOBAL
);
/* Install all the flags into the MAKE envariable. */
if ((p
= Var_Value(MAKEFLAGS
, VAR_GLOBAL
)) && *p
)
setenv("MAKEFLAGS", p
, 1);
* 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
)) {
char *vpath
, *path
, *cp
, savec
;
* GCC stores string constants in read-only memory, but
* Var_Subst will want to write this thing, so store it
static char VPATH
[] = "${VPATH}";
vpath
= Var_Subst(VPATH
, VAR_CMD
, FALSE
);
/* skip to end of directory */
for (cp
= path
; *cp
!= ':' && *cp
!= '\0'; cp
++);
/* Save terminator character so know when to stop */
/* Add directory to search path */
Dir_AddDir(dirSearchPath
, path
);
(void)free((Address
)vpath
);
* 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.
targs
= Parse_MainName();
targs
= Targ_FindList(create
, TARG_CREATE
);
* this was original amMake -- want to allow parallelism, so put this
* 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 should it exist).
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 */
extern Lst parseIncPath
, sysIncPath
;
char *name
, path
[MAXPATHLEN
+ 1];
if (!strcmp(fname
, "-")) {
Parse_File("(stdin)", stdin
);
Var_Set("MAKEFILE", "", VAR_GLOBAL
);
if (stream
= fopen(fname
, "r"))
/* if we've chdir'd, rebuild the path name */
if (curdir
&& *fname
!= '/') {
(void)sprintf(path
, "%s/%s", curdir
, fname
);
if (stream
= fopen(path
, "r")) {
/* look in -I and system include directories. */
name
= Dir_FindFile(fname
, parseIncPath
);
name
= Dir_FindFile(fname
, sysIncPath
);
if (!name
|| !(stream
= fopen(name
, "r")))
* 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.
found
: Var_Set("MAKEFILE", fname
, VAR_GLOBAL
);
Parse_File(fname
, stream
);
* Print an error message given its format.
* The message is printed.
fmt
= va_arg(ap
, char *);
(void)vfprintf(stderr
, fmt
, ap
);
(void)fprintf(stderr
, "\n");
* Produce a Fatal error message. If jobs are running, waits for them
fmt
= va_arg(ap
, char *);
(void)vfprintf(stderr
, fmt
, ap
);
(void)fprintf(stderr
, "\n");
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
(void)fprintf(stderr
, "make: ");
fmt
= va_arg(ap
, char *);
(void)vfprintf(stderr
, fmt
, ap
);
(void)fprintf(stderr
, "\n");
* 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");
* malloc, but die on error.
* die when out of memory.
(void)fprintf(stderr
, "make: %s.\n", strerror(errno
));
* exit with usage message
"usage: make [-eiknqrst] [-D variable] [-d flags] [-f makefile ]\n\t\
[-I directory] [-j max_jobs] [variable=value]\n");