diff options
Diffstat (limited to '')
-rw-r--r-- | src/kmk/main.c | 4482 |
1 files changed, 4482 insertions, 0 deletions
diff --git a/src/kmk/main.c b/src/kmk/main.c new file mode 100644 index 0000000..d56af0a --- /dev/null +++ b/src/kmk/main.c @@ -0,0 +1,4482 @@ +/* Argument parsing and main program of GNU Make. +Copyright (C) 1988-2016 Free Software Foundation, Inc. +This file is part of GNU Make. + +GNU Make is free software; you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation; either version 3 of the License, or (at your option) any later +version. + +GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "makeint.h" +#include "os.h" +#include "filedef.h" +#include "dep.h" +#include "variable.h" +#include "job.h" +#include "commands.h" +#include "rule.h" +#include "debug.h" +#include "getopt.h" +#ifdef KMK +# include "kbuild.h" +#endif +#ifdef CONFIG_WITH_KMK_BUILTIN_STATS +# include "kmkbuiltin.h" +#endif + +#include <assert.h> +#ifdef _AMIGA +# include <dos/dos.h> +# include <proto/dos.h> +#endif +#ifdef WINDOWS32 +# include <windows.h> +# include <io.h> +# include "pathstuff.h" +# ifndef CONFIG_NEW_WIN_CHILDREN +# include "sub_proc.h" +# else +# include "w32/winchildren.h" +# endif +# include "w32err.h" +#endif +#ifdef __EMX__ +# include <sys/types.h> +# include <sys/wait.h> +#endif +#ifdef HAVE_FCNTL_H +# include <fcntl.h> +#endif +#ifdef CONFIG_WITH_COMPILER +# include "kmk_cc_exec.h" +#endif + +#ifdef KMK /* for get_online_cpu_count */ +# if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +# include <sys/sysctl.h> +# endif +# ifdef __OS2__ +# define INCL_BASE +# include <os2.h> +# endif +# ifdef __HAIKU__ +# include <OS.h> +# endif +#endif /* KMK*/ + +#if defined(HAVE_SYS_RESOURCE_H) && defined(HAVE_GETRLIMIT) && defined(HAVE_SETRLIMIT) +# define SET_STACK_SIZE +#endif + +#ifdef SET_STACK_SIZE +# include <sys/resource.h> +#endif + +#ifdef _AMIGA +int __stack = 20000; /* Make sure we have 20K of stack space */ +#endif +#ifdef VMS +int vms_use_mcr_command = 0; +int vms_always_use_cmd_file = 0; +int vms_gnv_shell = 0; +int vms_legacy_behavior = 0; +int vms_comma_separator = 0; +int vms_unix_simulation = 0; +int vms_report_unix_paths = 0; + +/* Evaluates if a VMS environment option is set, only look at first character */ +static int +get_vms_env_flag (const char *name, int default_value) +{ +char * value; +char x; + + value = getenv (name); + if (value == NULL) + return default_value; + + x = toupper (value[0]); + switch (x) + { + case '1': + case 'T': + case 'E': + return 1; + break; + case '0': + case 'F': + case 'D': + return 0; + } +} +#endif + +#ifdef CONFIG_WITH_PRINT_STATS_SWITCH +void print_variable_stats (void); +void print_dir_stats (void); +void print_file_stats (void); +#endif + +#if defined HAVE_WAITPID || defined HAVE_WAIT3 +# define HAVE_WAIT_NOHANG +#endif + +#if !defined(HAVE_UNISTD_H) && !defined(_MSC_VER) /* bird */ +int chdir (); +#endif +#ifndef STDC_HEADERS +# ifndef sun /* Sun has an incorrect decl in a header. */ +void exit (int) __attribute__ ((noreturn)); +# endif +double atof (); +#endif + +static void clean_jobserver (int status); +static void print_data_base (void); +static void print_version (void); +static void decode_switches (int argc, const char **argv, int env); +static void decode_env_switches (const char *envar, unsigned int len); +static struct variable *define_makeflags (int all, int makefile); +static char *quote_for_env (char *out, const char *in); +static void initialize_global_hash_tables (void); + + +/* The structure that describes an accepted command switch. */ + +struct command_switch + { + int c; /* The switch character. */ + + enum /* Type of the value. */ + { + flag, /* Turn int flag on. */ + flag_off, /* Turn int flag off. */ + string, /* One string per invocation. */ + strlist, /* One string per switch. */ + filename, /* A string containing a file name. */ + positive_int, /* A positive integer. */ + floating, /* A floating-point number (double). */ + ignore /* Ignored. */ + } type; + + void *value_ptr; /* Pointer to the value-holding variable. */ + + unsigned int env:1; /* Can come from MAKEFLAGS. */ + unsigned int toenv:1; /* Should be put in MAKEFLAGS. */ + unsigned int no_makefile:1; /* Don't propagate when remaking makefiles. */ + + const void *noarg_value; /* Pointer to value used if no arg given. */ + const void *default_value; /* Pointer to default value. */ + + const char *long_name; /* Long option name. */ + }; + +/* True if C is a switch value that corresponds to a short option. */ + +#define short_option(c) ((c) <= CHAR_MAX) + +/* The structure used to hold the list of strings given + in command switches of a type that takes strlist arguments. */ + +struct stringlist + { + const char **list; /* Nil-terminated list of strings. */ + unsigned int idx; /* Index into above. */ + unsigned int max; /* Number of pointers allocated. */ + }; + + +/* The recognized command switches. */ + +/* Nonzero means do extra verification (that may slow things down). */ + +int verify_flag; + +/* Nonzero means do not print commands to be executed (-s). */ + +int silent_flag; + +/* Nonzero means just touch the files + that would appear to need remaking (-t) */ + +int touch_flag; + +/* Nonzero means just print what commands would need to be executed, + don't actually execute them (-n). */ + +int just_print_flag; + +#ifdef CONFIG_PRETTY_COMMAND_PRINTING +/* Nonzero means to print commands argument for argument skipping blanks. */ + +int pretty_command_printing; +#endif + +#ifdef CONFIG_WITH_PRINT_STATS_SWITCH +/* Nonzero means to print internal statistics before exiting. */ + +int print_stats_flag; +#endif + +#ifdef CONFIG_WITH_PRINT_TIME_SWITCH +/* Minimum number of seconds to report, -1 if disabled. */ + +int print_time_min = -1; +static int default_print_time_min = -1; +static int no_val_print_time_min = 0; +static big_int make_start_ts = -1; +int print_time_width = 5; +#endif + +/* Print debugging info (--debug). */ + +static struct stringlist *db_flags = 0; +static int debug_flag = 0; + +int db_level = 0; + +/* Synchronize output (--output-sync). */ + +char *output_sync_option = 0; + +#ifdef WINDOWS32 +/* Suspend make in main for a short time to allow debugger to attach */ + +int suspend_flag = 0; +#endif + +/* Environment variables override makefile definitions. */ + +int env_overrides = 0; + +/* Nonzero means ignore status codes returned by commands + executed to remake files. Just treat them all as successful (-i). */ + +int ignore_errors_flag = 0; + +/* Nonzero means don't remake anything, just print the data base + that results from reading the makefile (-p). */ + +int print_data_base_flag = 0; + +/* Nonzero means don't remake anything; just return a nonzero status + if the specified targets are not up to date (-q). */ + +int question_flag = 0; + +/* Nonzero means do not use any of the builtin rules (-r) / variables (-R). */ + +int no_builtin_rules_flag = 0; +int no_builtin_variables_flag = 0; + +/* Nonzero means keep going even if remaking some file fails (-k). */ + +int keep_going_flag; +int default_keep_going_flag = 0; + +/* Nonzero means check symlink mtimes. */ + +int check_symlink_flag = 0; + +/* Nonzero means print directory before starting and when done (-w). */ + +int print_directory_flag = 0; + +/* Nonzero means ignore print_directory_flag and never print the directory. + This is necessary because print_directory_flag is set implicitly. */ + +int inhibit_print_directory_flag = 0; + +/* Nonzero means print version information. */ + +int print_version_flag = 0; + +/* List of makefiles given with -f switches. */ + +static struct stringlist *makefiles = 0; + +/* Size of the stack when we started. */ + +#ifdef SET_STACK_SIZE +struct rlimit stack_limit; +#endif + + +/* Number of job slots for parallelism. */ + +unsigned int job_slots; + +#define INVALID_JOB_SLOTS (-1) +static unsigned int master_job_slots = 0; +static int arg_job_slots = INVALID_JOB_SLOTS; + +#ifdef KMK +static int default_job_slots = INVALID_JOB_SLOTS; +#else +static const int default_job_slots = INVALID_JOB_SLOTS; +#endif + +/* Value of job_slots that means no limit. */ + +static const int inf_jobs = 0; + +/* Authorization for the jobserver. */ + +static char *jobserver_auth = NULL; + +/* Handle for the mutex used on Windows to synchronize output of our + children under -O. */ + +char *sync_mutex = NULL; + +/* Maximum load average at which multiple jobs will be run. + Negative values mean unlimited, while zero means limit to + zero load (which could be useful to start infinite jobs remotely + but one at a time locally). */ +#ifndef NO_FLOAT +double max_load_average = -1.0; +double default_load_average = -1.0; +#else +int max_load_average = -1; +int default_load_average = -1; +#endif + +/* List of directories given with -C switches. */ + +static struct stringlist *directories = 0; + +/* List of include directories given with -I switches. */ + +static struct stringlist *include_directories = 0; + +/* List of files given with -o switches. */ + +static struct stringlist *old_files = 0; + +/* List of files given with -W switches. */ + +static struct stringlist *new_files = 0; + +/* List of strings to be eval'd. */ +static struct stringlist *eval_strings = 0; + +/* If nonzero, we should just print usage and exit. */ + +static int print_usage_flag = 0; + +/* If nonzero, we should print a warning message + for each reference to an undefined variable. */ + +int warn_undefined_variables_flag; + +/* If nonzero, always build all targets, regardless of whether + they appear out of date or not. */ + +static int always_make_set = 0; +int always_make_flag = 0; + +/* If nonzero, we're in the "try to rebuild makefiles" phase. */ + +int rebuilding_makefiles = 0; + +/* Remember the original value of the SHELL variable, from the environment. */ + +struct variable shell_var; + +/* This character introduces a command: it's the first char on the line. */ + +char cmd_prefix = '\t'; + +#ifdef KMK +/* Process priority. + 0 = no change; + 1 = idle / max nice; + 2 = below normal / nice 10; + 3 = normal / nice 0; + 4 = high / nice -10; + 5 = realtime / nice -19; */ + +int process_priority = 0; + +/* Process affinity mask; 0 means any CPU. */ + +int process_affinity = 0; +#endif /* KMK */ + +#if defined (CONFIG_WITH_MAKE_STATS) || defined (CONFIG_WITH_MINIMAL_STATS) +/* When set, we'll gather expensive statistics like for the heap. */ + +int make_expensive_statistics = 0; +#endif + +#if defined (WINDOWS32) && defined (CONFIG_NEW_WIN_CHILDREN) +/* --job-object[=mode]. */ +char *win_job_object_mode = NULL; + +/* --job-object-name=name */ +char *win_job_object_name = NULL; + +/** --job-object-no-kill. */ +int win_job_object_no_kill = 0; +#endif + + +/* The usage output. We write it this way to make life easier for the + translators, especially those trying to translate to right-to-left + languages like Hebrew. */ + +static const char *const usage[] = + { + N_("Options:\n"), + N_("\ + -b, -m Ignored for compatibility.\n"), + N_("\ + -B, --always-make Unconditionally make all targets.\n"), + N_("\ + -C DIRECTORY, --directory=DIRECTORY\n\ + Change to DIRECTORY before doing anything.\n"), + N_("\ + -d Print lots of debugging information.\n"), + N_("\ + --debug[=FLAGS] Print various types of debugging information.\n"), + N_("\ + -e, --environment-overrides\n\ + Environment variables override makefiles.\n"), + N_("\ + --eval=STRING Evaluate STRING as a makefile statement.\n"), + N_("\ + -f FILE, --file=FILE, --makefile=FILE\n\ + Read FILE as a makefile.\n"), + N_("\ + -h, --help Print this message and exit.\n"), + N_("\ + -i, --ignore-errors Ignore errors from recipes.\n"), + N_("\ + -I DIRECTORY, --include-dir=DIRECTORY\n\ + Search DIRECTORY for included makefiles.\n"), +#ifdef KMK + N_("\ + -j [N], --jobs[=N] Allow N jobs at once; infinite jobs with no arg.\n\ + The default is the number of active CPUs.\n"), +#else + N_("\ + -j [N], --jobs[=N] Allow N jobs at once; infinite jobs with no arg.\n"), +#endif + N_("\ + -k, --keep-going Keep going when some targets can't be made.\n"), + N_("\ + -l [N], --load-average[=N], --max-load[=N]\n\ + Don't start multiple jobs unless load is below N.\n"), + N_("\ + -L, --check-symlink-times Use the latest mtime between symlinks and target.\n"), + N_("\ + -n, --just-print, --dry-run, --recon\n\ + Don't actually run any recipe; just print them.\n"), + N_("\ + -o FILE, --old-file=FILE, --assume-old=FILE\n\ + Consider FILE to be very old and don't remake it.\n"), +#ifndef KMK + N_("\ + -O[TYPE], --output-sync[=TYPE]\n\ + Synchronize output of parallel jobs by TYPE.\n"), +#elif defined(KBUILD_OS_WINDOWS) + N_("\ + -O[TYPE], --output-sync[=TYPE]\n\ + Synchronize output of parallel jobs by TYPE:\n\ + none = no synchronization.\n\ + line = receip line output\n\ + target = entire receip output (default)\n\ + recurse = entire recursive invocation\n"), +#else + N_("\ + -O[TYPE], --output-sync[=TYPE]\n\ + Synchronize output of parallel jobs by TYPE:\n\ + none = no synchronization (default).\n\ + line = receip line output\n\ + target = entire receip output\n\ + recurse = entire recursive invocation\n"), +#endif + N_("\ + -p, --print-data-base Print make's internal database.\n"), + N_("\ + -q, --question Run no recipe; exit status says if up to date.\n"), + N_("\ + -r, --no-builtin-rules Disable the built-in implicit rules.\n"), + N_("\ + -R, --no-builtin-variables Disable the built-in variable settings.\n"), + N_("\ + -s, --silent, --quiet Don't echo recipes.\n"), + N_("\ + -S, --no-keep-going, --stop\n\ + Turns off -k.\n"), + N_("\ + -t, --touch Touch targets instead of remaking them.\n"), + N_("\ + --trace Print tracing information.\n"), + N_("\ + -v, --version Print the version number of make and exit.\n"), + N_("\ + -w, --print-directory Print the current directory.\n"), + N_("\ + --no-print-directory Turn off -w, even if it was turned on implicitly.\n"), + N_("\ + -W FILE, --what-if=FILE, --new-file=FILE, --assume-new=FILE\n\ + Consider FILE to be infinitely new.\n"), + N_("\ + --warn-undefined-variables Warn when an undefined variable is referenced.\n"), +#ifdef KMK + N_("\ + --affinity=mask Sets the CPU affinity on some hosts.\n"), + N_("\ + --priority=1-5 Sets the process priority / nice level:\n\ + 1 = idle / max nice;\n\ + 2 = below normal / nice 10;\n\ + 3 = normal / nice 0;\n\ + 4 = high / nice -10;\n\ + 5 = realtime / nice -19;\n"), + N_("\ + --nice Alias for --priority=1\n"), +#endif /* KMK */ +#ifdef CONFIG_PRETTY_COMMAND_PRINTING + N_("\ + --pretty-command-printing Makes the command echo easier to read.\n"), +#endif +#ifdef CONFIG_WITH_PRINT_STATS_SWITCH + N_("\ + --print-stats Print make statistics.\n"), +#endif +#ifdef CONFIG_WITH_PRINT_TIME_SWITCH + N_("\ + --print-time[=MIN-SEC] Print file build times starting at arg.\n"), +#endif +#ifdef CONFIG_WITH_MAKE_STATS + N_("\ + --statistics Gather extra statistics for $(make-stats ).\n"), +#endif +#if defined (WINDOWS32) && defined (CONFIG_NEW_WIN_CHILDREN) + N_("\ + --job-object=mode Windows job object mode:\n\ + login = Per login session (default).\n\ + root = Root make instance only.\n\ + each = Each make instances.\n\ + none = No job objects.\n"), + N_("\ + --job-object-name=name Name of windows job object to open or create.\n\ + The default name depends on the level.\n"), + N_("\ + --job-object-no-kill Do not kill orphaned child processes when done.\n"), +#endif + NULL + }; + +/* The table of command switches. + Order matters here: this is the order MAKEFLAGS will be constructed. + So be sure all simple flags (single char, no argument) come first. */ + +static const struct command_switch switches[] = + { + { 'b', ignore, 0, 0, 0, 0, 0, 0, 0 }, + { 'B', flag, &always_make_set, 1, 1, 0, 0, 0, "always-make" }, + { 'd', flag, &debug_flag, 1, 1, 0, 0, 0, 0 }, +#ifdef WINDOWS32 + { 'D', flag, &suspend_flag, 1, 1, 0, 0, 0, "suspend-for-debug" }, +#endif + { 'e', flag, &env_overrides, 1, 1, 0, 0, 0, "environment-overrides", }, + { 'h', flag, &print_usage_flag, 0, 0, 0, 0, 0, "help" }, + { 'i', flag, &ignore_errors_flag, 1, 1, 0, 0, 0, "ignore-errors" }, + { 'k', flag, &keep_going_flag, 1, 1, 0, 0, &default_keep_going_flag, + "keep-going" }, + { 'L', flag, &check_symlink_flag, 1, 1, 0, 0, 0, "check-symlink-times" }, + { 'm', ignore, 0, 0, 0, 0, 0, 0, 0 }, + { 'n', flag, &just_print_flag, 1, 1, 1, 0, 0, "just-print" }, + { 'p', flag, &print_data_base_flag, 1, 1, 0, 0, 0, "print-data-base" }, +#ifdef CONFIG_PRETTY_COMMAND_PRINTING + { CHAR_MAX+50, flag, (char *) &pretty_command_printing, 1, 1, 1, 0, 0, + "pretty-command-printing" }, +#endif +#ifdef CONFIG_WITH_PRINT_STATS_SWITCH + { CHAR_MAX+51, flag, (char *) &print_stats_flag, 1, 1, 1, 0, 0, + "print-stats" }, +#endif +#ifdef CONFIG_WITH_PRINT_TIME_SWITCH + { CHAR_MAX+52, positive_int, (char *) &print_time_min, 1, 1, 0, + (char *) &no_val_print_time_min, (char *) &default_print_time_min, + "print-time" }, +#endif +#ifdef KMK + { CHAR_MAX+54, positive_int, (char *) &process_priority, 1, 1, 0, + (char *) &process_priority, (char *) &process_priority, "priority" }, + { CHAR_MAX+55, positive_int, (char *) &process_affinity, 1, 1, 0, + (char *) &process_affinity, (char *) &process_affinity, "affinity" }, + { CHAR_MAX+56, flag, (char *) &process_priority, 1, 1, 0, 0, 0, "nice" }, +#endif + { 'q', flag, &question_flag, 1, 1, 1, 0, 0, "question" }, + { 'r', flag, &no_builtin_rules_flag, 1, 1, 0, 0, 0, "no-builtin-rules" }, + { 'R', flag, &no_builtin_variables_flag, 1, 1, 0, 0, 0, + "no-builtin-variables" }, + { 's', flag, &silent_flag, 1, 1, 0, 0, 0, "silent" }, + { 'S', flag_off, &keep_going_flag, 1, 1, 0, 0, &default_keep_going_flag, + "no-keep-going" }, +#if defined (CONFIG_WITH_MAKE_STATS) || defined (CONFIG_WITH_MINIMAL_STATS) + { CHAR_MAX+57, flag, (char *) &make_expensive_statistics, 1, 1, 1, 0, 0, + "statistics" }, +#endif + { 't', flag, &touch_flag, 1, 1, 1, 0, 0, "touch" }, + { 'v', flag, &print_version_flag, 1, 1, 0, 0, 0, "version" }, + { 'w', flag, &print_directory_flag, 1, 1, 0, 0, 0, "print-directory" }, + + /* These options take arguments. */ + { 'C', filename, &directories, 0, 0, 0, 0, 0, "directory" }, + { 'f', filename, &makefiles, 0, 0, 0, 0, 0, "file" }, + { 'I', filename, &include_directories, 1, 1, 0, 0, 0, + "include-dir" }, + { 'j', positive_int, &arg_job_slots, 1, 1, 0, &inf_jobs, &default_job_slots, + "jobs" }, +#ifndef NO_FLOAT + { 'l', floating, &max_load_average, 1, 1, 0, &default_load_average, + &default_load_average, "load-average" }, +#else + { 'l', positive_int, &max_load_average, 1, 1, 0, &default_load_average, + &default_load_average, "load-average" }, +#endif + { 'o', filename, &old_files, 0, 0, 0, 0, 0, "old-file" }, + { 'O', string, &output_sync_option, 1, 1, 0, "target", 0, "output-sync" }, + { 'W', filename, &new_files, 0, 0, 0, 0, 0, "what-if" }, + + /* These are long-style options. */ + { CHAR_MAX+1, strlist, &db_flags, 1, 1, 0, "basic", 0, "debug" }, + { CHAR_MAX+2, string, &jobserver_auth, 1, 1, 0, 0, 0, "jobserver-auth" }, + { CHAR_MAX+3, flag, &trace_flag, 1, 1, 0, 0, 0, "trace" }, + { CHAR_MAX+4, flag, &inhibit_print_directory_flag, 1, 1, 0, 0, 0, + "no-print-directory" }, + { CHAR_MAX+5, flag, &warn_undefined_variables_flag, 1, 1, 0, 0, 0, + "warn-undefined-variables" }, + { CHAR_MAX+6, strlist, &eval_strings, 1, 0, 0, 0, 0, "eval" }, + { CHAR_MAX+7, string, &sync_mutex, 1, 1, 0, 0, 0, "sync-mutex" }, +#if defined (WINDOWS32) && defined (CONFIG_NEW_WIN_CHILDREN) + { CHAR_MAX+58, string, &win_job_object_mode, 1, 1, 1, 0, 0, "job-object" }, + { CHAR_MAX+59, string, &win_job_object_name, 1, 1, 1, 0, 0, + "job-object-name" }, + { CHAR_MAX+60, flag, &win_job_object_no_kill, 1, 1, 1, 0, 0, + "job-object-no-kill" }, +#endif + { 0, 0, 0, 0, 0, 0, 0, 0, 0 } + }; + +/* Secondary long names for options. */ + +static struct option long_option_aliases[] = + { + { "quiet", no_argument, 0, 's' }, + { "stop", no_argument, 0, 'S' }, + { "new-file", required_argument, 0, 'W' }, + { "assume-new", required_argument, 0, 'W' }, + { "assume-old", required_argument, 0, 'o' }, + { "max-load", optional_argument, 0, 'l' }, + { "dry-run", no_argument, 0, 'n' }, + { "recon", no_argument, 0, 'n' }, + { "makefile", required_argument, 0, 'f' }, + }; + +/* List of goal targets. */ + +#ifndef KMK +static +#endif +struct goaldep *goals, *lastgoal; + +/* List of variables which were defined on the command line + (or, equivalently, in MAKEFLAGS). */ + +struct command_variable + { + struct command_variable *next; + struct variable *variable; + }; +static struct command_variable *command_variables; + +/* The name we were invoked with. */ + +#ifdef WINDOWS32 +/* On MS-Windows, we chop off the .exe suffix in 'main', so this + cannot be 'const'. */ +char *program; +#else +const char *program; +#endif + +/* Our current directory before processing any -C options. */ + +char *directory_before_chdir; + +/* Our current directory after processing all -C options. */ + +char *starting_directory; + +/* Value of the MAKELEVEL variable at startup (or 0). */ + +unsigned int makelevel; + +/* Pointer to the value of the .DEFAULT_GOAL special variable. + The value will be the name of the goal to remake if the command line + does not override it. It can be set by the makefile, or else it's + the first target defined in the makefile whose name does not start + with '.'. */ + +struct variable * default_goal_var; + +/* Pointer to structure for the file .DEFAULT + whose commands are used for any file that has none of its own. + This is zero if the makefiles do not define .DEFAULT. */ + +struct file *default_file; + +/* Nonzero if we have seen the magic '.POSIX' target. + This turns on pedantic compliance with POSIX.2. */ + +int posix_pedantic; + +/* Nonzero if we have seen the '.SECONDEXPANSION' target. + This turns on secondary expansion of prerequisites. */ + +int second_expansion; + +#ifdef CONFIG_WITH_2ND_TARGET_EXPANSION +/* Nonzero if we have seen the '.SECONDTARGETEXPANSION' target. + This turns on secondary expansion of targets. */ + +int second_target_expansion; +#endif + +/* Nonzero if we have seen the '.ONESHELL' target. + This causes the entire recipe to be handed to SHELL + as a single string, potentially containing newlines. */ + +int one_shell; + +/* One of OUTPUT_SYNC_* if the "--output-sync" option was given. This + attempts to synchronize the output of parallel jobs such that the results + of each job stay together. */ + +#ifdef KMK +int output_sync = OUTPUT_SYNC_TARGET; +#else +int output_sync = OUTPUT_SYNC_NONE; +#endif + +/* Nonzero if the "--trace" option was given. */ + +int trace_flag = 0; + +#ifndef CONFIG_WITH_EXTENDED_NOTPARALLEL + +/* Nonzero if we have seen the '.NOTPARALLEL' target. + This turns off parallel builds for this invocation of make. */ + +#else /* CONFIG_WITH_EXTENDED_NOTPARALLEL */ + +/* Negative if we have seen the '.NOTPARALLEL' target with an + empty dependency list. + + Zero if no '.NOTPARALLEL' or no file in the dependency list + is being executed. + + Positive when a file in the '.NOTPARALLEL' dependency list + is in progress, the value is the number of notparallel files + in progress (running or queued for running). + + In short, any nonzero value means no more parallel builing. */ +#endif /* CONFIG_WITH_EXTENDED_NOTPARALLEL */ + +int not_parallel; + +/* Nonzero if some rule detected clock skew; we keep track so (a) we only + print one warning about it during the run, and (b) we can print a final + warning at the end of the run. */ + +int clock_skew_detected; + +/* Map of possible stop characters for searching strings. */ +#ifndef UCHAR_MAX +# define UCHAR_MAX 255 +#endif +#ifdef _MSC_VER +__declspec(align(512)) /* bird: improve cacheline & tlb mojo */ +#endif +unsigned short stopchar_map[UCHAR_MAX + 1] = {0}; + +/* If output-sync is enabled we'll collect all the output generated due to + options, while reading makefiles, etc. */ + +struct output make_sync; + +#ifdef KMK +/** Current umask() value. */ +mode_t g_fUMask = 0022; +#endif + + +/* Mask of signals that are being caught with fatal_error_signal. */ + +#ifdef POSIX +sigset_t fatal_signal_set; +#else +# ifdef HAVE_SIGSETMASK +int fatal_signal_mask; +# endif +#endif + +#if !HAVE_DECL_BSD_SIGNAL && !defined bsd_signal +# if !defined HAVE_SIGACTION +# define bsd_signal signal +# else +typedef RETSIGTYPE (*bsd_signal_ret_t) (int); + +static bsd_signal_ret_t +bsd_signal (int sig, bsd_signal_ret_t func) +{ + struct sigaction act, oact; + act.sa_handler = func; + act.sa_flags = SA_RESTART; + sigemptyset (&act.sa_mask); + sigaddset (&act.sa_mask, sig); + if (sigaction (sig, &act, &oact) != 0) + return SIG_ERR; + return oact.sa_handler; +} +# endif +#endif + +#ifdef CONFIG_WITH_ALLOC_CACHES +struct alloccache dep_cache; +struct alloccache goaldep_cache; +struct alloccache nameseq_cache; +struct alloccache file_cache; +struct alloccache commands_cache; +struct alloccache variable_cache; +struct alloccache variable_set_cache; +struct alloccache variable_set_list_cache; + +static void +initialize_global_alloc_caches (void) +{ + alloccache_init (&dep_cache, sizeof (struct dep), "dep", NULL, NULL); + alloccache_init (&goaldep_cache, sizeof (struct goaldep), "goaldep", NULL, NULL); + alloccache_init (&nameseq_cache, sizeof (struct nameseq), "nameseq", NULL, NULL); + alloccache_init (&file_cache, sizeof (struct file), "file", NULL, NULL); + alloccache_init (&commands_cache, sizeof (struct commands), "commands", NULL, NULL); + alloccache_init (&variable_cache, sizeof (struct variable), "variable", NULL, NULL); + alloccache_init (&variable_set_cache, sizeof (struct variable_set), "variable_set", NULL, NULL); + alloccache_init (&variable_set_list_cache, sizeof (struct variable_set_list), "variable_set_list", NULL, NULL); +} +#endif /* CONFIG_WITH_ALLOC_CACHES */ + +static void +initialize_global_hash_tables (void) +{ + init_hash_global_variable_set (); + strcache_init (); + init_hash_files (); + hash_init_directories (); + hash_init_function_table (); +} + +/* This character map locate stop chars when parsing GNU makefiles. + Each element is true if we should stop parsing on that character. */ + +static void +initialize_stopchar_map (void) +{ + int i; + + stopchar_map[(int)'\0'] = MAP_NUL; + stopchar_map[(int)'#'] = MAP_COMMENT; + stopchar_map[(int)';'] = MAP_SEMI; + stopchar_map[(int)'='] = MAP_EQUALS; + stopchar_map[(int)':'] = MAP_COLON; + stopchar_map[(int)'%'] = MAP_PERCENT; + stopchar_map[(int)'|'] = MAP_PIPE; + stopchar_map[(int)'.'] = MAP_DOT | MAP_USERFUNC; + stopchar_map[(int)','] = MAP_COMMA; + stopchar_map[(int)'$'] = MAP_VARIABLE; + + stopchar_map[(int)'-'] = MAP_USERFUNC; + stopchar_map[(int)'_'] = MAP_USERFUNC; + + stopchar_map[(int)' '] = MAP_BLANK; + stopchar_map[(int)'\t'] = MAP_BLANK; + + stopchar_map[(int)'/'] = MAP_DIRSEP; +#if defined(VMS) + stopchar_map[(int)':'] |= MAP_DIRSEP; + stopchar_map[(int)']'] |= MAP_DIRSEP; + stopchar_map[(int)'>'] |= MAP_DIRSEP; +#elif defined(HAVE_DOS_PATHS) + stopchar_map[(int)'\\'] |= MAP_DIRSEP; +#endif + + for (i = 1; i <= UCHAR_MAX; ++i) + { + if (isspace (i) && NONE_SET (stopchar_map[i], MAP_BLANK)) + /* Don't mark blank characters as newline characters. */ + stopchar_map[i] |= MAP_NEWLINE; + else if (isalnum (i)) + stopchar_map[i] |= MAP_USERFUNC; + } +} + +static const char * +expand_command_line_file (const char *name) +{ + const char *cp; + char *expanded = 0; + + if (name[0] == '\0') + O (fatal, NILF, _("empty string invalid as file name")); + + if (name[0] == '~') + { + expanded = tilde_expand (name); + if (expanded && expanded[0] != '\0') + name = expanded; + } + + /* This is also done in parse_file_seq, so this is redundant + for names read from makefiles. It is here for names passed + on the command line. */ + while (name[0] == '.' && name[1] == '/') + { + name += 2; + while (name[0] == '/') + /* Skip following slashes: ".//foo" is "foo", not "/foo". */ + ++name; + } + + if (name[0] == '\0') + { + /* Nothing else but one or more "./", maybe plus slashes! */ + name = "./"; + } + + cp = strcache_add (name); + + free (expanded); + + return cp; +} + +/* Toggle -d on receipt of SIGUSR1. */ + +#ifdef SIGUSR1 +static RETSIGTYPE +debug_signal_handler (int sig UNUSED) +{ + db_level = db_level ? DB_NONE : DB_BASIC; +} +#endif + +static void +decode_debug_flags (void) +{ + const char **pp; + + if (debug_flag) + db_level = DB_ALL; + + if (db_flags) + for (pp=db_flags->list; *pp; ++pp) + { + const char *p = *pp; + + while (1) + { + switch (tolower (p[0])) + { + case 'a': + db_level |= DB_ALL; + break; + case 'b': + db_level |= DB_BASIC; + break; + case 'i': + db_level |= DB_BASIC | DB_IMPLICIT; + break; + case 'j': + db_level |= DB_JOBS; + break; + case 'm': + db_level |= DB_BASIC | DB_MAKEFILES; + break; + case 'n': + db_level = 0; + break; + case 'v': + db_level |= DB_BASIC | DB_VERBOSE; + break; +#ifdef DB_KMK + case 'k': + db_level |= DB_KMK; + break; +#endif /* DB_KMK */ + default: + OS (fatal, NILF, + _("unknown debug level specification '%s'"), p); + } + + while (*(++p) != '\0') + if (*p == ',' || *p == ' ') + { + ++p; + break; + } + + if (*p == '\0') + break; + } + } + + if (db_level) + verify_flag = 1; + + if (! db_level) + debug_flag = 0; +} + +static void +decode_output_sync_flags (void) +{ +#ifdef NO_OUTPUT_SYNC + output_sync = OUTPUT_SYNC_NONE; +#else + if (output_sync_option) + { + if (streq (output_sync_option, "none")) + output_sync = OUTPUT_SYNC_NONE; + else if (streq (output_sync_option, "line")) + output_sync = OUTPUT_SYNC_LINE; + else if (streq (output_sync_option, "target")) + output_sync = OUTPUT_SYNC_TARGET; + else if (streq (output_sync_option, "recurse")) + output_sync = OUTPUT_SYNC_RECURSE; + else + OS (fatal, NILF, + _("unknown output-sync type '%s'"), output_sync_option); + } + + if (sync_mutex) + RECORD_SYNC_MUTEX (sync_mutex); +#endif +} + + +#ifdef KMK +static void +set_make_priority_and_affinity (void) +{ +# ifdef WINDOWS32 + DWORD dwClass, dwPriority; + + if (process_affinity) + if (!SetProcessAffinityMask (GetCurrentProcess (), process_affinity)) + fprintf (stderr, "warning: SetProcessAffinityMask (,%#x) failed with last error %d\n", + process_affinity, GetLastError ()); + + switch (process_priority) + { + case 0: return; + case 1: dwClass = IDLE_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_IDLE; break; + case 2: dwClass = BELOW_NORMAL_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_BELOW_NORMAL; break; + case 3: dwClass = NORMAL_PRIORITY_CLASS; dwPriority = THREAD_PRIORITY_NORMAL; break; + case 4: dwClass = HIGH_PRIORITY_CLASS; dwPriority = 0xffffffff; break; + case 5: dwClass = REALTIME_PRIORITY_CLASS; dwPriority = 0xffffffff; break; + default: ON (fatal, NILF, _("invalid priority %d\n"), process_priority); + } + if (!SetPriorityClass (GetCurrentProcess (), dwClass)) + fprintf (stderr, "warning: SetPriorityClass (,%#x) failed with last error %d\n", + dwClass, GetLastError ()); + if (dwPriority != 0xffffffff + && !SetThreadPriority (GetCurrentThread (), dwPriority)) + fprintf (stderr, "warning: SetThreadPriority (,%#x) failed with last error %d\n", + dwPriority, GetLastError ()); + +#elif defined(__HAIKU__) + int32 iNewPriority; + status_t error; + + switch (process_priority) + { + case 0: return; + case 1: iNewPriority = B_LOWEST_ACTIVE_PRIORITY; break; + case 2: iNewPriority = B_LOW_PRIORITY; break; + case 3: iNewPriority = B_NORMAL_PRIORITY; break; + case 4: iNewPriority = B_URGENT_DISPLAY_PRIORITY; break; + case 5: iNewPriority = B_REAL_TIME_DISPLAY_PRIORITY; break; + default: ON (fatal, NILF, _("invalid priority %d\n"), process_priority); + } + error = set_thread_priority (find_thread (NULL), iNewPriority); + if (error != B_OK) + fprintf (stderr, "warning: set_thread_priority (,%d) failed: %s\n", + iNewPriority, strerror (error)); + +# else /*#elif HAVE_NICE */ + int nice_level = 0; + switch (process_priority) + { + case 0: return; + case 1: nice_level = 19; break; + case 2: nice_level = 10; break; + case 3: nice_level = 0; break; + case 4: nice_level = -10; break; + case 5: nice_level = -19; break; + default: ON (fatal, NILF, _("invalid priority %d\n"), process_priority); + } + errno = 0; + if (nice (nice_level) == -1 && errno != 0) + fprintf (stderr, "warning: nice (%d) failed: %s\n", + nice_level, strerror (errno)); +# endif +} +#endif /* KMK */ + + +#ifdef WINDOWS32 + +#ifndef NO_OUTPUT_SYNC + +/* This is called from start_job_command when it detects that + output_sync option is in effect. The handle to the synchronization + mutex is passed, as a string, to sub-makes via the --sync-mutex + command-line argument. */ +void +# ifdef CONFIG_NEW_WIN_CHILDREN +prepare_mutex_handle_string (const char *mtxname) +{ + if (!sync_mutex) + { + sync_mutex = xstrdup(mtxname); + define_makeflags (1, 0); + } +} +# else +prepare_mutex_handle_string (sync_handle_t handle) +{ + if (!sync_mutex) + { + /* Prepare the mutex handle string for our children. */ + /* 2 hex digits per byte + 2 characters for "0x" + null. */ + sync_mutex = xmalloc ((2 * sizeof (sync_handle_t)) + 2 + 1); + sprintf (sync_mutex, "0x%Ix", handle); + define_makeflags (1, 0); + } +} +# endif + +#endif /* NO_OUTPUT_SYNC */ + +# ifndef KMK /* I'd rather have WER collect dumps. */ +/* + * HANDLE runtime exceptions by avoiding a requestor on the GUI. Capture + * exception and print it to stderr instead. + * + * If ! DB_VERBOSE, just print a simple message and exit. + * If DB_VERBOSE, print a more verbose message. + * If compiled for DEBUG, let exception pass through to GUI so that + * debuggers can attach. + */ +LONG WINAPI +handle_runtime_exceptions (struct _EXCEPTION_POINTERS *exinfo) +{ + PEXCEPTION_RECORD exrec = exinfo->ExceptionRecord; + LPSTR cmdline = GetCommandLine (); + LPSTR prg = strtok (cmdline, " "); + CHAR errmsg[1024]; +#ifdef USE_EVENT_LOG + HANDLE hEventSource; + LPTSTR lpszStrings[1]; +#endif + + if (! ISDB (DB_VERBOSE)) + { + sprintf (errmsg, + _("%s: Interrupt/Exception caught (code = 0x%lx, addr = 0x%p)\n"), + prg, exrec->ExceptionCode, exrec->ExceptionAddress); + fprintf (stderr, errmsg); + exit (255); + } + + sprintf (errmsg, + _("\nUnhandled exception filter called from program %s\nExceptionCode = %lx\nExceptionFlags = %lx\nExceptionAddress = 0x%p\n"), + prg, exrec->ExceptionCode, exrec->ExceptionFlags, + exrec->ExceptionAddress); + + if (exrec->ExceptionCode == EXCEPTION_ACCESS_VIOLATION + && exrec->NumberParameters >= 2) + sprintf (&errmsg[strlen(errmsg)], + (exrec->ExceptionInformation[0] + ? _("Access violation: write operation at address 0x%p\n") + : _("Access violation: read operation at address 0x%p\n")), + (PVOID)exrec->ExceptionInformation[1]); + + /* turn this on if we want to put stuff in the event log too */ +#ifdef USE_EVENT_LOG + hEventSource = RegisterEventSource (NULL, "GNU Make"); + lpszStrings[0] = errmsg; + + if (hEventSource != NULL) + { + ReportEvent (hEventSource, /* handle of event source */ + EVENTLOG_ERROR_TYPE, /* event type */ + 0, /* event category */ + 0, /* event ID */ + NULL, /* current user's SID */ + 1, /* strings in lpszStrings */ + 0, /* no bytes of raw data */ + lpszStrings, /* array of error strings */ + NULL); /* no raw data */ + + (VOID) DeregisterEventSource (hEventSource); + } +#endif + + /* Write the error to stderr too */ + fprintf (stderr, errmsg); + +#ifdef DEBUG + return EXCEPTION_CONTINUE_SEARCH; +#else + exit (255); + return (255); /* not reached */ +#endif +} +# endif /* !KMK */ + +/* + * On WIN32 systems we don't have the luxury of a /bin directory that + * is mapped globally to every drive mounted to the system. Since make could + * be invoked from any drive, and we don't want to propagate /bin/sh + * to every single drive. Allow ourselves a chance to search for + * a value for default shell here (if the default path does not exist). + */ + +int +find_and_set_default_shell (const char *token) +{ + int sh_found = 0; + char *atoken = 0; + const char *search_token; + const char *tokend; + PATH_VAR(sh_path); + extern const char *default_shell; + + if (!token) + search_token = default_shell; + else + search_token = atoken = xstrdup (token); + + /* If the user explicitly requests the DOS cmd shell, obey that request. + However, make sure that's what they really want by requiring the value + of SHELL either equal, or have a final path element of, "cmd" or + "cmd.exe" case-insensitive. */ + tokend = search_token + strlen (search_token) - 3; + if (((tokend == search_token + || (tokend > search_token + && (tokend[-1] == '/' || tokend[-1] == '\\'))) + && !strcasecmp (tokend, "cmd")) + || ((tokend - 4 == search_token + || (tokend - 4 > search_token + && (tokend[-5] == '/' || tokend[-5] == '\\'))) + && !strcasecmp (tokend - 4, "cmd.exe"))) + { + batch_mode_shell = 1; + unixy_shell = 0; +# if 1 /* bird: sprintf? wtf. */ + default_shell = unix_slashes (xstrdup (search_token)); +# else + sprintf (sh_path, "%s", search_token); + default_shell = xstrdup (w32ify (sh_path, 0)); +# endif + DB (DB_VERBOSE, (_("find_and_set_shell() setting default_shell = %s\n"), + default_shell)); + sh_found = 1; + } + else if (!no_default_sh_exe + && (token == NULL || !strcmp (search_token, default_shell))) + { + /* no new information, path already set or known */ + sh_found = 1; + } + else if (_access (search_token, 0) == 0) + { + /* search token path was found */ +# if 1 /* bird: sprintf? wtf. */ + default_shell = unix_slashes (xstrdup (search_token)); +# else + sprintf (sh_path, "%s", search_token); + default_shell = xstrdup (w32ify (sh_path, 0)); +# endif + DB (DB_VERBOSE, (_("find_and_set_shell() setting default_shell = %s\n"), + default_shell)); + sh_found = 1; + } + else + { + char *p; + struct variable *v = lookup_variable (STRING_SIZE_TUPLE ("PATH")); + + /* Search Path for shell */ + if (v && v->value) + { + char *ep; + + p = v->value; + ep = strchr (p, PATH_SEPARATOR_CHAR); + + while (ep && *ep) + { + *ep = '\0'; + +# if 1 /* bird: insanity insurance */ + _snprintf (sh_path, GET_PATH_MAX, "%s/%s", p, search_token); +# else + sprintf (sh_path, "%s/%s", p, search_token); +# endif + if (_access (sh_path, 0) == 0) + { +# if 1 /* bird: we can modify sh_path directly. */ + default_shell = xstrdup (unix_slashes (sh_path)); +# else + default_shell = xstrdup (w32ify (sh_path, 0)); +# endif + sh_found = 1; + *ep = PATH_SEPARATOR_CHAR; + + /* terminate loop */ + p += strlen (p); + } + else + { + *ep = PATH_SEPARATOR_CHAR; + p = ++ep; + } + + ep = strchr (p, PATH_SEPARATOR_CHAR); + } + + /* be sure to check last element of Path */ + if (p && *p) + { +# if 1 /* bird: insanity insurance */ + _snprintf (sh_path, GET_PATH_MAX, "%s/%s", p, search_token); +# else + sprintf (sh_path, "%s/%s", p, search_token); +# endif + if (_access (sh_path, 0) == 0) + { +# if 1 /* bird: we can modify sh_path directly. */ + default_shell = xstrdup (unix_slashes (sh_path)); +# else + default_shell = xstrdup (w32ify (sh_path, 0)); +# endif + sh_found = 1; + } + } + + if (sh_found) + DB (DB_VERBOSE, + (_("find_and_set_shell() path search set default_shell = %s\n"), + default_shell)); + } + } + + /* naive test */ + if (!unixy_shell && sh_found + && (strstr (default_shell, "sh") || strstr (default_shell, "SH"))) + { + unixy_shell = 1; + batch_mode_shell = 0; + } + +#ifdef BATCH_MODE_ONLY_SHELL + batch_mode_shell = 1; +#endif + + free (atoken); + + return (sh_found); +} + +/* bird: */ +#ifdef CONFIG_NEW_WIN32_CTRL_EVENT +#include <process.h> +static UINT g_tidMainThread = 0; +static int volatile g_sigPending = 0; /* lazy bird */ +# ifndef _M_IX86 +static LONG volatile g_lTriggered = 0; +static CONTEXT g_Ctx; +# endif + +# ifdef _M_IX86 +static __declspec(naked) void dispatch_stub(void) +{ + __asm { + pushfd + pushad + cld + } + fflush(stdout); + /*fprintf(stderr, "dbg: raising %s on the main thread (%d)\n", g_sigPending == SIGINT ? "SIGINT" : "SIGBREAK", _getpid());*/ + raise(g_sigPending); + __asm { + popad + popfd + ret + } +} +# else /* !_M_IX86 */ +static void dispatch_stub(void) +{ + fflush(stdout); + /*fprintf(stderr, "dbg: raising %s on the main thread (%d)\n", g_sigPending == SIGINT ? "SIGINT" : "SIGBREAK", _getpid());*/ + raise(g_sigPending); + + SetThreadContext(GetCurrentThread(), &g_Ctx); + fprintf(stderr, "fatal error: SetThreadContext failed with last error %d\n", GetLastError()); + for (;;) + exit(131); +} +# endif /* !_M_IX86 */ + +static BOOL WINAPI ctrl_event(DWORD CtrlType) +{ + int sig = (CtrlType == CTRL_C_EVENT) ? SIGINT : SIGBREAK; + HANDLE hThread; + CONTEXT Ctx; + + /*fprintf(stderr, "dbg: ctrl_event sig=%d\n", sig);*/ +#ifndef _M_IX86 + /* only once. */ + if (InterlockedExchange(&g_lTriggered, 1)) + { + Sleep(1); + return TRUE; + } +#endif + + /* open the main thread and suspend it. */ + hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, g_tidMainThread); + SuspendThread(hThread); + + /* Get the thread context and if we've get a valid Esp, dispatch + it on the main thread otherwise raise the signal in the + ctrl-event thread (this). */ + memset(&Ctx, 0, sizeof(Ctx)); + Ctx.ContextFlags = CONTEXT_FULL; + if (GetThreadContext(hThread, &Ctx) +#ifdef _M_IX86 + && Ctx.Esp >= 0x1000 +#else + && Ctx.Rsp >= 0x1000 +#endif + ) + { +#ifdef _M_IX86 + ((uintptr_t *)Ctx.Esp)[-1] = Ctx.Eip; + Ctx.Esp -= sizeof(uintptr_t); + Ctx.Eip = (uintptr_t)&dispatch_stub; +#else + g_Ctx = Ctx; + Ctx.Rsp -= 0x80; + Ctx.Rsp &= ~(uintptr_t)0xf; + Ctx.Rsp += 8; /* (Stack aligned before call instruction, not after.) */ + Ctx.Rip = (uintptr_t)&dispatch_stub; +#endif + + SetThreadContext(hThread, &Ctx); + g_sigPending = sig; + ResumeThread(hThread); + CloseHandle(hThread); + } + else + { + fprintf(stderr, "dbg: raising %s on the ctrl-event thread (%d)\n", sig == SIGINT ? "SIGINT" : "SIGBREAK", _getpid()); + raise(sig); + ResumeThread(hThread); + CloseHandle(hThread); + exit(130); + } + + Sleep(1); + return TRUE; +} +#endif /* CONFIG_NEW_WIN32_CTRL_EVENT */ + +#endif /* WINDOWS32 */ + +#ifdef KMK +/* Determins the number of CPUs that are currently online. + This is used to setup the default number of job slots. */ +static int +get_online_cpu_count(void) +{ +# ifdef WINDOWS32 + /* Windows: Count the active CPUs. */ + int cpus; + + /* Process groups (W7+). */ + typedef DWORD (WINAPI *PFNGETACTIVEPROCESSORCOUNT)(DWORD); + PFNGETACTIVEPROCESSORCOUNT pfnGetActiveProcessorCount; + pfnGetActiveProcessorCount = (PFNGETACTIVEPROCESSORCOUNT)GetProcAddress(GetModuleHandleW(L"kernel32.dll"), + "GetActiveProcessorCount"); + if (pfnGetActiveProcessorCount) + cpus = pfnGetActiveProcessorCount(ALL_PROCESSOR_GROUPS); + /* Legacy (<= Vista). */ + else + { + int i; + SYSTEM_INFO si; + GetSystemInfo(&si); + for (i = cpus = 0; i < sizeof(si.dwActiveProcessorMask) * 8; i++) + { + if (si.dwActiveProcessorMask & 1) + cpus++; + si.dwActiveProcessorMask >>= 1; + } + } + if (!cpus) + cpus = 1; +# ifndef CONFIG_NEW_WIN_CHILDREN + if (cpus > 64) + cpus = 64; /* (wait for multiple objects limit) */ +# endif + return cpus; + +# elif defined(__OS2__) + /* OS/2: Count the active CPUs. */ + int cpus, i, j; + MPAFFINITY mp; + if (DosQueryThreadAffinity(AFNTY_SYSTEM, &mp)) + return 1; + for (j = cpus = 0; j < sizeof(mp.mask) / sizeof(mp.mask[0]); j++) + for (i = 0; i < 32; i++) + if (mp.mask[j] & (1UL << i)) + cpus++; + return cpus ? cpus : 1; + +# else + /* UNIX like systems, try sysconf and sysctl. */ + int cpus = -1; +# if defined(CTL_HW) + int mib[2]; + size_t sz; +# endif + +# ifdef _SC_NPROCESSORS_ONLN + cpus = sysconf(_SC_NPROCESSORS_ONLN); + if (cpus >= 1) + return cpus; + cpus = -1; +# endif + +# if defined(CTL_HW) +# ifdef HW_AVAILCPU + sz = sizeof(cpus); + mib[0] = CTL_HW; + mib[1] = HW_AVAILCPU; + if (!sysctl(mib, 2, &cpus, &sz, NULL, 0) + && cpus >= 1) + return cpus; + cpus = -1; +# endif /* HW_AVAILCPU */ + + sz = sizeof(cpus); + mib[0] = CTL_HW; + mib[1] = HW_NCPU; + if (!sysctl(mib, 2, &cpus, &sz, NULL, 0) + && cpus >= 1) + return cpus; + cpus = -1; +# endif /* CTL_HW */ + + /* no idea / failure, just return 1. */ + return 1; +# endif +} +#endif /* KMK */ + +#ifdef __MSDOS__ +static void +msdos_return_to_initial_directory (void) +{ + if (directory_before_chdir) + chdir (directory_before_chdir); +} +#endif /* __MSDOS__ */ + +static void +reset_jobserver (void) +{ + jobserver_clear (); + free (jobserver_auth); + jobserver_auth = NULL; +} + +#ifdef _AMIGA +int +main (int argc, char **argv) +#else +int +main (int argc, char **argv, char **envp) +#endif +{ + static char *stdin_nm = 0; +#ifdef CONFIG_WITH_MAKE_STATS + unsigned long long uStartTick = CURRENT_CLOCK_TICK(); +#endif + int makefile_status = MAKE_SUCCESS; + struct goaldep *read_files; + PATH_VAR (current_directory); + unsigned int restarts = 0; + unsigned int syncing = 0; + int argv_slots; +#ifdef WINDOWS32 + const char *unix_path = NULL; + const char *windows32_path = NULL; + +# ifndef ELECTRIC_HEAP /* Drop this because it prevents JIT debugging. */ +# ifndef KMK /* Don't want none of this crap. */ + SetUnhandledExceptionFilter (handle_runtime_exceptions); +# endif +# endif /* !ELECTRIC_HEAP */ + +# ifdef KMK + /* Clear the SEM_NOGPFAULTERRORBOX flag so WER will generate dumps when we run + under cygwin. To void popups, set WER registry value DontShowUI to 1. */ + if (getenv("KMK_NO_SET_ERROR_MODE") == NULL) + SetErrorMode(SetErrorMode(0) & ~SEM_NOGPFAULTERRORBOX); +# endif + + /* start off assuming we have no shell */ + unixy_shell = 0; + no_default_sh_exe = 1; +#endif +#ifdef CONFIG_WITH_PRINT_TIME_SWITCH + make_start_ts = nano_timestamp (); +#endif + + output_init (&make_sync); + + initialize_stopchar_map(); + +#ifdef SET_STACK_SIZE + /* Get rid of any avoidable limit on stack size. */ + { + struct rlimit rlim; + + /* Set the stack limit huge so that alloca does not fail. */ + if (getrlimit (RLIMIT_STACK, &rlim) == 0 + && rlim.rlim_cur > 0 && rlim.rlim_cur < rlim.rlim_max) + { + stack_limit = rlim; + rlim.rlim_cur = rlim.rlim_max; + setrlimit (RLIMIT_STACK, &rlim); + } + else + stack_limit.rlim_cur = 0; + } +#endif + + /* Needed for OS/2 */ + initialize_main (&argc, &argv); + +#ifdef KMK + init_kbuild (argc, argv); +#endif + +#ifdef MAKE_MAINTAINER_MODE + /* In maintainer mode we always enable verification. */ + verify_flag = 1; +#endif + +#if defined (__MSDOS__) && !defined (_POSIX_SOURCE) + /* Request the most powerful version of 'system', to + make up for the dumb default shell. */ + __system_flags = (__system_redirect + | __system_use_shell + | __system_allow_multiple_cmds + | __system_allow_long_cmds + | __system_handle_null_commands + | __system_emulate_chdir); + +#endif + + /* Set up gettext/internationalization support. */ + setlocale (LC_ALL, ""); + /* The cast to void shuts up compiler warnings on systems that + disable NLS. */ +#ifdef LOCALEDIR /* bird */ + (void)bindtextdomain (PACKAGE, LOCALEDIR); + (void)textdomain (PACKAGE); +#endif + +#ifdef POSIX + sigemptyset (&fatal_signal_set); +#define ADD_SIG(sig) sigaddset (&fatal_signal_set, sig) +#else +#ifdef HAVE_SIGSETMASK + fatal_signal_mask = 0; +#define ADD_SIG(sig) fatal_signal_mask |= sigmask (sig) +#else +#define ADD_SIG(sig) (void)sig +#endif +#endif + +#define FATAL_SIG(sig) \ + if (bsd_signal (sig, fatal_error_signal) == SIG_IGN) \ + bsd_signal (sig, SIG_IGN); \ + else \ + ADD_SIG (sig); + +#ifdef SIGHUP + FATAL_SIG (SIGHUP); +#endif +#ifdef SIGQUIT + FATAL_SIG (SIGQUIT); +#endif + FATAL_SIG (SIGINT); + FATAL_SIG (SIGTERM); + +#ifdef __MSDOS__ + /* Windows 9X delivers FP exceptions in child programs to their + parent! We don't want Make to die when a child divides by zero, + so we work around that lossage by catching SIGFPE. */ + FATAL_SIG (SIGFPE); +#endif + +#ifdef SIGDANGER + FATAL_SIG (SIGDANGER); +#endif +#ifdef SIGXCPU + FATAL_SIG (SIGXCPU); +#endif +#ifdef SIGXFSZ + FATAL_SIG (SIGXFSZ); +#endif + +#ifdef KMK + /* Get the incoming umask so we don't have to modify it later to get it. */ + umask(g_fUMask = umask(0077)); +#endif + +#ifdef CONFIG_NEW_WIN32_CTRL_EVENT + /* bird: dispatch signals in our own way to try avoid deadlocks. */ + g_tidMainThread = GetCurrentThreadId (); + SetConsoleCtrlHandler (ctrl_event, TRUE); +#endif /* CONFIG_NEW_WIN32_CTRL_EVENT */ + +#undef FATAL_SIG + + /* Do not ignore the child-death signal. This must be done before + any children could possibly be created; otherwise, the wait + functions won't work on systems with the SVR4 ECHILD brain + damage, if our invoker is ignoring this signal. */ + +#ifdef HAVE_WAIT_NOHANG +# if defined SIGCHLD + (void) bsd_signal (SIGCHLD, SIG_DFL); +# endif +# if defined SIGCLD && SIGCLD != SIGCHLD + (void) bsd_signal (SIGCLD, SIG_DFL); +# endif +#endif + + output_init (NULL); + + /* Figure out where this program lives. */ + + if (argv[0] == 0) + argv[0] = (char *)""; + if (argv[0][0] == '\0') +#ifdef KMK + program = "kmk"; +#else + program = "make"; +#endif + else + { + program = strrchr (argv[0], '/'); +#if defined(__MSDOS__) || defined(__EMX__) + if (program == 0) + program = strrchr (argv[0], '\\'); + else + { + /* Some weird environments might pass us argv[0] with + both kinds of slashes; we must find the rightmost. */ + char *p = strrchr (argv[0], '\\'); + if (p && p > program) + program = p; + } + if (program == 0 && argv[0][1] == ':') + program = argv[0] + 1; +#endif +#ifdef WINDOWS32 + if (program == 0) + { + /* Extract program from full path */ + program = strrchr (argv[0], '\\'); + if (program) + { + int argv0_len = strlen (program); + if (argv0_len > 4 && streq (&program[argv0_len - 4], ".exe")) + /* Remove .exe extension */ + program[argv0_len - 4] = '\0'; + } + } +#endif +#ifdef VMS + set_program_name (argv[0]); + program = program_name; + { + const char *shell; + char pwdbuf[256]; + char *pwd; + shell = getenv ("SHELL"); + if (shell != NULL) + vms_gnv_shell = 1; + + /* Need to know if CRTL set to report UNIX paths. Use getcwd as + it works on all versions of VMS. */ + pwd = getcwd(pwdbuf, 256); + if (pwd[0] == '/') + vms_report_unix_paths = 1; + + vms_use_mcr_command = get_vms_env_flag ("GNV$MAKE_USE_MCR", 0); + + vms_always_use_cmd_file = get_vms_env_flag ("GNV$MAKE_USE_CMD_FILE", 0); + + /* Legacy behavior is on VMS is older behavior that needed to be + changed to be compatible with standard make behavior. + For now only completely disable when running under a Bash shell. + TODO: Update VMS built in recipes and macros to not need this + behavior, at which time the default may change. */ + vms_legacy_behavior = get_vms_env_flag ("GNV$MAKE_OLD_VMS", + !vms_gnv_shell); + + /* VMS was changed to use a comma separator in the past, but that is + incompatible with built in functions that expect space separated + lists. Allow this to be selectively turned off. */ + vms_comma_separator = get_vms_env_flag ("GNV$MAKE_COMMA", + vms_legacy_behavior); + + /* Some Posix shell syntax options are incompatible with VMS syntax. + VMS requires double quotes for strings and escapes quotes + differently. When this option is active, VMS will try + to simulate Posix shell simulations instead of using + VMS DCL behavior. */ + vms_unix_simulation = get_vms_env_flag ("GNV$MAKE_SHELL_SIM", + !vms_legacy_behavior); + + } + if (need_vms_symbol () && !vms_use_mcr_command) + create_foreign_command (program_name, argv[0]); +#else + if (program == 0) + program = argv[0]; + else + ++program; +#endif + } + + /* Set up to access user data (files). */ + user_access (); + +# ifdef CONFIG_WITH_COMPILER + kmk_cc_init (); +# endif +#ifdef CONFIG_WITH_ALLOC_CACHES + initialize_global_alloc_caches (); +#endif + initialize_global_hash_tables (); +#ifdef KMK + init_kbuild_object (); +#endif + + /* Figure out where we are. */ + +#ifdef WINDOWS32 + if (getcwd_fs (current_directory, GET_PATH_MAX) == 0) +#else + if (getcwd (current_directory, GET_PATH_MAX) == 0) +#endif + { +#ifdef HAVE_GETCWD + perror_with_name ("getcwd", ""); +#else + OS (error, NILF, "getwd: %s", current_directory); +#endif + current_directory[0] = '\0'; + directory_before_chdir = 0; + } + else + directory_before_chdir = xstrdup (current_directory); + +#ifdef __MSDOS__ + /* Make sure we will return to the initial directory, come what may. */ + atexit (msdos_return_to_initial_directory); +#endif + + /* Initialize the special variables. */ + define_variable_cname (".VARIABLES", "", o_default, 0)->special = 1; + /* define_variable_cname (".TARGETS", "", o_default, 0)->special = 1; */ + define_variable_cname (".RECIPEPREFIX", "", o_default, 0)->special = 1; + define_variable_cname (".SHELLFLAGS", "-c", o_default, 0); + define_variable_cname (".LOADED", "", o_default, 0); + + /* Set up .FEATURES + Use a separate variable because define_variable_cname() is a macro and + some compilers (MSVC) don't like conditionals in macros. */ + { + const char *features = "target-specific order-only second-expansion" + " else-if shortest-stem undefine oneshell" +#ifndef NO_ARCHIVES + " archives" +#endif +#ifdef MAKE_JOBSERVER + " jobserver" +#endif +#ifndef NO_OUTPUT_SYNC + " output-sync" +#endif +#ifdef MAKE_SYMLINKS + " check-symlink" +#endif +#ifdef HAVE_GUILE + " guile" +#endif +#ifdef MAKE_LOAD + " load" +#endif +#ifdef CONFIG_WITH_EXPLICIT_MULTITARGET + " explicit-multitarget" +#endif +#ifdef CONFIG_WITH_PREPEND_ASSIGNMENT + " prepend-assignment" +#endif + ; + + define_variable_cname (".FEATURES", features, o_default, 0); + } + +#ifdef KMK + /* Initialize the default number of jobs to the cpu/core/smt count. */ + default_job_slots = arg_job_slots = job_slots = get_online_cpu_count (); +#endif /* KMK */ + + /* Configure GNU Guile support */ + guile_gmake_setup (NILF); + + /* Read in variables from the environment. It is important that this be + done before $(MAKE) is figured out so its definitions will not be + from the environment. */ + +#ifndef _AMIGA + { + unsigned int i; + + for (i = 0; envp[i] != 0; ++i) + { + struct variable *v; + const char *ep = envp[i]; + /* By default, export all variables culled from the environment. */ + enum variable_export export = v_export; + unsigned int len; + + while (! STOP_SET (*ep, MAP_EQUALS)) + ++ep; + + /* If there's no equals sign it's a malformed environment. Ignore. */ + if (*ep == '\0') + continue; + +#ifdef WINDOWS32 + if (!unix_path && strneq (envp[i], "PATH=", 5)) + unix_path = ep+1; + else if (!strnicmp (envp[i], "Path=", 5)) + { + if (!windows32_path) + windows32_path = ep+1; + /* PATH gets defined after the loop exits. */ + continue; + } +#endif + + /* Length of the variable name, and skip the '='. */ + len = ep++ - envp[i]; + + /* If this is MAKE_RESTARTS, check to see if the "already printed + the enter statement" flag is set. */ + if (len == 13 && strneq (envp[i], "MAKE_RESTARTS", 13)) + { + if (*ep == '-') + { + OUTPUT_TRACED (); + ++ep; + } + restarts = (unsigned int) atoi (ep); + export = v_noexport; + } + + v = define_variable (envp[i], len, ep, o_env, 1); + + /* POSIX says the value of SHELL set in the makefile won't change the + value of SHELL given to subprocesses. */ + if (streq (v->name, "SHELL")) + { +#ifndef __MSDOS__ + export = v_noexport; +#endif +#ifndef CONFIG_WITH_STRCACHE2 + shell_var.name = xstrdup ("SHELL"); +#else + shell_var.name = v->name; +#endif + shell_var.length = 5; +#ifndef CONFIG_WITH_VALUE_LENGTH + shell_var.value = xstrdup (ep); +#else + shell_var.value = xstrndup (v->value, v->value_length); + shell_var.value_length = v->value_length; +#endif + } + + v->export = export; + } + } +#ifdef WINDOWS32 + /* If we didn't find a correctly spelled PATH we define PATH as + * either the first misspelled value or an empty string + */ + if (!unix_path) + define_variable_cname ("PATH", windows32_path ? windows32_path : "", + o_env, 1)->export = v_export; +#endif +#else /* For Amiga, read the ENV: device, ignoring all dirs */ + { + BPTR env, file, old; + char buffer[1024]; + int len; + __aligned struct FileInfoBlock fib; + + env = Lock ("ENV:", ACCESS_READ); + if (env) + { + old = CurrentDir (DupLock (env)); + Examine (env, &fib); + + while (ExNext (env, &fib)) + { + if (fib.fib_DirEntryType < 0) /* File */ + { + /* Define an empty variable. It will be filled in + variable_lookup(). Makes startup quite a bit faster. */ + define_variable (fib.fib_FileName, + strlen (fib.fib_FileName), + "", o_env, 1)->export = v_export; + } + } + UnLock (env); + UnLock (CurrentDir (old)); + } + } +#endif + + /* Decode the switches. */ + decode_env_switches (STRING_SIZE_TUPLE ("GNUMAKEFLAGS")); + + /* Clear GNUMAKEFLAGS to avoid duplication. */ + define_variable_cname ("GNUMAKEFLAGS", "", o_env, 0); + +#ifdef KMK + decode_env_switches (STRING_SIZE_TUPLE ("KMK_FLAGS")); +#else /* !KMK */ + decode_env_switches (STRING_SIZE_TUPLE ("MAKEFLAGS")); + +#if 0 + /* People write things like: + MFLAGS="CC=gcc -pipe" "CFLAGS=-g" + and we set the -p, -i and -e switches. Doesn't seem quite right. */ + decode_env_switches (STRING_SIZE_TUPLE ("MFLAGS")); +#endif +#endif /* !KMK */ + + /* In output sync mode we need to sync any output generated by reading the + makefiles, such as in $(info ...) or stderr from $(shell ...) etc. */ + + syncing = make_sync.syncout = (output_sync == OUTPUT_SYNC_LINE + || output_sync == OUTPUT_SYNC_TARGET); + OUTPUT_SET (&make_sync); + + /* Remember the job slots set through the environment vs. command line. */ + { + int env_slots = arg_job_slots; + arg_job_slots = INVALID_JOB_SLOTS; + + decode_switches (argc, (const char **)argv, 0); + argv_slots = arg_job_slots; + + if (arg_job_slots == INVALID_JOB_SLOTS) + arg_job_slots = env_slots; + } + + /* Set a variable specifying whether stdout/stdin is hooked to a TTY. */ +#ifdef HAVE_ISATTY + if (isatty (fileno (stdout))) + if (! lookup_variable (STRING_SIZE_TUPLE ("MAKE_TERMOUT"))) + { + const char *tty = TTYNAME (fileno (stdout)); + define_variable_cname ("MAKE_TERMOUT", tty ? tty : DEFAULT_TTYNAME, + o_default, 0)->export = v_export; + } + if (isatty (fileno (stderr))) + if (! lookup_variable (STRING_SIZE_TUPLE ("MAKE_TERMERR"))) + { + const char *tty = TTYNAME (fileno (stderr)); + define_variable_cname ("MAKE_TERMERR", tty ? tty : DEFAULT_TTYNAME, + o_default, 0)->export = v_export; + } +#endif + + /* Reset in case the switches changed our minds. */ + syncing = (output_sync == OUTPUT_SYNC_LINE + || output_sync == OUTPUT_SYNC_TARGET); + +#ifdef KMK + set_make_priority_and_affinity (); +#endif + + if (make_sync.syncout && ! syncing) + output_close (&make_sync); + + make_sync.syncout = syncing; + OUTPUT_SET (&make_sync); + + /* Figure out the level of recursion. */ + { + struct variable *v = lookup_variable (STRING_SIZE_TUPLE (MAKELEVEL_NAME)); + if (v && v->value[0] != '\0' && v->value[0] != '-') + makelevel = (unsigned int) atoi (v->value); + else + makelevel = 0; + } + +#ifdef WINDOWS32 + if (suspend_flag) + { + fprintf (stderr, "%s (pid = %ld)\n", argv[0], GetCurrentProcessId ()); + fprintf (stderr, _("%s is suspending for 30 seconds..."), argv[0]); + Sleep (30 * 1000); + fprintf (stderr, _("done sleep(30). Continuing.\n")); + } +#endif + + /* Set always_make_flag if -B was given and we've not restarted already. */ + always_make_flag = always_make_set && (restarts == 0); + + /* Print version information, and exit. */ + if (print_version_flag) + { + print_version (); + die (MAKE_SUCCESS); + } + + if (ISDB (DB_BASIC)) + print_version (); + +#ifndef VMS + /* Set the "MAKE_COMMAND" variable to the name we were invoked with. + (If it is a relative pathname with a slash, prepend our directory name + so the result will run the same program regardless of the current dir. + If it is a name with no slash, we can only hope that PATH did not + find it in the current directory.) */ +#ifdef WINDOWS32 + /* + * Convert from backslashes to forward slashes for + * programs like sh which don't like them. Shouldn't + * matter if the path is one way or the other for + * CreateProcess(). + */ + if (strpbrk (argv[0], "/:\\") || strstr (argv[0], "..") + || strneq (argv[0], "//", 2)) +# if 1 /* bird */ + { + PATH_VAR (tmp_path_buf); + argv[0] = xstrdup (unix_slashes_resolved (argv[0], tmp_path_buf, + GET_PATH_MAX)); + } +# else /* bird */ + //argv[0] = xstrdup (w32ify (argv[0], 1)); +# endif /* bird */ +#else /* WINDOWS32 */ +#if defined (__MSDOS__) || defined (__EMX__) + if (strchr (argv[0], '\\')) + { + char *p; + + argv[0] = xstrdup (argv[0]); + for (p = argv[0]; *p; p++) + if (*p == '\\') + *p = '/'; + } + /* If argv[0] is not in absolute form, prepend the current + directory. This can happen when Make is invoked by another DJGPP + program that uses a non-absolute name. */ + if (current_directory[0] != '\0' + && argv[0] != 0 + && (argv[0][0] != '/' && (argv[0][0] == '\0' || argv[0][1] != ':')) +# ifdef __EMX__ + /* do not prepend cwd if argv[0] contains no '/', e.g. "make" */ + && (strchr (argv[0], '/') != 0 || strchr (argv[0], '\\') != 0) +# endif + ) + argv[0] = xstrdup (concat (3, current_directory, "/", argv[0])); +#else /* !__MSDOS__ */ + if (current_directory[0] != '\0' + && argv[0] != 0 && argv[0][0] != '/' && strchr (argv[0], '/') != 0 +#ifdef HAVE_DOS_PATHS + && (argv[0][0] != '\\' && (!argv[0][0] || argv[0][1] != ':')) + && strchr (argv[0], '\\') != 0 +#endif + ) + argv[0] = xstrdup (concat (3, current_directory, "/", argv[0])); +#endif /* !__MSDOS__ */ +#endif /* WINDOWS32 */ +#endif + + /* We may move, but until we do, here we are. */ + starting_directory = current_directory; + + /* Set up the job_slots value and the jobserver. This can't be usefully set + in the makefile, and we want to verify the authorization is valid before + make has a chance to start using it for something else. */ + + if (jobserver_auth) + { + if (argv_slots == INVALID_JOB_SLOTS) + { + if (jobserver_parse_auth (jobserver_auth)) + { + /* Success! Use the jobserver. */ + job_slots = 0; + goto job_setup_complete; + } + + O (error, NILF, _("warning: jobserver unavailable: using -j1. Add '+' to parent make rule.")); + arg_job_slots = 1; + } + + /* The user provided a -j setting on the command line: use it. */ + else if (!restarts) + /* If restarts is >0 we already printed this message. */ + O (error, NILF, + _("warning: -jN forced in submake: disabling jobserver mode.")); + + /* We failed to use our parent's jobserver. */ + reset_jobserver (); + job_slots = (unsigned int)arg_job_slots; + } + else if (arg_job_slots == INVALID_JOB_SLOTS) +#ifdef KMK + job_slots = default_job_slots; /* The default is set to CPU count early in main. */ +#else + /* The default is one job at a time. */ + job_slots = 1; +#endif + else + /* Use whatever was provided. */ + job_slots = (unsigned int)arg_job_slots; + + job_setup_complete: + +#if defined (WINDOWS32) && defined(CONFIG_NEW_WIN_CHILDREN) + /* Initialize the windows child management. */ + MkWinChildInit(job_slots); +#endif + + /* The extra indirection through $(MAKE_COMMAND) is done + for hysterical raisins. */ + +#ifdef VMS + if (vms_use_mcr_command) + define_variable_cname ("MAKE_COMMAND", vms_command (argv[0]), o_default, 0); + else + define_variable_cname ("MAKE_COMMAND", program, o_default, 0); +#else + define_variable_cname ("MAKE_COMMAND", argv[0], o_default, 0); +#endif + define_variable_cname ("MAKE", "$(MAKE_COMMAND)", o_default, 1); +#ifdef KMK + (void) define_variable ("KMK", 3, argv[0], o_default, 1); +#endif + + if (command_variables != 0) + { + struct command_variable *cv; + struct variable *v; + unsigned int len = 0; + char *value, *p; + + /* Figure out how much space will be taken up by the command-line + variable definitions. */ + for (cv = command_variables; cv != 0; cv = cv->next) + { + v = cv->variable; + len += 2 * strlen (v->name); + if (! v->recursive) + ++len; + ++len; + len += 2 * strlen (v->value); + ++len; + } + + /* Now allocate a buffer big enough and fill it. */ + p = value = alloca (len); + for (cv = command_variables; cv != 0; cv = cv->next) + { + v = cv->variable; + p = quote_for_env (p, v->name); + if (! v->recursive) + *p++ = ':'; + *p++ = '='; + p = quote_for_env (p, v->value); + *p++ = ' '; + } + p[-1] = '\0'; /* Kill the final space and terminate. */ + + /* Define an unchangeable variable with a name that no POSIX.2 + makefile could validly use for its own variable. */ + define_variable_cname ("-*-command-variables-*-", value, o_automatic, 0); + + /* Define the variable; this will not override any user definition. + Normally a reference to this variable is written into the value of + MAKEFLAGS, allowing the user to override this value to affect the + exported value of MAKEFLAGS. In POSIX-pedantic mode, we cannot + allow the user's setting of MAKEOVERRIDES to affect MAKEFLAGS, so + a reference to this hidden variable is written instead. */ +#ifdef KMK + define_variable_cname ("KMK_OVERRIDES", "${-*-command-variables-*-}", + o_env, 1); +#else + define_variable_cname ("MAKEOVERRIDES", "${-*-command-variables-*-}", + o_env, 1); +#endif +#ifdef VMS + vms_export_dcl_symbol ("MAKEOVERRIDES", "${-*-command-variables-*-}"); +#endif + } + + /* If there were -C flags, move ourselves about. */ + if (directories != 0) + { + unsigned int i; + for (i = 0; directories->list[i] != 0; ++i) + { + const char *dir = directories->list[i]; +#ifdef WINDOWS32 + /* WINDOWS32 chdir() doesn't work if the directory has a trailing '/' + But allow -C/ just in case someone wants that. */ + { + char *p = (char *)dir + strlen (dir) - 1; + while (p > dir && (p[0] == '/' || p[0] == '\\')) + --p; + p[1] = '\0'; + } +#endif + if (chdir (dir) < 0) + pfatal_with_name (dir); + } + } + +#ifdef KMK + /* Check for [Mm]akefile.kup and change directory when found. + Makefile.kmk overrides Makefile.kup but not plain Makefile. + If no -C arguments were given, fake one to indicate chdir. */ + if (makefiles == 0) + { + struct stat st; + if (( ( stat ("Makefile.kup", &st) == 0 + && S_ISREG (st.st_mode) ) + || ( stat ("makefile.kup", &st) == 0 + && S_ISREG (st.st_mode) ) ) + && stat ("Makefile.kmk", &st) < 0 + && stat ("makefile.kmk", &st) < 0) + { + static char fake_path[3*16 + 32] = ".."; + char *cur = &fake_path[2]; + int up_levels = 1; + while (up_levels < 16) + { + /* File with higher precedence.s */ + strcpy (cur, "/Makefile.kmk"); + if (stat (fake_path, &st) == 0) + break; + strcpy (cur, "/makefile.kmk"); + if (stat (fake_path, &st) == 0) + break; + + /* the .kup files */ + strcpy (cur, "/Makefile.kup"); + if ( stat (fake_path, &st) != 0 + || !S_ISREG (st.st_mode)) + { + strcpy (cur, "/makefile.kup"); + if ( stat (fake_path, &st) != 0 + || !S_ISREG (st.st_mode)) + break; + } + + /* ok */ + strcpy (cur, "/.."); + cur += 3; + up_levels++; + } + + if (up_levels >= 16) + O (fatal, NILF, _("Makefile.kup recursion is too deep.")); + + /* attempt to change to the directory. */ + *cur = '\0'; + if (chdir (fake_path) < 0) + pfatal_with_name (fake_path); + + /* add the string to the directories. */ + if (!directories) + { + directories = xmalloc (sizeof(*directories)); + directories->list = xmalloc (5 * sizeof (char *)); + directories->max = 5; + directories->idx = 0; + } + else if (directories->idx == directories->max - 1) + { + directories->max += 5; + directories->list = xrealloc ((void *)directories->list, + directories->max * sizeof (char *)); + } + directories->list[directories->idx++] = fake_path; + } + } +#endif /* KMK */ + +#ifdef WINDOWS32 + /* + * THIS BLOCK OF CODE MUST COME AFTER chdir() CALL ABOVE IN ORDER + * TO NOT CONFUSE THE DEPENDENCY CHECKING CODE IN implicit.c. + * + * The functions in dir.c can incorrectly cache information for "." + * before we have changed directory and this can cause file + * lookups to fail because the current directory (.) was pointing + * at the wrong place when it was first evaluated. + */ +#ifdef KMK /* this is really a candidate for all platforms... */ + { + extern const char *default_shell; + const char *bin = get_kbuild_bin_path(); + char *newshell; + size_t len = strlen (bin); + default_shell = newshell = xmalloc (len + sizeof("/kmk_ash.exe")); + memcpy (newshell, bin, len); + strcpy (newshell + len, "/kmk_ash.exe"); + no_default_sh_exe = 0; + batch_mode_shell = 1; + unixy_shell = 1; + } +#else /* !KMK */ + no_default_sh_exe = !find_and_set_default_shell (NULL); +#endif /* !KMK */ +#endif /* WINDOWS32 */ + + /* Except under -s, always do -w in sub-makes and under -C. */ + if (!silent_flag && (directories != 0 || makelevel > 0)) + print_directory_flag = 1; + + /* Let the user disable that with --no-print-directory. */ + if (inhibit_print_directory_flag) + print_directory_flag = 0; + + /* If -R was given, set -r too (doesn't make sense otherwise!) */ + if (no_builtin_variables_flag) + no_builtin_rules_flag = 1; + + /* Construct the list of include directories to search. */ + + construct_include_path (include_directories == 0 + ? 0 : include_directories->list); + + /* If we chdir'ed, figure out where we are now. */ + if (directories) + { +#ifdef WINDOWS32 + if (getcwd_fs (current_directory, GET_PATH_MAX) == 0) +#else + if (getcwd (current_directory, GET_PATH_MAX) == 0) +#endif + { +#ifdef HAVE_GETCWD + perror_with_name ("getcwd", ""); +#else + OS (error, NILF, "getwd: %s", current_directory); +#endif + starting_directory = 0; + } + else + starting_directory = current_directory; + } + + define_variable_cname ("CURDIR", current_directory, o_file, 0); + + /* Read any stdin makefiles into temporary files. */ + + if (makefiles != 0) + { + unsigned int i; + for (i = 0; i < makefiles->idx; ++i) + if (makefiles->list[i][0] == '-' && makefiles->list[i][1] == '\0') + { + /* This makefile is standard input. Since we may re-exec + and thus re-read the makefiles, we read standard input + into a temporary file and read from that. */ + FILE *outfile; + char *template; + const char *tmpdir; + + if (stdin_nm) + O (fatal, NILF, + _("Makefile from standard input specified twice.")); + +#ifdef VMS +# define DEFAULT_TMPDIR "/sys$scratch/" +#else +# ifdef P_tmpdir +# define DEFAULT_TMPDIR P_tmpdir +# else +# define DEFAULT_TMPDIR "/tmp" +# endif +#endif +#define DEFAULT_TMPFILE "GmXXXXXX" + + if (((tmpdir = getenv ("TMPDIR")) == NULL || *tmpdir == '\0') +#if defined (__MSDOS__) || defined (WINDOWS32) || defined (__EMX__) + /* These are also used commonly on these platforms. */ + && ((tmpdir = getenv ("TEMP")) == NULL || *tmpdir == '\0') + && ((tmpdir = getenv ("TMP")) == NULL || *tmpdir == '\0') +#endif + ) + tmpdir = DEFAULT_TMPDIR; + + template = alloca (strlen (tmpdir) + CSTRLEN (DEFAULT_TMPFILE) + 2); + strcpy (template, tmpdir); + +#ifdef HAVE_DOS_PATHS + if (strchr ("/\\", template[strlen (template) - 1]) == NULL) + strcat (template, "/"); +#else +# ifndef VMS + if (template[strlen (template) - 1] != '/') + strcat (template, "/"); +# endif /* !VMS */ +#endif /* !HAVE_DOS_PATHS */ + + strcat (template, DEFAULT_TMPFILE); + outfile = output_tmpfile (&stdin_nm, template); + if (outfile == 0) + pfatal_with_name (_("fopen (temporary file)")); + while (!feof (stdin) && ! ferror (stdin)) + { + char buf[2048]; + unsigned int n = fread (buf, 1, sizeof (buf), stdin); + if (n > 0 && fwrite (buf, 1, n, outfile) != n) + pfatal_with_name (_("fwrite (temporary file)")); + } + fclose (outfile); + + /* Replace the name that read_all_makefiles will + see with the name of the temporary file. */ + makefiles->list[i] = strcache_add (stdin_nm); + + /* Make sure the temporary file will not be remade. */ + { + struct file *f = enter_file (strcache_add (stdin_nm)); + f->updated = 1; + f->update_status = us_success; + f->command_state = cs_finished; + /* Can't be intermediate, or it'll be removed too early for + make re-exec. */ + f->intermediate = 0; + f->dontcare = 0; + } + } + } + +#if !defined(__EMX__) || defined(__KLIBC__) /* Don't use a SIGCHLD handler for good old EMX (bird) */ +#if !defined(HAVE_WAIT_NOHANG) || defined(MAKE_JOBSERVER) + /* Set up to handle children dying. This must be done before + reading in the makefiles so that 'shell' function calls will work. + + If we don't have a hanging wait we have to fall back to old, broken + functionality here and rely on the signal handler and counting + children. + + If we're using the jobs pipe we need a signal handler so that SIGCHLD is + not ignored; we need it to interrupt the read(2) of the jobserver pipe if + we're waiting for a token. + + If none of these are true, we don't need a signal handler at all. */ + { +# if defined SIGCHLD + bsd_signal (SIGCHLD, child_handler); +# endif +# if defined SIGCLD && SIGCLD != SIGCHLD + bsd_signal (SIGCLD, child_handler); +# endif + } + +#ifdef HAVE_PSELECT + /* If we have pselect() then we need to block SIGCHLD so it's deferred. */ + { + sigset_t block; + sigemptyset (&block); + sigaddset (&block, SIGCHLD); + if (sigprocmask (SIG_SETMASK, &block, NULL) < 0) + pfatal_with_name ("sigprocmask(SIG_SETMASK, SIGCHLD)"); + } +#endif + +#endif +#endif + + /* Let the user send us SIGUSR1 to toggle the -d flag during the run. */ +#ifdef SIGUSR1 + bsd_signal (SIGUSR1, debug_signal_handler); +#endif + + /* Define the initial list of suffixes for old-style rules. */ + set_default_suffixes (); + + /* Define the file rules for the built-in suffix rules. These will later + be converted into pattern rules. We used to do this in + install_default_implicit_rules, but since that happens after reading + makefiles, it results in the built-in pattern rules taking precedence + over makefile-specified suffix rules, which is wrong. */ + install_default_suffix_rules (); + + /* Define some internal and special variables. */ + define_automatic_variables (); + + /* Set up the MAKEFLAGS and MFLAGS variables for makefiles to see. + Initialize it to be exported but allow the makefile to reset it. */ + define_makeflags (0, 0)->export = v_export; + + /* Define the default variables. */ + define_default_variables (); + + default_file = enter_file (strcache_add (".DEFAULT")); + + default_goal_var = define_variable_cname (".DEFAULT_GOAL", "", o_file, 0); + + /* Evaluate all strings provided with --eval. + Also set up the $(-*-eval-flags-*-) variable. */ + + if (eval_strings) + { + char *p, *value; + unsigned int i; + unsigned int len = (CSTRLEN ("--eval=") + 1) * eval_strings->idx; + + for (i = 0; i < eval_strings->idx; ++i) + { +#ifndef CONFIG_WITH_VALUE_LENGTH + p = xstrdup (eval_strings->list[i]); + len += 2 * strlen (p); + eval_buffer (p, NULL); +#else + unsigned int sub_len = strlen(eval_strings->list[i]); + p = xstrndup (eval_strings->list[i], sub_len); + len += 2 * sub_len; + eval_buffer (p, NULL, p + sub_len); +#endif + free (p); + } + + p = value = alloca (len); + for (i = 0; i < eval_strings->idx; ++i) + { + strcpy (p, "--eval="); + p += CSTRLEN ("--eval="); + p = quote_for_env (p, eval_strings->list[i]); + *(p++) = ' '; + } + p[-1] = '\0'; + + define_variable_cname ("-*-eval-flags-*-", value, o_automatic, 0); + } + + /* Read all the makefiles. */ + + read_files = read_all_makefiles (makefiles == 0 ? 0 : makefiles->list); + +#ifdef WINDOWS32 + /* look one last time after reading all Makefiles */ + if (no_default_sh_exe) + no_default_sh_exe = !find_and_set_default_shell (NULL); +#endif /* WINDOWS32 */ + +#if defined (__MSDOS__) || defined (__EMX__) || defined (VMS) + /* We need to know what kind of shell we will be using. */ + { + extern int _is_unixy_shell (const char *_path); + struct variable *shv = lookup_variable (STRING_SIZE_TUPLE ("SHELL")); + extern int unixy_shell; + extern const char *default_shell; + + if (shv && *shv->value) + { + char *shell_path = recursively_expand (shv); + + if (shell_path && _is_unixy_shell (shell_path)) + unixy_shell = 1; + else + unixy_shell = 0; + if (shell_path) + default_shell = shell_path; + } + } +#endif /* __MSDOS__ || __EMX__ */ + + { + int old_builtin_rules_flag = no_builtin_rules_flag; + int old_builtin_variables_flag = no_builtin_variables_flag; + + /* Decode switches again, for variables set by the makefile. */ + decode_env_switches (STRING_SIZE_TUPLE ("GNUMAKEFLAGS")); + + /* Clear GNUMAKEFLAGS to avoid duplication. */ + define_variable_cname ("GNUMAKEFLAGS", "", o_override, 0); + +#ifdef KMK + decode_env_switches (STRING_SIZE_TUPLE ("KMK_FLAGS")); +#else /* !KMK */ + decode_env_switches (STRING_SIZE_TUPLE ("MAKEFLAGS")); +#if 0 + decode_env_switches (STRING_SIZE_TUPLE ("MFLAGS")); +#endif +#endif /* !KMK */ + + /* Reset in case the switches changed our mind. */ + syncing = (output_sync == OUTPUT_SYNC_LINE + || output_sync == OUTPUT_SYNC_TARGET); + + if (make_sync.syncout && ! syncing) + output_close (&make_sync); + + make_sync.syncout = syncing; + OUTPUT_SET (&make_sync); + + /* If we've disabled builtin rules, get rid of them. */ + if (no_builtin_rules_flag && ! old_builtin_rules_flag) + { + if (suffix_file->builtin) + { + free_dep_chain (suffix_file->deps); + suffix_file->deps = 0; + } + define_variable_cname ("SUFFIXES", "", o_default, 0); + } + + /* If we've disabled builtin variables, get rid of them. */ + if (no_builtin_variables_flag && ! old_builtin_variables_flag) + undefine_default_variables (); + } + +#if defined (__MSDOS__) || defined (__EMX__) || defined (VMS) + if (arg_job_slots != 1 +# ifdef __EMX__ + && _osmode != OS2_MODE /* turn off -j if we are in DOS mode */ +# endif + ) + { + O (error, NILF, + _("Parallel jobs (-j) are not supported on this platform.")); + O (error, NILF, _("Resetting to single job (-j1) mode.")); + arg_job_slots = job_slots = 1; + } +#endif + + /* If we have >1 slot at this point, then we're a top-level make. + Set up the jobserver. + + Every make assumes that it always has one job it can run. For the + submakes it's the token they were given by their parent. For the top + make, we just subtract one from the number the user wants. */ + + if (job_slots > 1 && jobserver_setup (job_slots - 1)) + { + /* Fill in the jobserver_auth for our children. */ + jobserver_auth = jobserver_get_auth (); + + if (jobserver_auth) + { + /* We're using the jobserver so set job_slots to 0. */ + master_job_slots = job_slots; + job_slots = 0; + } + } + + /* If we're not using parallel jobs, then we don't need output sync. + This is so people can enable output sync in GNUMAKEFLAGS or similar, but + not have it take effect unless parallel builds are enabled. */ + if (syncing && job_slots == 1) + { + OUTPUT_UNSET (); + output_close (&make_sync); + syncing = 0; + output_sync = OUTPUT_SYNC_NONE; + } + +#ifndef MAKE_SYMLINKS + if (check_symlink_flag) + { + O (error, NILF, _("Symbolic links not supported: disabling -L.")); + check_symlink_flag = 0; + } +#endif + + /* Set up MAKEFLAGS and MFLAGS again, so they will be right. */ + + define_makeflags (1, 0); + + /* Make each 'struct goaldep' point at the 'struct file' for the file + depended on. Also do magic for special targets. */ + + snap_deps (); + + /* Convert old-style suffix rules to pattern rules. It is important to + do this before installing the built-in pattern rules below, so that + makefile-specified suffix rules take precedence over built-in pattern + rules. */ + + convert_to_pattern (); + + /* Install the default implicit pattern rules. + This used to be done before reading the makefiles. + But in that case, built-in pattern rules were in the chain + before user-defined ones, so they matched first. */ + + install_default_implicit_rules (); + + /* Compute implicit rule limits. */ + + count_implicit_rule_limits (); + + /* Construct the listings of directories in VPATH lists. */ + + build_vpath_lists (); + + /* Mark files given with -o flags as very old and as having been updated + already, and files given with -W flags as brand new (time-stamp as far + as possible into the future). If restarts is set we'll do -W later. */ + + if (old_files != 0) + { + const char **p; + for (p = old_files->list; *p != 0; ++p) + { + struct file *f = enter_file (*p); + f->last_mtime = f->mtime_before_update = OLD_MTIME; + f->updated = 1; + f->update_status = us_success; + f->command_state = cs_finished; + } + } + + if (!restarts && new_files != 0) + { + const char **p; + for (p = new_files->list; *p != 0; ++p) + { + struct file *f = enter_file (*p); + f->last_mtime = f->mtime_before_update = NEW_MTIME; + } + } + + /* Initialize the remote job module. */ + remote_setup (); + + /* Dump any output we've collected. */ + + OUTPUT_UNSET (); + output_close (&make_sync); + + if (read_files != 0) + { + /* Update any makefiles if necessary. */ + + FILE_TIMESTAMP *makefile_mtimes = 0; + unsigned int mm_idx = 0; + char **aargv = NULL; + const char **nargv; + int nargc; + enum update_status status; + + DB (DB_BASIC, (_("Updating makefiles....\n"))); + + /* Remove any makefiles we don't want to try to update. + Also record the current modtimes so we can compare them later. */ + { + register struct goaldep *d, *last; + last = 0; + d = read_files; + while (d != 0) + { + struct file *f = d->file; + if (f->double_colon) + for (f = f->double_colon; f != NULL; f = f->prev) + { + if (f->deps == 0 && f->cmds != 0) + { + /* This makefile is a :: target with commands, but + no dependencies. So, it will always be remade. + This might well cause an infinite loop, so don't + try to remake it. (This will only happen if + your makefiles are written exceptionally + stupidly; but if you work for Athena, that's how + you write your makefiles.) */ + + DB (DB_VERBOSE, + (_("Makefile '%s' might loop; not remaking it.\n"), + f->name)); + + if (last == 0) + read_files = d->next; + else + last->next = d->next; + + /* Free the storage. */ + free_goaldep (d); + + d = last == 0 ? read_files : last->next; + + break; + } + } + + if (f == NULL || !f->double_colon) + { + makefile_mtimes = xrealloc (makefile_mtimes, + (mm_idx+1) + * sizeof (FILE_TIMESTAMP)); + makefile_mtimes[mm_idx++] = file_mtime_no_search (d->file); + last = d; + d = d->next; + } + } + } + + /* Set up 'MAKEFLAGS' specially while remaking makefiles. */ + define_makeflags (1, 1); + + { + int orig_db_level = db_level; + + if (! ISDB (DB_MAKEFILES)) + db_level = DB_NONE; + + rebuilding_makefiles = 1; + status = update_goal_chain (read_files); + rebuilding_makefiles = 0; + + db_level = orig_db_level; + } + + switch (status) + { + case us_question: + /* The only way this can happen is if the user specified -q and asked + for one of the makefiles to be remade as a target on the command + line. Since we're not actually updating anything with -q we can + treat this as "did nothing". */ + + case us_none: + /* Did nothing. */ + break; + + case us_failed: + /* Failed to update. Figure out if we care. */ + { + /* Nonzero if any makefile was successfully remade. */ + int any_remade = 0; + /* Nonzero if any makefile we care about failed + in updating or could not be found at all. */ + int any_failed = 0; + unsigned int i; + struct goaldep *d; + + for (i = 0, d = read_files; d != 0; ++i, d = d->next) + { + if (d->file->updated) + { + /* This makefile was updated. */ + if (d->file->update_status == us_success) + { + /* It was successfully updated. */ + any_remade |= (file_mtime_no_search (d->file) + != makefile_mtimes[i]); + } + else if (! (d->flags & RM_DONTCARE)) + { + FILE_TIMESTAMP mtime; + /* The update failed and this makefile was not + from the MAKEFILES variable, so we care. */ + OS (error, NILF, _("Failed to remake makefile '%s'."), + d->file->name); + mtime = file_mtime_no_search (d->file); + any_remade |= (mtime != NONEXISTENT_MTIME + && mtime != makefile_mtimes[i]); + makefile_status = MAKE_FAILURE; + } + } + else + /* This makefile was not found at all. */ + if (! (d->flags & RM_DONTCARE)) + { + const char *dnm = dep_name (d); + size_t l = strlen (dnm); + + /* This is a makefile we care about. See how much. */ + if (d->flags & RM_INCLUDED) + /* An included makefile. We don't need to die, but we + do want to complain. */ + error (NILF, l, + _("Included makefile '%s' was not found."), dnm); + else + { + /* A normal makefile. We must die later. */ + error (NILF, l, + _("Makefile '%s' was not found"), dnm); + any_failed = 1; + } + } + } + /* Reset this to empty so we get the right error message below. */ + read_files = 0; + + if (any_remade) + goto re_exec; + if (any_failed) + die (MAKE_FAILURE); + break; + } + + case us_success: + re_exec: + /* Updated successfully. Re-exec ourselves. */ + + remove_intermediates (0); + + if (print_data_base_flag) + print_data_base (); + + clean_jobserver (0); + + if (makefiles != 0) + { + /* These names might have changed. */ + int i, j = 0; + for (i = 1; i < argc; ++i) + if (strneq (argv[i], "-f", 2)) /* XXX */ + { + if (argv[i][2] == '\0') + /* This cast is OK since we never modify argv. */ + argv[++i] = (char *) makefiles->list[j]; + else + argv[i] = xstrdup (concat (2, "-f", makefiles->list[j])); + ++j; + } + } + + /* Add -o option for the stdin temporary file, if necessary. */ + nargc = argc; + if (stdin_nm) + { + void *m = xmalloc ((nargc + 2) * sizeof (char *)); + aargv = m; + memcpy (aargv, argv, argc * sizeof (char *)); + aargv[nargc++] = xstrdup (concat (2, "-o", stdin_nm)); + aargv[nargc] = 0; + nargv = m; + } + else + nargv = (const char**)argv; + + if (directories != 0 && directories->idx > 0) + { + int bad = 1; + if (directory_before_chdir != 0) + { + if (chdir (directory_before_chdir) < 0) + perror_with_name ("chdir", ""); + else + bad = 0; + } + if (bad) + O (fatal, NILF, + _("Couldn't change back to original directory.")); + } + + ++restarts; + + if (ISDB (DB_BASIC)) + { + const char **p; + printf (_("Re-executing[%u]:"), restarts); + for (p = nargv; *p != 0; ++p) + printf (" %s", *p); + putchar ('\n'); + fflush (stdout); + } + +#ifndef _AMIGA + { + char **p; + for (p = environ; *p != 0; ++p) + { + if (strneq (*p, MAKELEVEL_NAME "=", MAKELEVEL_LENGTH+1)) + { + *p = alloca (40); + sprintf (*p, "%s=%u", MAKELEVEL_NAME, makelevel); +#ifdef VMS + vms_putenv_symbol (*p); +#endif + } + else if (strneq (*p, "MAKE_RESTARTS=", CSTRLEN ("MAKE_RESTARTS="))) + { + *p = alloca (40); + sprintf (*p, "MAKE_RESTARTS=%s%u", + OUTPUT_IS_TRACED () ? "-" : "", restarts); + restarts = 0; + } + } + } +#else /* AMIGA */ + { + char buffer[256]; + + sprintf (buffer, "%u", makelevel); + SetVar (MAKELEVEL_NAME, buffer, -1, GVF_GLOBAL_ONLY); + + sprintf (buffer, "%s%u", OUTPUT_IS_TRACED () ? "-" : "", restarts); + SetVar ("MAKE_RESTARTS", buffer, -1, GVF_GLOBAL_ONLY); + restarts = 0; + } +#endif + + /* If we didn't set the restarts variable yet, add it. */ + if (restarts) + { + char *b = alloca (40); + sprintf (b, "MAKE_RESTARTS=%s%u", + OUTPUT_IS_TRACED () ? "-" : "", restarts); + putenv (b); + } + + fflush (stdout); + fflush (stderr); + +#ifdef _AMIGA + exec_command (nargv); + exit (0); +#elif defined (__EMX__) + { + /* It is not possible to use execve() here because this + would cause the parent process to be terminated with + exit code 0 before the child process has been terminated. + Therefore it may be the best solution simply to spawn the + child process including all file handles and to wait for its + termination. */ + int pid; + int r; + pid = child_execute_job (NULL, 1, nargv, environ); + + /* is this loop really necessary? */ + do { + pid = wait (&r); + } while (pid <= 0); + /* use the exit code of the child process */ + exit (WIFEXITED(r) ? WEXITSTATUS(r) : EXIT_FAILURE); + } +#else +#ifdef SET_STACK_SIZE + /* Reset limits, if necessary. */ + if (stack_limit.rlim_cur) + setrlimit (RLIMIT_STACK, &stack_limit); +#endif +# if !defined(WINDOWS32) || !defined(CONFIG_NEW_WIN_CHILDREN) + exec_command ((char **)nargv, environ); +# else + MkWinChildReExecMake ((char **)nargv, environ); +# endif +#endif + free (aargv); + break; + } + + /* Free the makefile mtimes. */ + free (makefile_mtimes); + } + + /* Set up 'MAKEFLAGS' again for the normal targets. */ + define_makeflags (1, 0); + + /* Set always_make_flag if -B was given. */ + always_make_flag = always_make_set; + + /* If restarts is set we haven't set up -W files yet, so do that now. */ + if (restarts && new_files != 0) + { + const char **p; + for (p = new_files->list; *p != 0; ++p) + { + struct file *f = enter_file (*p); + f->last_mtime = f->mtime_before_update = NEW_MTIME; + } + } + + /* If there is a temp file from reading a makefile from stdin, get rid of + it now. */ + if (stdin_nm && unlink (stdin_nm) < 0 && errno != ENOENT) + perror_with_name (_("unlink (temporary file): "), stdin_nm); + + /* If there were no command-line goals, use the default. */ + if (goals == 0) + { + char *p; + + if (default_goal_var->recursive) + p = variable_expand (default_goal_var->value); + else + { + p = variable_buffer_output (variable_buffer, default_goal_var->value, + strlen (default_goal_var->value)); + *p = '\0'; + p = variable_buffer; + } + + if (*p != '\0') + { + struct file *f = lookup_file (p); + + /* If .DEFAULT_GOAL is a non-existent target, enter it into the + table and let the standard logic sort it out. */ + if (f == 0) + { + struct nameseq *ns; + + ns = PARSE_SIMPLE_SEQ (&p, struct nameseq); + if (ns) + { + /* .DEFAULT_GOAL should contain one target. */ + if (ns->next != 0) + O (fatal, NILF, + _(".DEFAULT_GOAL contains more than one target")); + +#ifndef CONFIG_WITH_VALUE_LENGTH + f = enter_file (strcache_add (ns->name)); +#else + f = enter_file (ns->name); +#endif + + ns->name = 0; /* It was reused by enter_file(). */ + free_ns_chain (ns); + } + } + + if (f) + { + goals = alloc_goaldep (); + goals->file = f; + } + } + } + else + lastgoal->next = 0; + + + if (!goals) + { + if (read_files == 0) + O (fatal, NILF, _("No targets specified and no makefile found")); + + O (fatal, NILF, _("No targets")); + } + + /* Update the goals. */ + + DB (DB_BASIC, (_("Updating goal targets....\n"))); + + { + switch (update_goal_chain (goals)) + { + case us_none: + /* Nothing happened. */ + /* FALLTHROUGH */ + case us_success: + /* Keep the previous result. */ + break; + case us_question: + /* We are under -q and would run some commands. */ + makefile_status = MAKE_TROUBLE; + break; + case us_failed: + /* Updating failed. POSIX.2 specifies exit status >1 for this; */ + makefile_status = MAKE_FAILURE; + break; + } + + /* If we detected some clock skew, generate one last warning */ + if (clock_skew_detected) + O (error, NILF, + _("warning: Clock skew detected. Your build may be incomplete.")); + + MAKE_STATS_2(if (uStartTick) printf("main ticks elapsed: %llu\n", (unsigned long long)(CURRENT_CLOCK_TICK() - uStartTick)) ); + /* Exit. */ + die (makefile_status); + } + + /* NOTREACHED */ + exit (MAKE_SUCCESS); +} + +/* Parsing of arguments, decoding of switches. */ + +static char options[1 + sizeof (switches) / sizeof (switches[0]) * 3]; +static struct option long_options[(sizeof (switches) / sizeof (switches[0])) + + (sizeof (long_option_aliases) / + sizeof (long_option_aliases[0]))]; + +/* Fill in the string and vector for getopt. */ +static void +init_switches (void) +{ + char *p; + unsigned int c; + unsigned int i; + + if (options[0] != '\0') + /* Already done. */ + return; + + p = options; + + /* Return switch and non-switch args in order, regardless of + POSIXLY_CORRECT. Non-switch args are returned as option 1. */ + *p++ = '-'; + + for (i = 0; switches[i].c != '\0'; ++i) + { + long_options[i].name = (switches[i].long_name == 0 ? "" : + switches[i].long_name); + long_options[i].flag = 0; + long_options[i].val = switches[i].c; + if (short_option (switches[i].c)) + *p++ = switches[i].c; + switch (switches[i].type) + { + case flag: + case flag_off: + case ignore: + long_options[i].has_arg = no_argument; + break; + + case string: + case strlist: + case filename: + case positive_int: + case floating: + if (short_option (switches[i].c)) + *p++ = ':'; + if (switches[i].noarg_value != 0) + { + if (short_option (switches[i].c)) + *p++ = ':'; + long_options[i].has_arg = optional_argument; + } + else + long_options[i].has_arg = required_argument; + break; + } + } + *p = '\0'; + for (c = 0; c < (sizeof (long_option_aliases) / + sizeof (long_option_aliases[0])); + ++c) + long_options[i++] = long_option_aliases[c]; + long_options[i].name = 0; +} + + +/* Non-option argument. It might be a variable definition. */ +static void +handle_non_switch_argument (const char *arg, int env) +{ + struct variable *v; + + if (arg[0] == '-' && arg[1] == '\0') + /* Ignore plain '-' for compatibility. */ + return; + +#ifdef VMS + { + /* VMS DCL quoting can result in foo="bar baz" showing up here. + Need to remove the double quotes from the value. */ + char * eq_ptr; + char * new_arg; + eq_ptr = strchr (arg, '='); + if ((eq_ptr != NULL) && (eq_ptr[1] == '"')) + { + int len; + int seg1; + int seg2; + len = strlen(arg); + new_arg = alloca(len); + seg1 = eq_ptr - arg + 1; + strncpy(new_arg, arg, (seg1)); + seg2 = len - seg1 - 1; + strncpy(&new_arg[seg1], &eq_ptr[2], seg2); + new_arg[seg1 + seg2] = 0; + if (new_arg[seg1 + seg2 - 1] == '"') + new_arg[seg1 + seg2 - 1] = 0; + arg = new_arg; + } + } +#endif + v = try_variable_definition (0, arg IF_WITH_VALUE_LENGTH_PARAM(NULL), o_command, 0); + if (v != 0) + { + /* It is indeed a variable definition. If we don't already have this + one, record a pointer to the variable for later use in + define_makeflags. */ + struct command_variable *cv; + + for (cv = command_variables; cv != 0; cv = cv->next) + if (cv->variable == v) + break; + + if (! cv) + { + cv = xmalloc (sizeof (*cv)); + cv->variable = v; + cv->next = command_variables; + command_variables = cv; + } + } + else if (! env) + { + /* Not an option or variable definition; it must be a goal + target! Enter it as a file and add it to the dep chain of + goals. */ + struct file *f = enter_file (strcache_add (expand_command_line_file (arg))); + f->cmd_target = 1; + + if (goals == 0) + { + goals = alloc_goaldep (); + lastgoal = goals; + } + else + { + lastgoal->next = alloc_goaldep (); + lastgoal = lastgoal->next; + } + + lastgoal->file = f; + + { + /* Add this target name to the MAKECMDGOALS variable. */ + struct variable *gv; + const char *value; + + gv = lookup_variable (STRING_SIZE_TUPLE ("MAKECMDGOALS")); + if (gv == 0) + value = f->name; + else + { + /* Paste the old and new values together */ + unsigned int oldlen, newlen; + char *vp; + + oldlen = strlen (gv->value); + newlen = strlen (f->name); + vp = alloca (oldlen + 1 + newlen + 1); + memcpy (vp, gv->value, oldlen); + vp[oldlen] = ' '; + memcpy (&vp[oldlen + 1], f->name, newlen + 1); + value = vp; + } + define_variable_cname ("MAKECMDGOALS", value, o_default, 0); + } + } +} + +/* Print a nice usage method. */ + +static void +print_usage (int bad) +{ + const char *const *cpp; + FILE *usageto; + + if (print_version_flag) + print_version (); + + usageto = bad ? stderr : stdout; + + fprintf (usageto, _("Usage: %s [options] [target] ...\n"), program); + + for (cpp = usage; *cpp; ++cpp) + fputs (_(*cpp), usageto); + +#ifdef KMK + if (!remote_description || *remote_description == '\0') + fprintf (usageto, _("\nThis program is built for %s/%s/%s [" __DATE__ " " __TIME__ "]\n"), + KBUILD_HOST, KBUILD_HOST_ARCH, KBUILD_HOST_CPU); + else + fprintf (usageto, _("\nThis program is built for %s/%s/%s (%s) [" __DATE__ " " __TIME__ "]\n"), + KBUILD_HOST, KBUILD_HOST_ARCH, KBUILD_HOST_CPU, remote_description); +#else /* !KMK */ + if (!remote_description || *remote_description == '\0') + fprintf (usageto, _("\nThis program built for %s\n"), make_host); + else + fprintf (usageto, _("\nThis program built for %s (%s)\n"), + make_host, remote_description); +#endif /* !KMK */ + + fprintf (usageto, _("Report bugs to <bug-make@gnu.org>\n")); +} + +/* Decode switches from ARGC and ARGV. + They came from the environment if ENV is nonzero. */ + +static void +decode_switches (int argc, const char **argv, int env) +{ + int bad = 0; + register const struct command_switch *cs; + register struct stringlist *sl; + register int c; + + /* getopt does most of the parsing for us. + First, get its vectors set up. */ + + init_switches (); + + /* Let getopt produce error messages for the command line, + but not for options from the environment. */ + opterr = !env; + /* Reset getopt's state. */ + optind = 0; + + while (optind < argc) + { + const char *coptarg; + + /* Parse the next argument. */ + c = getopt_long (argc, (char*const*)argv, options, long_options, NULL); + coptarg = optarg; + if (c == EOF) + /* End of arguments, or "--" marker seen. */ + break; + else if (c == 1) + /* An argument not starting with a dash. */ + handle_non_switch_argument (coptarg, env); + else if (c == '?') + /* Bad option. We will print a usage message and die later. + But continue to parse the other options so the user can + see all he did wrong. */ + bad = 1; + else + for (cs = switches; cs->c != '\0'; ++cs) + if (cs->c == c) + { + /* Whether or not we will actually do anything with + this switch. We test this individually inside the + switch below rather than just once outside it, so that + options which are to be ignored still consume args. */ + int doit = !env || cs->env; + + switch (cs->type) + { + default: + abort (); + + case ignore: + break; + + case flag: + case flag_off: + if (doit) + *(int *) cs->value_ptr = cs->type == flag; + break; + + case string: + case strlist: + case filename: + if (!doit) + break; + + if (! coptarg) + coptarg = xstrdup (cs->noarg_value); + else if (*coptarg == '\0') + { + char opt[2] = "c"; + const char *op = opt; + + if (short_option (cs->c)) + opt[0] = cs->c; + else + op = cs->long_name; + + error (NILF, strlen (op), + _("the '%s%s' option requires a non-empty string argument"), + short_option (cs->c) ? "-" : "--", op); + bad = 1; + break; + } + + if (cs->type == string) + { + char **val = (char **)cs->value_ptr; + free (*val); + *val = xstrdup (coptarg); + break; + } + + sl = *(struct stringlist **) cs->value_ptr; + if (sl == 0) + { + sl = xmalloc (sizeof (struct stringlist)); + sl->max = 5; + sl->idx = 0; + sl->list = xmalloc (5 * sizeof (char *)); + *(struct stringlist **) cs->value_ptr = sl; + } + else if (sl->idx == sl->max - 1) + { + sl->max += 5; + /* MSVC erroneously warns without a cast here. */ + sl->list = xrealloc ((void *)sl->list, + sl->max * sizeof (char *)); + } + if (cs->type == filename) + sl->list[sl->idx++] = expand_command_line_file (coptarg); + else + sl->list[sl->idx++] = xstrdup (coptarg); + sl->list[sl->idx] = 0; + break; + + case positive_int: + /* See if we have an option argument; if we do require that + it's all digits, not something like "10foo". */ + if (coptarg == 0 && argc > optind) + { + const char *cp; + for (cp=argv[optind]; ISDIGIT (cp[0]); ++cp) + ; + if (cp[0] == '\0') + coptarg = argv[optind++]; + } + + if (!doit) + break; + + if (coptarg) + { + int i = atoi (coptarg); + const char *cp; + + /* Yes, I realize we're repeating this in some cases. */ + for (cp = coptarg; ISDIGIT (cp[0]); ++cp) + ; + + if (i < 1 || cp[0] != '\0') + { + error (NILF, 0, + _("the '-%c' option requires a positive integer argument"), + cs->c); + bad = 1; + } + else + *(unsigned int *) cs->value_ptr = i; + } + else + *(unsigned int *) cs->value_ptr + = *(unsigned int *) cs->noarg_value; + break; + +#ifndef NO_FLOAT + case floating: + if (coptarg == 0 && optind < argc + && (ISDIGIT (argv[optind][0]) || argv[optind][0] == '.')) + coptarg = argv[optind++]; + + if (doit) + *(double *) cs->value_ptr + = (coptarg != 0 ? atof (coptarg) + : *(double *) cs->noarg_value); + + break; +#endif + } + + /* We've found the switch. Stop looking. */ + break; + } + } + + /* There are no more options according to getting getopt, but there may + be some arguments left. Since we have asked for non-option arguments + to be returned in order, this only happens when there is a "--" + argument to prevent later arguments from being options. */ + while (optind < argc) + handle_non_switch_argument (argv[optind++], env); + + if (!env && (bad || print_usage_flag)) + { + print_usage (bad); + die (bad ? MAKE_FAILURE : MAKE_SUCCESS); + } + + /* If there are any options that need to be decoded do it now. */ + decode_debug_flags (); + decode_output_sync_flags (); + +#if defined (WINDOWS32) && defined (CONFIG_NEW_WIN_CHILDREN) + /* validate the job object mode value . */ + if (win_job_object_mode == NULL) + win_job_object_mode = xstrdup ("login"); + else if ( strcmp (win_job_object_mode, "none") != 0 + && strcmp (win_job_object_mode, "login") != 0 + && strcmp (win_job_object_mode, "root") != 0 + && strcmp (win_job_object_mode, "each") != 0) + OS (fatal, NILF, _("unknown job object mode '%s'"), win_job_object_mode); +#endif +} + +/* Decode switches from environment variable ENVAR (which is LEN chars long). + We do this by chopping the value into a vector of words, prepending a + dash to the first word if it lacks one, and passing the vector to + decode_switches. */ + +static void +decode_env_switches (const char *envar, unsigned int len) +{ + char *varref = alloca (2 + len + 2); + char *value, *p, *buf; + int argc; + const char **argv; + + /* Get the variable's value. */ + varref[0] = '$'; + varref[1] = '('; + memcpy (&varref[2], envar, len); + varref[2 + len] = ')'; + varref[2 + len + 1] = '\0'; + value = variable_expand (varref); + + /* Skip whitespace, and check for an empty value. */ + NEXT_TOKEN (value); + len = strlen (value); + if (len == 0) + return; + + /* Allocate a vector that is definitely big enough. */ + argv = alloca ((1 + len + 1) * sizeof (char *)); + + /* getopt will look at the arguments starting at ARGV[1]. + Prepend a spacer word. */ + argv[0] = 0; + argc = 1; + + /* We need a buffer to copy the value into while we split it into words + and unquote it. Set up in case we need to prepend a dash later. */ + buf = alloca (1 + len + 1); + buf[0] = '-'; + p = buf+1; + argv[argc] = p; + while (*value != '\0') + { + if (*value == '\\' && value[1] != '\0') + ++value; /* Skip the backslash. */ + else if (ISBLANK (*value)) + { + /* End of the word. */ + *p++ = '\0'; + argv[++argc] = p; + do + ++value; + while (ISBLANK (*value)); + continue; + } + *p++ = *value++; + } + *p = '\0'; + argv[++argc] = 0; + assert (p < buf + len + 2); + + if (argv[1][0] != '-' && strchr (argv[1], '=') == 0) + /* The first word doesn't start with a dash and isn't a variable + definition, so add a dash. */ + argv[1] = buf; + + /* Parse those words. */ + decode_switches (argc, argv, 1); +} + +/* Quote the string IN so that it will be interpreted as a single word with + no magic by decode_env_switches; also double dollar signs to avoid + variable expansion in make itself. Write the result into OUT, returning + the address of the next character to be written. + Allocating space for OUT twice the length of IN is always sufficient. */ + +static char * +quote_for_env (char *out, const char *in) +{ + while (*in != '\0') + { + if (*in == '$') + *out++ = '$'; + else if (ISBLANK (*in) || *in == '\\') + *out++ = '\\'; + *out++ = *in++; + } + + return out; +} + +/* Define the MAKEFLAGS and MFLAGS variables to reflect the settings of the + command switches. Include options with args if ALL is nonzero. + Don't include options with the 'no_makefile' flag set if MAKEFILE. */ + +static struct variable * +define_makeflags (int all, int makefile) +{ +#ifdef KMK + static const char ref[] = "$(KMK_OVERRIDES)"; +#else + static /*<- bird*/ const char ref[] = "$(MAKEOVERRIDES)"; +#endif + static /*<- bird*/ const char posixref[] = "$(-*-command-variables-*-)"; + static /*<- bird*/ const char evalref[] = "$(-*-eval-flags-*-)"; + const struct command_switch *cs; + char *flagstring; + char *p; + + /* We will construct a linked list of 'struct flag's describing + all the flags which need to go in MAKEFLAGS. Then, once we + know how many there are and their lengths, we can put them all + together in a string. */ + + struct flag + { + struct flag *next; + const struct command_switch *cs; + const char *arg; + }; + struct flag *flags = 0; + struct flag *last = 0; + unsigned int flagslen = 0; +#define ADD_FLAG(ARG, LEN) \ + do { \ + struct flag *new = alloca (sizeof (struct flag)); \ + new->cs = cs; \ + new->arg = (ARG); \ + new->next = 0; \ + if (! flags) \ + flags = new; \ + else \ + last->next = new; \ + last = new; \ + if (new->arg == 0) \ + /* Just a single flag letter: " -x" */ \ + flagslen += 3; \ + else \ + /* " -xfoo", plus space to escape "foo". */ \ + flagslen += 1 + 1 + 1 + (3 * (LEN)); \ + if (!short_option (cs->c)) \ + /* This switch has no single-letter version, so we use the long. */ \ + flagslen += 2 + strlen (cs->long_name); \ + } while (0) + + for (cs = switches; cs->c != '\0'; ++cs) + if (cs->toenv && (!makefile || !cs->no_makefile)) + switch (cs->type) + { + case ignore: + break; + + case flag: + case flag_off: + if ((!*(int *) cs->value_ptr) == (cs->type == flag_off) + && (cs->default_value == 0 + || *(int *) cs->value_ptr != *(int *) cs->default_value)) + ADD_FLAG (0, 0); + break; + + case positive_int: + if (all) + { + if ((cs->default_value != 0 + && (*(unsigned int *) cs->value_ptr + == *(unsigned int *) cs->default_value))) + break; + else if (cs->noarg_value != 0 + && (*(unsigned int *) cs->value_ptr == + *(unsigned int *) cs->noarg_value)) + ADD_FLAG ("", 0); /* Optional value omitted; see below. */ + else + { + char *buf = alloca (30); + sprintf (buf, "%u", *(unsigned int *) cs->value_ptr); + ADD_FLAG (buf, strlen (buf)); + } + } + break; + +#ifndef NO_FLOAT + case floating: + if (all) + { + if (cs->default_value != 0 + && (*(double *) cs->value_ptr + == *(double *) cs->default_value)) + break; + else if (cs->noarg_value != 0 + && (*(double *) cs->value_ptr + == *(double *) cs->noarg_value)) + ADD_FLAG ("", 0); /* Optional value omitted; see below. */ + else + { + char *buf = alloca (100); + sprintf (buf, "%g", *(double *) cs->value_ptr); + ADD_FLAG (buf, strlen (buf)); + } + } + break; +#endif + + case string: + if (all) + { + p = *((char **)cs->value_ptr); + if (p) + ADD_FLAG (p, strlen (p)); + } + break; + + case filename: + case strlist: + if (all) + { + struct stringlist *sl = *(struct stringlist **) cs->value_ptr; + if (sl != 0) + { + unsigned int i; + for (i = 0; i < sl->idx; ++i) + ADD_FLAG (sl->list[i], strlen (sl->list[i])); + } + } + break; + + default: + abort (); + } + +#undef ADD_FLAG + + /* Four more for the possible " -- ", plus variable references. */ + flagslen += 4 + CSTRLEN (posixref) + 1 + CSTRLEN (evalref) + 1; + + /* Construct the value in FLAGSTRING. + We allocate enough space for a preceding dash and trailing null. */ + flagstring = alloca (1 + flagslen + 1); + memset (flagstring, '\0', 1 + flagslen + 1); + p = flagstring; + + /* Start with a dash, for MFLAGS. */ + *p++ = '-'; + + /* Add simple options as a group. */ + while (flags != 0 && !flags->arg && short_option (flags->cs->c)) + { + *p++ = flags->cs->c; + flags = flags->next; + } + + /* Now add more complex flags: ones with options and/or long names. */ + while (flags) + { + *p++ = ' '; + *p++ = '-'; + + /* Add the flag letter or name to the string. */ + if (short_option (flags->cs->c)) + *p++ = flags->cs->c; + else + { + /* Long options require a double-dash. */ + *p++ = '-'; + strcpy (p, flags->cs->long_name); + p += strlen (p); + } + /* An omitted optional argument has an ARG of "". */ + if (flags->arg && flags->arg[0] != '\0') + { + if (!short_option (flags->cs->c)) + /* Long options require '='. */ + *p++ = '='; + p = quote_for_env (p, flags->arg); + } + flags = flags->next; + } + + /* If no flags at all, get rid of the initial dash. */ + if (p == &flagstring[1]) + { + flagstring[0] = '\0'; + p = flagstring; + } + +#ifdef KMK + /* Define MFLAGS before appending variable definitions. Omit an initial + empty dash. Since MFLAGS is not parsed for flags, there is no reason to + override any makefile redefinition. */ + define_variable_cname ("MFLAGS", + flagstring + (flagstring[0] == '-' && flagstring[1] == ' ' ? 2 : 0), + o_env, 1); +#endif /* !KMK */ + + /* Write a reference to -*-eval-flags-*-, which contains all the --eval + flag options. */ + if (eval_strings) + { + *p++ = ' '; + memcpy (p, evalref, CSTRLEN (evalref)); + p += CSTRLEN (evalref); + } + + if (all && command_variables) + { + /* Write a reference to $(MAKEOVERRIDES), which contains all the + command-line variable definitions. Separate the variables from the + switches with a "--" arg. */ + + strcpy (p, " -- "); + p += 4; + + /* Copy in the string. */ + if (posix_pedantic) + { + memcpy (p, posixref, CSTRLEN (posixref)); + p += CSTRLEN (posixref); + } + else + { + memcpy (p, ref, CSTRLEN (ref)); + p += CSTRLEN (ref); + } + } + + /* If there is a leading dash, omit it. */ + if (flagstring[0] == '-') + ++flagstring; + +#ifdef KMK + /* Provide simple access to some of the options. */ + { + char val[32]; + sprintf (val, "%u", job_slots ? job_slots : master_job_slots); + define_variable_cname ("KMK_OPTS_JOBS", val, o_default, 1); + sprintf (val, "%u", default_job_slots); + define_variable_cname ("KMK_OPTS_JOBS_DEFAULT", val, o_default, 1); + define_variable_cname ("KMK_OPTS_KEEP_GOING", keep_going_flag ? "1" : "0", o_default, 1); + define_variable_cname ("KMK_OPTS_JUST_PRINT", just_print_flag ? "1" : "0", o_default, 1); + define_variable_cname ("KMK_OPTS_PRETTY_COMMAND_PRINTING", + pretty_command_printing ? "1" : "0", o_default, 1); + sprintf (val, "%u", process_priority); + define_variable_cname ("KMK_OPTS_PRORITY", val, o_default, 1); + sprintf (val, "%u", process_affinity); + define_variable_cname ("KMK_OPTS_AFFINITY", val, o_default, 1); +# if defined (CONFIG_WITH_MAKE_STATS) || defined (CONFIG_WITH_MINIMAL_STATS) + define_variable_cname ("KMK_OPTS_STATISTICS", make_expensive_statistics ? "1" : "0", + o_default, 1); +# endif +# ifdef CONFIG_WITH_PRINT_TIME_SWITCH + sprintf (val, "%u", print_time_min); + define_variable_cname ("KMK_OPTS_PRINT_TIME", val, o_default, 1); +# endif + } +#endif + + /* This used to use o_env, but that lost when a makefile defined MAKEFLAGS. + Makefiles set MAKEFLAGS to add switches, but we still want to redefine + its value with the full set of switches. Then we used o_file, but that + lost when users added -e, causing a previous MAKEFLAGS env. var. to take + precedence over the new one. Of course, an override or command + definition will still take precedence. */ +#ifdef KMK + return define_variable_cname ("KMK_FLAGS", flagstring, + env_overrides ? o_env_override : o_file, 1); +#else + return define_variable_cname ("MAKEFLAGS", flagstring, + env_overrides ? o_env_override : o_file, 1); +#endif +} + +/* Print version information. */ + +static void +print_version (void) +{ + static int printed_version = 0; + + const char *precede = print_data_base_flag ? "# " : ""; + + if (printed_version) + /* Do it only once. */ + return; + +#ifdef KMK + printf ("%skmk - kBuild version %d.%d.%d (r%u)\n\ +\n", + precede, KBUILD_VERSION_MAJOR, KBUILD_VERSION_MINOR, + KBUILD_VERSION_PATCH, KBUILD_SVN_REV); + + printf("%sBased on GNU Make %s:\n", precede, version_string); + +#else /* !KMK */ + printf ("%sGNU Make %s\n", precede, version_string); + + if (!remote_description || *remote_description == '\0') + printf (_("%sBuilt for %s\n"), precede, make_host); + else + printf (_("%sBuilt for %s (%s)\n"), + precede, make_host, remote_description); +#endif /* !KMK */ + + /* Print this untranslated. The coding standards recommend translating the + (C) to the copyright symbol, but this string is going to change every + year, and none of the rest of it should be translated (including the + word "Copyright"), so it hardly seems worth it. */ + + printf ("%sCopyright (C) 1988-2016 Free Software Foundation, Inc.\n", + precede); + + printf (_("%sLicense GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\ +%sThis is free software: you are free to change and redistribute it.\n\ +%sThere is NO WARRANTY, to the extent permitted by law.\n"), + precede, precede, precede); + +#ifdef KMK + printf ("\n\ +%skBuild modifications:\n\ +%s Copyright (c) 2005-2018 knut st. osmundsen.\n\ +\n\ +%skmkbuiltin commands derived from *BSD sources:\n\ +%s Copyright (c) 1983 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994\n\ +%s The Regents of the University of California. All rights reserved.\n\ +%s Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>\n", + precede, precede, precede, precede, precede, precede); + +# ifdef KBUILD_PATH + printf (_("\n\ +%sKBUILD_PATH: '%s' (default '%s')\n\ +%sKBUILD_BIN_PATH: '%s' (default '%s')\n\ +\n"), + precede, get_kbuild_path(), KBUILD_PATH, + precede, get_kbuild_bin_path(), KBUILD_BIN_PATH); +# else /* !KBUILD_PATH */ + printf ("\n\ +%sKBUILD_PATH: '%s'\n\ +%sKBUILD_BIN_PATH: '%s'\n\ +\n", + precede, get_kbuild_path(), + precede, get_kbuild_bin_path()); +# endif /* !KBUILD_PATH */ + + if (!remote_description || *remote_description == '\0') + printf (_("%sThis program is a %s build, built for %s/%s/%s [" __DATE__ " " __TIME__ "]\n\n"), + precede, KBUILD_TYPE, KBUILD_HOST, KBUILD_HOST_ARCH, KBUILD_HOST_CPU); + else + printf (_("%sThis program is a %s build, built for %s/%s/%s (%s) [" __DATE__ " " __TIME__ "]\n\n"), + precede, KBUILD_TYPE, KBUILD_HOST, KBUILD_HOST_ARCH, KBUILD_HOST_CPU, remote_description); + +#endif /* KMK */ + + printed_version = 1; + + /* Flush stdout so the user doesn't have to wait to see the + version information while make thinks about things. */ + fflush (stdout); +} + +/* Print a bunch of information about this and that. */ + +static void +print_data_base (void) +{ + time_t when = time ((time_t *) 0); + + print_version (); + + printf (_("\n# Make data base, printed on %s"), ctime (&when)); + + print_variable_data_base (); + print_dir_data_base (); + print_rule_data_base (); + print_file_data_base (); + print_vpath_data_base (); +#ifdef KMK + print_kbuild_data_base (); +#endif +#ifndef CONFIG_WITH_STRCACHE2 + strcache_print_stats ("#"); +#else + strcache2_print_stats_all ("#"); +#endif +#ifdef CONFIG_WITH_ALLOC_CACHES + alloccache_print_all (); +#endif +#ifdef CONFIG_WITH_COMPILER + kmk_cc_print_stats (); +#endif + + when = time ((time_t *) 0); + printf (_("\n# Finished Make data base on %s\n"), ctime (&when)); +} +#ifdef CONFIG_WITH_PRINT_STATS_SWITCH + +static void +print_stats () +{ + time_t when; + + when = time ((time_t *) 0); + printf (_("\n# Make statistics, printed on %s"), ctime (&when)); + + /* Allocators: */ +# ifdef CONFIG_WITH_COMPILER + kmk_cc_print_stats (); +# endif +# ifndef CONFIG_WITH_STRCACHE2 + strcache_print_stats ("#"); +# else + strcache2_print_stats_all ("#"); +# endif +# ifdef CONFIG_WITH_ALLOC_CACHES + alloccache_print_all (); +# endif + print_heap_stats (); + + /* Make stuff: */ + print_variable_stats (); + print_file_stats (); + print_dir_stats (); +# ifdef KMK + print_kbuild_define_stats (); +# endif +# ifdef CONFIG_WITH_KMK_BUILTIN_STATS + kmk_builtin_print_stats (stdout, "# "); +# endif +# ifdef CONFIG_WITH_COMPILER + kmk_cc_print_stats (); +# endif + + when = time ((time_t *) 0); + printf (_("\n# Finished Make statistics on %s\n"), ctime (&when)); +} +#endif /* CONFIG_WITH_PRINT_STATS_SWITCH */ + +static void +clean_jobserver (int status) +{ + /* Sanity: have we written all our jobserver tokens back? If our + exit status is 2 that means some kind of syntax error; we might not + have written all our tokens so do that now. If tokens are left + after any other error code, that's bad. */ + + if (jobserver_enabled() && jobserver_tokens) + { + if (status != 2) + ON (error, NILF, + "INTERNAL: Exiting with %u jobserver tokens (should be 0)!", + jobserver_tokens); + else + /* Don't write back the "free" token */ + while (--jobserver_tokens) + jobserver_release (0); + } + + + /* Sanity: If we're the master, were all the tokens written back? */ + + if (master_job_slots) + { + /* We didn't write one for ourself, so start at 1. */ + unsigned int tokens = 1 + jobserver_acquire_all (); + + if (tokens != master_job_slots) + ONN (error, NILF, + "INTERNAL: Exiting with %u jobserver tokens available; should be %u!", + tokens, master_job_slots); + + reset_jobserver (); + } +} + +/* Exit with STATUS, cleaning up as necessary. */ + +void +#ifdef KMK +die_with_job_output (int status, struct output *out) +#else +die (int status) +#endif +{ + static char dying = 0; +#ifdef KMK + static char need_2nd_error = 0; + static char need_2nd_error_output = 0; +#endif + + if (!dying) + { + int err; + + dying = 1; + + if (print_version_flag) + print_version (); + +#ifdef KMK + /* Flag 2nd error message. */ + if (status != 0 + && ( job_slots_used > 0 + || print_data_base_flag + || print_stats_flag)) + { + need_2nd_error = 1; + need_2nd_error_output = job_slots_used >= 2 + && out != NULL + && out != &make_sync; + if (need_2nd_error_output) + output_metered = 0; + } +#endif /* KMK */ + + /* Wait for children to die. */ + err = (status != 0); + while (job_slots_used > 0) + reap_children (1, err); + + /* Let the remote job module clean up its state. */ + remote_cleanup (); + + /* Remove the intermediate files. */ + remove_intermediates (0); + + if (print_data_base_flag) + print_data_base (); + +#ifdef CONFIG_WITH_PRINT_STATS_SWITCH + if (print_stats_flag) + print_stats (); +#endif + if (verify_flag) + verify_file_data_base (); + +#ifdef NDEBUG /* bird: Don't waste time on debug sanity checks. */ + if (print_data_base_flag || db_level) +#endif + verify_file_data_base (); + + clean_jobserver (status); + + if (output_context) + { + /* die() might be called in a recipe output context due to an + $(error ...) function. */ + output_close (output_context); + + if (output_context != &make_sync) + output_close (&make_sync); + + OUTPUT_UNSET (); + } + + output_close (NULL); + + /* Try to move back to the original directory. This is essential on + MS-DOS (where there is really only one process), and on Unix it + puts core files in the original directory instead of the -C + directory. Must wait until after remove_intermediates(), or unlinks + of relative pathnames fail. */ + if (directory_before_chdir != 0) + { + /* If it fails we don't care: shut up GCC. */ + int _x UNUSED; + _x = chdir (directory_before_chdir); + } + +#ifdef CONFIG_WITH_PRINT_TIME_SWITCH + if (print_time_min != -1) + { + big_int elapsed = nano_timestamp () - make_start_ts; + if (elapsed >= print_time_min * BIG_INT_C(1000000000)) + { + char buf[64]; + format_elapsed_nano (buf, sizeof (buf), elapsed); + message (1, strlen (buf), _("%*s"), print_time_width, buf); + } + } +#endif + } + +#ifdef KMK + /* The failure might be lost in a -j <lots> run, so mention the + failure again before exiting. */ + if (need_2nd_error != 0) + ON (error, NILF, _("*** Exiting with status %d"), status); + if (out) + { + out->dont_truncate = 0; + if (need_2nd_error_output && output_metered > 20) + output_dump (out); + else + output_reset (out); + output_close (out); + } +#endif + + exit (status); +} + +#ifdef KMK +void die (int status) +{ + die_with_job_output (status, NULL); +} +#endif |