master xplshn/aruu / cmd / posix / make / main.c
  1#include <errno.h>
  2#include <signal.h>
  3#include <stdarg.h>
  4#include <stdio.h>
  5#include <stdlib.h>
  6#include <string.h>
  7
  8#include "make.h"
  9
 10#ifndef SIGINT
 11#define SIGINT -1
 12#endif
 13
 14#ifndef SIGTERM
 15#define SIGTERM -1
 16#endif
 17
 18#ifndef SIGQUIT
 19#define SIGQUIT -1
 20#endif
 21
 22#ifndef SIGHUP
 23#define SIGHUP -1
 24#endif
 25
 26int                   kflag, dflag, nflag, iflag, sflag;
 27int                   eflag, pflag, tflag, qflag;
 28int                   exitstatus;
 29volatile sig_atomic_t stop;
 30
 31void
 32debug(char *fmt, ...)
 33{
 34  va_list va;
 35
 36  if (!dflag)
 37    return;
 38
 39  va_start(va, fmt);
 40  vfprintf(stdout, fmt, va);
 41  fputc('\n', stdout);
 42  va_end(va);
 43}
 44
 45int
 46hash(char *name)
 47{
 48  int      c;
 49  unsigned h = 5381;
 50
 51  while ((c = *name++))
 52    h = h * 33 ^ c;
 53
 54  return h;
 55}
 56
 57void
 58sighandler(int signo)
 59{
 60  killchild();
 61  stop = signo;
 62}
 63
 64static void
 65usage(void)
 66{
 67  fputs(
 68      "usage: make [-eiknprSstd] [-f file] [-j jobs] "
 69      "[macro=value ...] [target ...]\n",
 70      stderr
 71  );
 72  exit(EXIT_FAILURE);
 73}
 74
 75static char *
 76getarg(char **args, char ***argv)
 77{
 78  char *s;
 79
 80  if ((*args)[1]) {
 81    s = (*args) + 1;
 82    *args += strlen(*args) - 1;
 83    return s;
 84  }
 85
 86  if (!argv)
 87    usage();
 88
 89  if ((*argv)[1] == NULL)
 90    usage();
 91  (*argv)++;
 92
 93  return **argv;
 94}
 95
 96static void
 97appendmakeflags(char *text)
 98{
 99  int   n;
100  char *s, *t, *fmt;
101
102  s   = getmacro("MAKEFLAGS");
103  fmt = *s ? "%s %s" : "%s%s";
104  n   = snprintf(NULL, 0, fmt, s, text);
105
106  t = emalloc(n + 1);
107  snprintf(t, n + 1, fmt, s, text);
108  setmacro("MAKEFLAGS", t, MAKEFLAGS, EXPORT);
109
110  free(t);
111}
112
113static int
114hasargs(int c)
115{
116  return c == 'f' || c == 'j';
117}
118
119static void
120parseflag(int flag, char **args, char ***argv)
121{
122  if (hasargs(flag))
123    getarg(args, argv);
124
125  switch (flag) {
126    case 'j':
127      /* ?man -j:jobs: number of parallel jobs (accepted, runs
128       * serially) */
129    case 'f':
130      /* ?man -f:file: read rules from file instead of makefile or
131       * Makefile */
132      break;
133    case 'e':
134      /* ?man -e: environment variables override makefile assignments
135       */
136      eflag = 1;
137      appendmakeflags("-e");
138      break;
139    case 'i':
140      /* ?man -i: ignore errors from executed commands */
141      iflag = 1;
142      appendmakeflags("-i");
143      break;
144    case 'k':
145      /* ?man -k: continue building other targets after an error */
146      kflag = 1;
147      appendmakeflags("-k");
148      break;
149    case 'n':
150      /* ?man -n: print commands without executing them */
151      nflag = 1;
152      appendmakeflags("-n");
153      break;
154    case 'p':
155      /* ?man -p: print the rule and macro database then exit */
156      pflag = 1;
157      break;
158    case 'q':
159      /* ?man -q: question mode, return zero if up to date, one
160       * otherwise */
161      qflag = 1;
162      appendmakeflags("-q");
163      break;
164    case 'r':
165      /* ?man -r: clear the built-in suffix rules and variables */
166      addtarget(".SUFFIXES", 0);
167      appendmakeflags("-r");
168      break;
169    case 'S':
170      /* ?man -S: cancel a previous -k, stop on first error */
171      kflag = 0;
172      appendmakeflags("-S");
173      break;
174    case 's':
175      /* ?man -s: silent, do not echo commands before running them */
176      sflag = 1;
177      appendmakeflags("-s");
178      break;
179    case 't':
180      /* ?man -t: touch targets instead of rebuilding them */
181      tflag = 1;
182      appendmakeflags("-t");
183      break;
184    case 'd':
185      /* ?man -d: enable debug output */
186      dflag = 1;
187      appendmakeflags("-d");
188      break;
189    default:
190      usage();
191  }
192}
193
194static int
195assign(char *s, int where, int export)
196{
197  int   pos;
198  char *t;
199
200  if ((t = strchr(s, '=')) == NULL)
201    return 0;
202
203  pos = t - s;
204
205  appendmakeflags(s);
206  t      = estrdup(s);
207  t[pos] = '\0';
208
209  setmacro(t, t + pos + 1, where, export);
210  free(t);
211  return 1;
212}
213
214static void
215parseargv(char **argv, char ***targets, int where, int export)
216{
217  char *s;
218  char *hm = NULL;
219
220  for (; *argv; ++argv) {
221    s = *argv;
222    if (hm == NULL && strcmp(s, "--") == 0) {
223      hm = *argv;
224    } else if (hm && s[0] == '-') {
225      break;
226    } else if (s[0] != '-') {
227      if (!assign(s, where, export))
228        break;
229      continue;
230    }
231    while (hm == NULL && *++s)
232      parseflag(*s, &s, &argv);
233  }
234
235  if (targets)
236    *targets = argv;
237}
238
239static void
240parsemakeflags(void)
241{
242  int   n;
243  char *s, *flags, **arr;
244
245  if ((flags = getenv("MAKEFLAGS")) == NULL)
246    return;
247
248  setmacro("MAKEFLAGS", "", MAKEFLAGS, EXPORT);
249
250  while (*flags == ' ' || *flags == '\t')
251    flags++;
252
253  if (flags[0] != '-' && !strchr(flags, '=')) {
254    while (*flags) {
255      parseflag(*flags, &flags, NULL);
256      flags++;
257    }
258  } else {
259    n   = 0;
260    arr = NULL;
261    for (s = strtok(flags, " \t"); s; s = strtok(NULL, " \t")) {
262      n++;
263      arr        = erealloc(arr, sizeof(char *) * (n + 1));
264      arr[n - 1] = s;
265      arr[n]     = NULL;
266    }
267
268    if (arr)
269      parseargv(arr, NULL, MAKEFLAGS, NOEXPORT);
270    free(arr);
271  }
272}
273
274static void
275parsemakefiles(char **argv)
276{
277  char *s, *arg;
278  int   c, hasmake;
279
280  hasmake = 0;
281  for (; *argv && **argv == '-'; ++argv) {
282    for (s = *argv; (c = *s); ++s) {
283      if (hasargs(c))
284        arg = getarg(&s, &argv);
285
286      if (c == 'f') {
287        if (strcmp(arg, "-") == 0)
288          arg = NULL;
289        parse(arg);
290        hasmake = 1;
291      }
292    }
293  }
294
295  if (hasmake)
296    return;
297
298  if (parse("makefile"))
299    return;
300  if (parse("Makefile"))
301    return;
302}
303
304/*
305 * We want to enable debug as earlier as possible,
306 * if we wait until we read the Makefiles then
307 * we are going to lose to much debug information.
308 */
309static void
310enadebug(char *argv[])
311{
312  int   c;
313  char *p;
314
315  for (; *argv && **argv == '-'; ++argv) {
316    p = *argv;
317    for (++p; (c = *p); ++p) {
318      if (hasargs(c))
319        getarg(&p, &argv);
320      if (c == 'd')
321        dflag = 1;
322    }
323  }
324}
325
326// ?man make: maintain program dependencies
327// ?man arguments: [name=value ...] [target ...]
328// ?man update files based on dependency rules in a makefile.
329// ?man if no -f is given, make tries makefile then Makefile.
330// ?man if no target is given the first non-dot target in the makefile is built.
331int
332main(int argc, char *argv[])
333{
334  char *arg0, **targets;
335
336  (void)argc;
337
338  signal(SIGINT, sighandler);
339  signal(SIGHUP, sighandler);
340  signal(SIGTERM, sighandler);
341  signal(SIGQUIT, sighandler);
342
343  targets = NULL;
344  arg0    = *argv++;
345
346  enadebug(argv);
347  inject(defaults);
348  setmacro("MAKE", arg0, MAKEFILE, NOEXPORT);
349
350  parsemakeflags();
351  parseargv(argv, &targets, CMDLINE, EXPORT);
352  parsemakefiles(argv);
353
354  if (pflag) {
355    dumpmacros();
356    dumprules();
357    exit(EXIT_SUCCESS);
358  }
359
360  if (!*targets) {
361    build(NULL);
362  } else {
363    while (*targets)
364      build(*targets++);
365  }
366
367  exit(exitstatus);
368
369  return 0;
370}