├── LICENSE ├── Makefile ├── README.md ├── check.c ├── input.c ├── macro.c ├── main.c ├── make.c ├── make.h ├── modtime.c ├── pdpmake.1 ├── rules.c ├── target.c ├── testsuite ├── LICENSE ├── make.tests ├── runtest └── testing.sh └── utils.c /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 9 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 10 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 11 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 12 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 13 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 14 | OTHER DEALINGS IN THE SOFTWARE. 15 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for make! 2 | .POSIX: 3 | .PHONY: install uninstall test clean 4 | 5 | PREFIX = /usr/local 6 | BINDIR = $(PREFIX)/bin 7 | MANDIR = $(PREFIX)/share/man 8 | 9 | OBJS = check.o input.o macro.o main.o make.o modtime.o rules.o target.o utils.o 10 | 11 | make: $(OBJS) 12 | $(CC) $(LDFLAGS) -o make $(OBJS) 13 | 14 | $(OBJS): make.h 15 | 16 | install: make 17 | test -d $(DESTDIR)$(BINDIR) || mkdir -p $(DESTDIR)$(BINDIR) 18 | cp -f make $(DESTDIR)$(BINDIR)/pdpmake 19 | test -d $(DESTDIR)$(MANDIR)/man1 || mkdir -p $(DESTDIR)$(MANDIR)/man1 20 | cp -f pdpmake.1 $(DESTDIR)$(MANDIR)/man1/pdpmake.1 21 | 22 | uninstall: 23 | rm -f $(DESTDIR)$(BINDIR)/pdpmake 24 | rm -f $(DESTDIR)$(MANDIR)/man1/pdpmake.1 25 | 26 | test: make 27 | @cd testsuite && ./runtest 28 | 29 | clean: 30 | rm -f $(OBJS) make 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Public domain POSIX make 2 | 3 | This is an implementation of [POSIX make](https://pubs.opengroup.org/onlinepubs/9799919799/utilities/make.html). 4 | 5 | It should build on most modernish Unix-style systems: 6 | 7 | - It comes with its own makefile, naturally, but if you don't have a `make` binary already the command `cc -o make *.c` should get you started. 8 | 9 | - Command line options may not work properly due to differences in how `getopt(3)` is reset. Adjust `GETOPT_RESET()` in make.h for your platform, if necessary. 10 | 11 | - Building on MacOS may require a command like: `make CFLAGS="-D_DARWIN_C_SOURCE -Dst_mtim=st_mtimespec"` 12 | 13 | Microsoft Windows users will find `pdpmake` included in the 14 | [BusyBox for Windows](https://frippery.org/busybox/index.html) binaries. 15 | Download an appropriate binary for your system and rename it `make.exe` or 16 | `pdpmake.exe`. BusyBox for Windows includes a Unix shell and many utilities. 17 | These can be used in Makefiles without any further setup. 18 | 19 | The default configuration enables some non-POSIX extensions. Generally 20 | these are compatible with GNU make: 21 | 22 | - double-colon rules 23 | - `ifdef`/`ifndef`/`ifeq`/`ifneq`/`else`/`endif` conditionals 24 | - `lib.a(mem1.o mem2.o...)` syntax for archive members 25 | - `:=` macro assignments (equivalent to POSIX `::=`) 26 | - chained inference rules 27 | - `*`/`?`/`[]` wildcards for filenames in target rules 28 | - the `$<` and `$*` internal macros are valid for target rules 29 | - skip duplicate entries in `$?` 30 | - `-C directory` command line option 31 | - `#` doesn't start a comment in macro expansions or build commands 32 | - `#` may be escaped with a backslash 33 | - macro definitions and targets can be mixed on the command line 34 | 35 | When extensions are enabled adding the `.POSIX` target to your makefile 36 | will disable them. Other versions of make tend to allow extensions even 37 | in POSIX mode. 38 | 39 | Setting the environment variable `PDPMAKE_POSIXLY_CORRECT` (its value 40 | doesn't matter) or giving the `--posix` option as the first on the 41 | command line also turn off extensions. 42 | 43 | [Additional information](https://frippery.org/make/index.html) and 44 | [release notes](https://frippery.org/make/release-notes/current.html) 45 | are available. 46 | -------------------------------------------------------------------------------- /check.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Check structures for make. 3 | */ 4 | #include "make.h" 5 | 6 | static void 7 | print_name(struct name *np) 8 | { 9 | if (np == firstname) 10 | printf("# default target\n"); 11 | printf("%s:", np->n_name); 12 | if ((np->n_flag & N_DOUBLE)) 13 | putchar(':'); 14 | } 15 | 16 | static void 17 | print_prerequisites(struct rule *rp) 18 | { 19 | struct depend *dp; 20 | 21 | for (dp = rp->r_dep; dp; dp = dp->d_next) 22 | printf(" %s", dp->d_name->n_name); 23 | } 24 | 25 | static void 26 | print_commands(struct rule *rp) 27 | { 28 | struct cmd *cp; 29 | 30 | for (cp = rp->r_cmd; cp; cp = cp->c_next) 31 | printf("\t%s\n", cp->c_cmd); 32 | } 33 | 34 | void 35 | print_details(void) 36 | { 37 | int i; 38 | struct macro *mp; 39 | struct name *np; 40 | struct rule *rp; 41 | 42 | for (i = 0; i < HTABSIZE; i++) 43 | for (mp = macrohead[i]; mp; mp = mp->m_next) 44 | printf("%s = %s\n", mp->m_name, mp->m_val); 45 | putchar('\n'); 46 | 47 | for (i = 0; i < HTABSIZE; i++) { 48 | for (np = namehead[i]; np; np = np->n_next) { 49 | if (!(np->n_flag & N_DOUBLE)) { 50 | print_name(np); 51 | for (rp = np->n_rule; rp; rp = rp->r_next) { 52 | print_prerequisites(rp); 53 | } 54 | putchar('\n'); 55 | 56 | for (rp = np->n_rule; rp; rp = rp->r_next) { 57 | print_commands(rp); 58 | } 59 | putchar('\n'); 60 | } else { 61 | for (rp = np->n_rule; rp; rp = rp->r_next) { 62 | print_name(np); 63 | print_prerequisites(rp); 64 | putchar('\n'); 65 | 66 | print_commands(rp); 67 | putchar('\n'); 68 | } 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /input.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Parse a makefile 3 | */ 4 | #include "make.h" 5 | #include 6 | 7 | int lineno; // Physical line number in file 8 | int dispno; // Line number for display purposes 9 | 10 | /* 11 | * Return a pointer to the next blank-delimited word or NULL if 12 | * there are none left. 13 | */ 14 | static char * 15 | gettok(char **ptr) 16 | { 17 | char *p; 18 | 19 | while (isblank(**ptr)) // Skip blanks 20 | (*ptr)++; 21 | 22 | if (**ptr == '\0') // Nothing after blanks 23 | return NULL; 24 | 25 | p = *ptr; // Word starts here 26 | 27 | while (**ptr != '\0' && !isblank(**ptr)) 28 | (*ptr)++; // Find end of word 29 | 30 | // Terminate token and move on unless already at end of string 31 | if (**ptr != '\0') 32 | *(*ptr)++ = '\0'; 33 | 34 | return(p); 35 | } 36 | 37 | /* 38 | * Skip over (possibly adjacent or nested) macro expansions. 39 | */ 40 | static char * 41 | skip_macro(const char *s) 42 | { 43 | while (*s && s[0] == '$') { 44 | if (s[1] == '(' || s[1] == '{') { 45 | char end = *++s == '(' ? ')' : '}'; 46 | while (*s && *s != end) 47 | s = skip_macro(s + 1); 48 | if (*s == end) 49 | ++s; 50 | } else if (s[1] != '\0') { 51 | s += 2; 52 | } else { 53 | break; 54 | } 55 | } 56 | return (char *)s; 57 | } 58 | 59 | #if !ENABLE_FEATURE_MAKE_POSIX_2024 60 | # define modify_words(v, m, lf, lr, fp, rp, fs, rs) \ 61 | modify_words(v, m, lf, lr, fs, rs) 62 | #endif 63 | /* 64 | * Process each whitespace-separated word in the input string: 65 | * 66 | * - replace paths with their directory or filename part 67 | * - replace prefixes and suffixes 68 | * 69 | * Returns an allocated string or NULL if the input is unmodified. 70 | */ 71 | static char * 72 | modify_words(const char *val, int modifier, size_t lenf, size_t lenr, 73 | const char *find_pref, const char *repl_pref, 74 | const char *find_suff, const char *repl_suff) 75 | { 76 | char *s, *copy, *word, *sep, *newword, *buf = NULL; 77 | #if ENABLE_FEATURE_MAKE_POSIX_2024 78 | size_t find_pref_len = 0, find_suff_len = 0; 79 | #endif 80 | 81 | if (!modifier && lenf == 0 && lenr == 0) 82 | return buf; 83 | 84 | #if ENABLE_FEATURE_MAKE_POSIX_2024 85 | if (find_pref) { 86 | // get length of find prefix, e.g: src/ 87 | find_pref_len = strlen(find_pref); 88 | // get length of find suffix, e.g: .c 89 | find_suff_len = lenf - find_pref_len - 1; 90 | } 91 | #endif 92 | 93 | s = copy = xstrdup(val); 94 | while ((word = gettok(&s)) != NULL) { 95 | newword = NULL; 96 | if (modifier) { 97 | sep = strrchr(word, '/'); 98 | if (modifier == 'D') { 99 | if (!sep) { 100 | word[0] = '.'; // no '/', return "." 101 | sep = word + 1; 102 | } else if (sep == word) { 103 | // '/' at start of word, return "/" 104 | sep = word + 1; 105 | } 106 | // else terminate at separator 107 | *sep = '\0'; 108 | } else if (/* modifier == 'F' && */ sep) { 109 | word = sep + 1; 110 | } 111 | } 112 | if (IF_FEATURE_MAKE_POSIX_2024(find_pref != NULL ||) 113 | lenf != 0 || lenr != 0) { 114 | size_t lenw = strlen(word); 115 | #if ENABLE_FEATURE_MAKE_POSIX_2024 116 | // This code implements pattern macro expansions: 117 | // https://austingroupbugs.net/view.php?id=519 118 | // 119 | // find: % 120 | // example: src/%.c 121 | // 122 | // For a pattern of the form: 123 | // $(string1:[op]%[os]=[np][%][ns]) 124 | // lenf is the length of [op]%[os]. So lenf >= 1. 125 | if (find_pref != NULL && lenw + 1 >= lenf) { 126 | // If prefix and suffix of word match find_pref and 127 | // find_suff, then do substitution. 128 | if (strncmp(word, find_pref, find_pref_len) == 0 && 129 | strcmp(word + lenw - find_suff_len, find_suff) == 0) { 130 | // replace: [%] 131 | // example: build/%.o or build/all.o (notice no %) 132 | // If repl_suff is NULL, replace whole word with repl_pref. 133 | if (!repl_suff) { 134 | word = newword = xstrdup(repl_pref); 135 | } else { 136 | word[lenw - find_suff_len] = '\0'; 137 | word = newword = xconcat3(repl_pref, 138 | word + find_pref_len, repl_suff); 139 | } 140 | } 141 | } else 142 | #endif 143 | if (lenw >= lenf && strcmp(word + lenw - lenf, find_suff) == 0) { 144 | word[lenw - lenf] = '\0'; 145 | word = newword = xconcat3(word, repl_suff, ""); 146 | } 147 | } 148 | buf = xappendword(buf, word); 149 | free(newword); 150 | } 151 | free(copy); 152 | return buf; 153 | } 154 | 155 | /* 156 | * Return a pointer to the next instance of a given character. Macro 157 | * expansions are skipped so the ':' and '=' in $(VAR:.s1=.s2) aren't 158 | * detected as separators in macro definitions. Some other situations 159 | * also require skipping the internals of a macro expansion. 160 | */ 161 | static char * 162 | find_char(const char *str, int c) 163 | { 164 | const char *s; 165 | 166 | for (s = skip_macro(str); *s; s = skip_macro(s + 1)) { 167 | if (*s == c) 168 | return (char *)s; 169 | } 170 | return NULL; 171 | } 172 | 173 | #if ENABLE_FEATURE_MAKE_EXTENSIONS && defined(__CYGWIN__) 174 | /* 175 | * Check for a target rule by searching for a colon that isn't 176 | * part of a Windows path. Return a pointer to the colon or NULL. 177 | */ 178 | static char * 179 | find_colon(char *p) 180 | { 181 | char *q; 182 | 183 | for (q = p; (q = strchr(q, ':')); ++q) { 184 | if (posix && !(pragma & P_WINDOWS)) 185 | break; 186 | if (q == p || !isalpha(q[-1]) || q[1] != '/') 187 | break; 188 | } 189 | return q; 190 | } 191 | #else 192 | # define find_colon(s) strchr(s, ':') 193 | #endif 194 | 195 | /* 196 | * Recursively expand any macros in str to an allocated string. 197 | */ 198 | char * 199 | expand_macros(const char *str, int except_dollar) 200 | { 201 | char *exp, *newexp, *s, *t, *p, *q, *name; 202 | char *find, *replace, *modified; 203 | char *expval, *expfind, *find_suff, *repl_suff; 204 | #if ENABLE_FEATURE_MAKE_POSIX_2024 205 | char *find_pref = NULL, *repl_pref = NULL; 206 | #endif 207 | size_t lenf, lenr; 208 | char modifier; 209 | struct macro *mp; 210 | 211 | exp = xstrdup(str); 212 | for (t = exp; *t; t++) { 213 | if (*t == '$') { 214 | if (t[1] == '\0') { 215 | break; 216 | } 217 | #if ENABLE_FEATURE_MAKE_POSIX_2024 218 | if (t[1] == '$' && except_dollar) { 219 | t++; 220 | continue; 221 | } 222 | #endif 223 | // Need to expand a macro. Find its extent (s to t inclusive) 224 | // and take a copy of its content. 225 | s = t; 226 | t++; 227 | if (*t == '{' || *t == '(') { 228 | t = find_char(t, *t == '{' ? '}' : ')'); 229 | if (t == NULL) 230 | error("unterminated variable '%s'", s); 231 | name = xstrndup(s + 2, t - s - 2); 232 | } else { 233 | name = xmalloc(2); 234 | name[0] = *t; 235 | name[1] = '\0'; 236 | } 237 | 238 | // Only do suffix replacement or pattern macro expansion 239 | // if both ':' and '=' are found, plus a '%' for the latter. 240 | // Suffix replacement is indicated by 241 | // find_pref == NULL && (lenf != 0 || lenr != 0); 242 | // pattern macro expansion by find_pref != NULL. 243 | expfind = NULL; 244 | find_suff = repl_suff = NULL; 245 | lenf = lenr = 0; 246 | if ((find = find_char(name, ':'))) { 247 | *find++ = '\0'; 248 | expfind = expand_macros(find, FALSE); 249 | if ((replace = find_char(expfind, '='))) { 250 | *replace++ = '\0'; 251 | lenf = strlen(expfind); 252 | #if ENABLE_FEATURE_MAKE_POSIX_2024 253 | if (!POSIX_2017 && (find_suff = strchr(expfind, '%'))) { 254 | find_pref = expfind; 255 | repl_pref = replace; 256 | *find_suff++ = '\0'; 257 | if ((repl_suff = strchr(replace, '%'))) 258 | *repl_suff++ = '\0'; 259 | } else 260 | #endif 261 | { 262 | if (IF_FEATURE_MAKE_EXTENSIONS(posix && 263 | !(pragma & P_EMPTY_SUFFIX) &&) 264 | lenf == 0) 265 | error("empty suffix%s", 266 | !ENABLE_FEATURE_MAKE_EXTENSIONS ? "" : 267 | ": allow with pragma empty_suffix"); 268 | find_suff = expfind; 269 | repl_suff = replace; 270 | lenr = strlen(repl_suff); 271 | } 272 | } 273 | } 274 | 275 | p = q = name; 276 | #if ENABLE_FEATURE_MAKE_POSIX_2024 277 | // If not in POSIX mode expand macros in the name. 278 | if (!POSIX_2017) { 279 | char *expname = expand_macros(name, FALSE); 280 | free(name); 281 | name = expname; 282 | } else 283 | #endif 284 | // Skip over nested expansions in name 285 | do { 286 | *q++ = *p; 287 | } while ((p = skip_macro(p + 1)) && *p); 288 | 289 | // The internal macros support 'D' and 'F' modifiers 290 | modifier = '\0'; 291 | switch (name[0]) { 292 | #if ENABLE_FEATURE_MAKE_POSIX_2024 293 | case '^': 294 | case '+': 295 | if (POSIX_2017) 296 | break; 297 | // fall through 298 | #endif 299 | case '@': case '%': case '?': case '<': case '*': 300 | if ((name[1] == 'D' || name[1] == 'F') && name[2] == '\0') { 301 | modifier = name[1]; 302 | name[1] = '\0'; 303 | } 304 | break; 305 | } 306 | 307 | modified = NULL; 308 | if ((mp = getmp(name))) { 309 | // Recursive expansion 310 | if (mp->m_flag) 311 | error("recursive macro %s", name); 312 | #if ENABLE_FEATURE_MAKE_POSIX_2024 313 | // Note if we've expanded $(MAKE) 314 | if (strcmp(name, "MAKE") == 0) 315 | opts |= OPT_make; 316 | #endif 317 | mp->m_flag = TRUE; 318 | expval = expand_macros(mp->m_val, FALSE); 319 | mp->m_flag = FALSE; 320 | modified = modify_words(expval, modifier, lenf, lenr, 321 | find_pref, repl_pref, find_suff, repl_suff); 322 | if (modified) 323 | free(expval); 324 | else 325 | modified = expval; 326 | } 327 | free(name); 328 | free(expfind); 329 | 330 | if (modified && *modified) { 331 | // The text to be replaced by the macro expansion is 332 | // from s to t inclusive. 333 | *s = '\0'; 334 | newexp = xconcat3(exp, modified, t + 1); 335 | t = newexp + (s - exp) + strlen(modified) - 1; 336 | free(exp); 337 | exp = newexp; 338 | } else { 339 | // Macro wasn't expanded or expanded to nothing. 340 | // Close the space occupied by the macro reference. 341 | q = t + 1; 342 | t = s - 1; 343 | while ((*s++ = *q++)) 344 | continue; 345 | } 346 | free(modified); 347 | } 348 | } 349 | return exp; 350 | } 351 | 352 | /* 353 | * Process a non-command line 354 | */ 355 | static void 356 | process_line(char *s) 357 | { 358 | char *t; 359 | 360 | // Strip comment 361 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 362 | // don't treat '#' in macro expansion as a comment 363 | // nor '#' outside macro expansion preceded by backslash 364 | if (!posix) { 365 | char *u = s; 366 | while ((t = find_char(u, '#')) && t > u && t[-1] == '\\') { 367 | for (u = t; *u; ++u) { 368 | u[-1] = u[0]; 369 | } 370 | *u = '\0'; 371 | u = t; 372 | } 373 | } else 374 | #endif 375 | t = strchr(s, '#'); 376 | if (t) 377 | *t = '\0'; 378 | 379 | // Replace escaped newline and any leading white space on the 380 | // following line with a single space. Stop processing at a 381 | // non-escaped newline. 382 | for (t = s; *s && *s != '\n'; ) { 383 | if (s[0] == '\\' && s[1] == '\n') { 384 | s += 2; 385 | while (isspace(*s)) 386 | ++s; 387 | *t++ = ' '; 388 | } else { 389 | *t++ = *s++; 390 | } 391 | } 392 | *t = '\0'; 393 | } 394 | 395 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 396 | enum { 397 | INITIAL = 0, 398 | SKIP_LINE = 1 << 0, 399 | EXPECT_ELSE = 1 << 1, 400 | GOT_MATCH = 1 << 2 401 | }; 402 | 403 | #define IF_MAX 10 404 | 405 | static uint8_t clevel = 0; 406 | static uint8_t cstate[IF_MAX + 1] = {INITIAL}; 407 | 408 | /* 409 | * Extract strings following ifeq/ifneq and compare them. 410 | * Return -1 on error. 411 | */ 412 | static int 413 | compare_strings(char *arg1) 414 | { 415 | char *arg2, *end, term, *t1, *t2; 416 | int ret; 417 | 418 | // Get first string terminator. 419 | if (arg1[0] == '(') 420 | term = ','; 421 | else if (arg1[0] == '"' || arg1[0] == '\'') 422 | term = arg1[0]; 423 | else 424 | return -1; 425 | 426 | arg2 = find_char(++arg1, term); 427 | if (arg2 == NULL) 428 | return -1; 429 | *arg2++ = '\0'; 430 | 431 | // Get second string terminator. 432 | if (term == ',') { 433 | term = ')'; 434 | } else { 435 | // Skip spaces between quoted strings. 436 | while (isspace(arg2[0])) 437 | arg2++; 438 | if (arg2[0] == '"' || arg2[0] == '\'') 439 | term = arg2[0]; 440 | else 441 | return -1; 442 | ++arg2; 443 | } 444 | 445 | end = find_char(arg2, term); 446 | if (end == NULL) 447 | return -1; 448 | *end++ = '\0'; 449 | 450 | if (gettok(&end) != NULL) { 451 | warning("unexpected text"); 452 | } 453 | 454 | t1 = expand_macros(arg1, FALSE); 455 | t2 = expand_macros(arg2, FALSE); 456 | 457 | ret = strcmp(t1, t2) == 0; 458 | free(t1); 459 | free(t2); 460 | return ret; 461 | } 462 | 463 | /* 464 | * Process conditional directives and return TRUE if the current line 465 | * should be skipped. 466 | */ 467 | static int 468 | skip_line(const char *str1) 469 | { 470 | char *copy, *q, *token; 471 | bool new_level = TRUE; 472 | // Default is to return skip flag for current level 473 | int ret = cstate[clevel] & SKIP_LINE; 474 | 475 | q = copy = xstrdup(str1); 476 | process_line(copy); 477 | if ((token = gettok(&q)) != NULL) { 478 | if (strcmp(token, "endif") == 0) { 479 | if (gettok(&q) != NULL) 480 | error_unexpected("text"); 481 | if (clevel == 0) 482 | error_unexpected(token); 483 | --clevel; 484 | ret = TRUE; 485 | goto end; 486 | } else if (strcmp(token, "else") == 0) { 487 | if (!(cstate[clevel] & EXPECT_ELSE)) 488 | error_unexpected(token); 489 | 490 | // If an earlier condition matched we'll now skip lines. 491 | // If not we don't, though an 'else if' may override this. 492 | if ((cstate[clevel] & GOT_MATCH)) 493 | cstate[clevel] |= SKIP_LINE; 494 | else 495 | cstate[clevel] &= ~SKIP_LINE; 496 | 497 | token = gettok(&q); 498 | if (token == NULL) { 499 | // Simple else with no conditional directive 500 | cstate[clevel] &= ~EXPECT_ELSE; 501 | ret = TRUE; 502 | goto end; 503 | } else { 504 | // A conditional directive is now required ('else if'). 505 | new_level = FALSE; 506 | } 507 | } 508 | 509 | if (strcmp(token, "ifdef") == 0 || strcmp(token, "ifndef") == 0 || 510 | strcmp(token, "ifeq") == 0 || strcmp(token, "ifneq") == 0) { 511 | int match; 512 | 513 | if (token[2] == 'd' || token[3] == 'd') { 514 | // ifdef/ifndef: find out if macro is defined. 515 | char *name = gettok(&q); 516 | if (name != NULL && gettok(&q) == NULL) { 517 | char *t = expand_macros(name, FALSE); 518 | struct macro *mp = getmp(t); 519 | match = mp != NULL && mp->m_val[0] != '\0'; 520 | free(t); 521 | } else { 522 | match = -1; 523 | } 524 | } else { 525 | // ifeq/ifneq: compare strings. 526 | match = compare_strings(q); 527 | } 528 | 529 | if (match >= 0) { 530 | if (new_level) { 531 | // Start a new level. 532 | if (clevel == IF_MAX) 533 | error("nesting too deep"); 534 | ++clevel; 535 | cstate[clevel] = EXPECT_ELSE | SKIP_LINE; 536 | // If we were skipping lines at the previous level 537 | // we need to continue doing that unconditionally 538 | // at the new level. 539 | if ((cstate[clevel - 1] & SKIP_LINE)) 540 | cstate[clevel] |= GOT_MATCH; 541 | } 542 | 543 | if (!(cstate[clevel] & GOT_MATCH)) { 544 | if (token[2] == 'n') 545 | match = !match; 546 | if (match) { 547 | cstate[clevel] &= ~SKIP_LINE; 548 | cstate[clevel] |= GOT_MATCH; 549 | } 550 | } 551 | } else { 552 | error("invalid condition"); 553 | } 554 | ret = TRUE; 555 | } else if (!new_level) { 556 | error("missing conditional"); 557 | } 558 | } 559 | end: 560 | free(copy); 561 | return ret; 562 | } 563 | #endif 564 | 565 | /* 566 | * If fd is NULL read the built-in rules. Otherwise read from the 567 | * specified file descriptor. 568 | */ 569 | static char * 570 | make_fgets(char *s, int size, FILE *fd) 571 | { 572 | return fd ? fgets(s, size, fd) : getrules(s, size); 573 | } 574 | 575 | /* 576 | * Read a newline-terminated line into an allocated string. 577 | * Backslash-escaped newlines don't terminate the line. 578 | * Ignore comment lines. Return NULL on EOF. 579 | */ 580 | static char * 581 | readline(FILE *fd, int want_command) 582 | { 583 | char *p, *str = NULL; 584 | int pos = 0; 585 | int len = 0; 586 | 587 | for (;;) { 588 | // We need room for at least one character and a NUL terminator 589 | if (len - pos > 1 && 590 | make_fgets(str + pos, len - pos, fd) == NULL) { 591 | if (pos) 592 | return str; 593 | free(str); 594 | return NULL; // EOF 595 | } 596 | 597 | if (len - pos < 2 || (p = strchr(str + pos, '\n')) == NULL) { 598 | // Need more room 599 | if (len) 600 | pos = len - 1; 601 | len += 256; 602 | str = xrealloc(str, len); 603 | continue; 604 | } 605 | lineno++; 606 | 607 | // Remove CR before LF 608 | if (p != str && p[-1] == '\r') { 609 | p[-1] = '\n'; 610 | *p-- = '\0'; 611 | } 612 | 613 | // Keep going if newline has been escaped 614 | if (p != str && p[-1] == '\\') { 615 | pos = p - str + 1; 616 | continue; 617 | } 618 | dispno = lineno; 619 | 620 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 621 | // Check for lines that are conditionally skipped. 622 | if (posix || !skip_line(str)) 623 | #endif 624 | { 625 | if (want_command && *str == '\t') 626 | return str; 627 | 628 | // Check for comment lines 629 | p = str; 630 | while (isblank(*p)) 631 | p++; 632 | 633 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 634 | if (*p != '\n' && (posix ? *str != '#' : *p != '#')) 635 | #else 636 | if (*p != '\n' && *str != '#') 637 | #endif 638 | return str; 639 | } 640 | 641 | pos = 0; 642 | } 643 | } 644 | 645 | /* 646 | * Return a pointer to the suffix name if the argument is a known suffix 647 | * or NULL if it isn't. 648 | */ 649 | const char * 650 | is_suffix(const char *s) 651 | { 652 | struct name *np; 653 | struct rule *rp; 654 | struct depend *dp; 655 | 656 | np = newname(".SUFFIXES"); 657 | for (rp = np->n_rule; rp; rp = rp->r_next) { 658 | for (dp = rp->r_dep; dp; dp = dp->d_next) { 659 | if (strcmp(s, dp->d_name->n_name) == 0) { 660 | return dp->d_name->n_name; 661 | } 662 | } 663 | } 664 | return NULL; 665 | } 666 | 667 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 668 | /* 669 | * Return TRUE if the argument is formed by concatenating two 670 | * known suffixes. 671 | */ 672 | int 673 | is_inference_target(const char *s) 674 | { 675 | struct name *np; 676 | struct rule *rp1, *rp2; 677 | struct depend *dp1, *dp2; 678 | 679 | np = newname(".SUFFIXES"); 680 | for (rp1 = np->n_rule; rp1; rp1 = rp1->r_next) { 681 | for (dp1 = rp1->r_dep; dp1; dp1 = dp1->d_next) { 682 | const char *suff1 = dp1->d_name->n_name; 683 | size_t len = strlen(suff1); 684 | 685 | if (strncmp(s, suff1, len) == 0) { 686 | for (rp2 = np->n_rule; rp2; rp2 = rp2->r_next) { 687 | for (dp2 = rp2->r_dep; dp2; dp2 = dp2->d_next) { 688 | const char *suff2 = dp2->d_name->n_name; 689 | if (strcmp(s + len, suff2) == 0) { 690 | return TRUE; 691 | } 692 | } 693 | } 694 | } 695 | } 696 | } 697 | return FALSE; 698 | } 699 | #endif 700 | 701 | enum { 702 | T_NORMAL = 0, 703 | T_SPECIAL = (1 << 0), 704 | T_INFERENCE = (1 << 1), // Inference rule 705 | T_NOPREREQ = (1 << 2), // If set must not have prerequisites 706 | T_COMMAND = (1 << 3), // If set must have commands, if unset must not 707 | }; 708 | 709 | /* 710 | * Determine if the argument is a special target and return a set 711 | * of flags indicating its properties. 712 | */ 713 | static int 714 | target_type(char *s) 715 | { 716 | int ret; 717 | static const char *s_name[] = { 718 | ".DEFAULT", 719 | ".POSIX", 720 | ".IGNORE", 721 | ".PRECIOUS", 722 | ".SILENT", 723 | ".SUFFIXES", 724 | #if ENABLE_FEATURE_MAKE_POSIX_2024 725 | ".PHONY", 726 | ".NOTPARALLEL", 727 | ".WAIT", 728 | #endif 729 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 730 | ".PRAGMA", 731 | #endif 732 | }; 733 | 734 | static const uint8_t s_type[] = { 735 | T_SPECIAL | T_NOPREREQ | T_COMMAND, 736 | T_SPECIAL | T_NOPREREQ, 737 | T_SPECIAL, 738 | T_SPECIAL, 739 | T_SPECIAL, 740 | T_SPECIAL, 741 | #if ENABLE_FEATURE_MAKE_POSIX_2024 742 | T_SPECIAL, 743 | T_SPECIAL | T_NOPREREQ, 744 | T_SPECIAL | T_NOPREREQ, 745 | #endif 746 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 747 | T_SPECIAL, 748 | #endif 749 | }; 750 | 751 | // Check for one of the known special targets 752 | for (ret = 0; ret < sizeof(s_name)/sizeof(s_name[0]); ret++) 753 | if (strcmp(s_name[ret], s) == 0) 754 | return s_type[ret]; 755 | 756 | // Check for an inference rule 757 | ret = T_NORMAL; 758 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 759 | if (!posix) { 760 | if (is_suffix(s) || is_inference_target(s)) { 761 | ret = T_INFERENCE | T_NOPREREQ | T_COMMAND; 762 | } 763 | } else 764 | #endif 765 | { 766 | // In POSIX inference rule targets must contain one or two dots 767 | char *sfx = suffix(s); 768 | if (*s == '.' && is_suffix(sfx)) { 769 | if (s == sfx) { // Single suffix rule 770 | ret = T_INFERENCE | T_NOPREREQ | T_COMMAND; 771 | } else { 772 | // Suffix is valid, check that prefix is too 773 | *sfx = '\0'; 774 | if (is_suffix(s)) 775 | ret = T_INFERENCE | T_NOPREREQ | T_COMMAND; 776 | *sfx = '.'; 777 | } 778 | } 779 | } 780 | return ret; 781 | } 782 | 783 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 784 | static int 785 | ends_with_bracket(const char *s) 786 | { 787 | const char *t = strrchr(s, ')'); 788 | return t && t[1] == '\0'; 789 | } 790 | #endif 791 | 792 | /* 793 | * Process a command line 794 | */ 795 | static char * 796 | process_command(char *s) 797 | { 798 | char *t, *u; 799 | #if ENABLE_FEATURE_MAKE_POSIX_2024 800 | int len; 801 | char *outside; 802 | #endif 803 | 804 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 805 | if (!(pragma & P_COMMAND_COMMENT) && posix) { 806 | // POSIX strips comments from command lines 807 | t = strchr(s, '#'); 808 | if (t) { 809 | *t = '\0'; 810 | warning("comment in command removed: keep with pragma command_comment"); 811 | } 812 | } 813 | #endif 814 | 815 | #if ENABLE_FEATURE_MAKE_POSIX_2024 816 | len = strlen(s) + 1; 817 | outside = xmalloc(len); 818 | memset(outside, 0, len); 819 | for (t = skip_macro(s); *t; t = skip_macro(t + 1)) { 820 | outside[t - s] = 1; 821 | } 822 | #endif 823 | 824 | // Process escaped newlines. Stop at first non-escaped newline. 825 | for (t = u = s; *u && *u != '\n'; ) { 826 | if (u[0] == '\\' && u[1] == '\n') { 827 | #if ENABLE_FEATURE_MAKE_POSIX_2024 828 | if (POSIX_2017 || outside[u - s]) { 829 | #endif 830 | // Outside macro: remove tab following escaped newline. 831 | *t++ = *u++; 832 | *t++ = *u++; 833 | u += (*u == '\t'); 834 | #if ENABLE_FEATURE_MAKE_POSIX_2024 835 | } else { 836 | // Inside macro: replace escaped newline and any leading 837 | // whitespace on the following line with a single space. 838 | u += 2; 839 | while (isspace(*u)) 840 | ++u; 841 | *t++ = ' '; 842 | } 843 | #endif 844 | } else { 845 | *t++ = *u++; 846 | } 847 | } 848 | *t = '\0'; 849 | #if ENABLE_FEATURE_MAKE_POSIX_2024 850 | free(outside); 851 | #endif 852 | return s; 853 | } 854 | 855 | #if ENABLE_FEATURE_MAKE_POSIX_2024 856 | static char * 857 | run_command(const char *cmd) 858 | { 859 | FILE *fd; 860 | char *s, *val = NULL; 861 | char buf[256]; 862 | size_t len = 0, nread; 863 | 864 | if ((fd = popen(cmd, "r")) == NULL) 865 | return val; 866 | 867 | for (;;) { 868 | nread = fread(buf, 1, sizeof(buf), fd); 869 | if (nread == 0) 870 | break; 871 | 872 | val = xrealloc(val, len + nread + 1); 873 | memcpy(val + len, buf, nread); 874 | len += nread; 875 | val[len] = '\0'; 876 | } 877 | pclose(fd); 878 | 879 | if (val == NULL) 880 | return val; 881 | 882 | // Strip leading whitespace in POSIX 2024 mode 883 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 884 | if (posix) 885 | #endif 886 | { 887 | s = val; 888 | while (isspace(*s)) { 889 | ++s; 890 | --len; 891 | } 892 | 893 | if (len == 0) { 894 | free(val); 895 | return NULL; 896 | } 897 | memmove(val, s, len + 1); 898 | } 899 | 900 | // Remove one newline from the end (BSD compatibility) 901 | if (val[len - 1] == '\n') 902 | val[len - 1] = '\0'; 903 | // Other newlines are changed to spaces 904 | for (s = val; *s; ++s) { 905 | if (*s == '\n') 906 | *s = ' '; 907 | } 908 | return val; 909 | } 910 | #endif 911 | 912 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 913 | /* 914 | * Check for an unescaped wildcard character 915 | */ 916 | static int wildchar(const char *p) 917 | { 918 | while (*p) { 919 | switch (*p) { 920 | case '?': 921 | case '*': 922 | case '[': 923 | return 1; 924 | case '\\': 925 | if (p[1] != '\0') 926 | ++p; 927 | break; 928 | } 929 | ++p; 930 | } 931 | return 0; 932 | } 933 | 934 | /* 935 | * Expand any wildcards in a pattern. Return TRUE if a match is 936 | * found, in which case the caller should call globfree() on the 937 | * glob_t structure. 938 | */ 939 | static int 940 | wildcard(char *p, glob_t *gd) 941 | { 942 | int ret; 943 | char *s; 944 | 945 | // Don't call glob() if there are no wildcards. 946 | if (!wildchar(p)) { 947 | nomatch: 948 | // Remove backslashes from the name. 949 | for (s = p; *p; ++p) { 950 | if (*p == '\\' && p[1] != '\0') 951 | continue; 952 | *s++ = *p; 953 | } 954 | *s = '\0'; 955 | return 0; 956 | } 957 | 958 | memset(gd, 0, sizeof(*gd)); 959 | ret = glob(p, GLOB_NOSORT, NULL, gd); 960 | if (ret == GLOB_NOMATCH) { 961 | globfree(gd); 962 | goto nomatch; 963 | } else if (ret != 0) { 964 | error("glob error for '%s'", p); 965 | } 966 | return 1; 967 | } 968 | 969 | void 970 | pragmas_from_env(void) 971 | { 972 | char *p, *q, *var; 973 | const char *env = getenv("PDPMAKE_PRAGMAS"); 974 | 975 | if (env == NULL) 976 | return; 977 | 978 | q = var = xstrdup(env); 979 | while ((p = gettok(&q)) != NULL) 980 | set_pragma(p); 981 | free(var); 982 | } 983 | #endif 984 | 985 | /* 986 | * Determine if a line is a target rule with an inline command. 987 | * Return a pointer to the semicolon separator if it is, else NULL. 988 | */ 989 | static char * 990 | inline_command(char *line) 991 | { 992 | char *p = find_char(line, ':'); 993 | 994 | if (p) 995 | p = strchr(p, ';'); 996 | return p; 997 | } 998 | 999 | /* 1000 | * Parse input from the makefile and construct a tree structure of it. 1001 | */ 1002 | void 1003 | input(FILE *fd, int ilevel) 1004 | { 1005 | char *p, *q, *s, *a, *str, *expanded, *copy; 1006 | char *str1, *str2; 1007 | struct name *np; 1008 | struct depend *dp; 1009 | struct cmd *cp; 1010 | int startno, count; 1011 | bool semicolon_cmd, seen_inference; 1012 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 1013 | uint8_t old_clevel = clevel; 1014 | bool dbl; 1015 | char *lib = NULL; 1016 | glob_t gd; 1017 | int nfile, i; 1018 | char **files; 1019 | #else 1020 | const bool dbl = FALSE; 1021 | #endif 1022 | #if ENABLE_FEATURE_MAKE_POSIX_2024 1023 | bool minus; 1024 | #else 1025 | const bool minus = FALSE; 1026 | #endif 1027 | 1028 | lineno = 0; 1029 | str1 = readline(fd, FALSE); 1030 | while (str1) { 1031 | str2 = NULL; 1032 | 1033 | // Newlines and comments are handled differently in command lines 1034 | // and other types of line. Take a copy of the current line before 1035 | // processing it as a non-command line in case it contains a 1036 | // rule with a command line. That is, a line of the form: 1037 | // 1038 | // target: prereq; command 1039 | // 1040 | copy = xstrdup(str1); 1041 | process_line(str1); 1042 | str = str1; 1043 | 1044 | // Check for an include line 1045 | # if ENABLE_FEATURE_MAKE_EXTENSIONS 1046 | if (!posix) 1047 | while (isblank(*str)) 1048 | ++str; 1049 | #endif 1050 | #if ENABLE_FEATURE_MAKE_POSIX_2024 1051 | minus = !POSIX_2017 && *str == '-'; 1052 | #endif 1053 | p = str + minus; 1054 | if (strncmp(p, "include", 7) == 0 && isblank(p[7])) { 1055 | const char *old_makefile = makefile; 1056 | int old_lineno = lineno; 1057 | 1058 | if (ilevel > 16) 1059 | error("too many includes"); 1060 | 1061 | #if ENABLE_FEATURE_MAKE_POSIX_2024 1062 | count = 0; 1063 | #endif 1064 | q = expanded = expand_macros(p + 7, FALSE); 1065 | while ((p = gettok(&q)) != NULL) { 1066 | FILE *ifd; 1067 | 1068 | #if ENABLE_FEATURE_MAKE_POSIX_2024 1069 | ++count; 1070 | if (!POSIX_2017) { 1071 | // Try to create include file or bring it up-to-date 1072 | opts |= OPT_include; 1073 | make(newname(p), 1); 1074 | opts &= ~OPT_include; 1075 | } 1076 | #endif 1077 | if ((ifd = fopen(p, "r")) == NULL) { 1078 | if (!minus) 1079 | error("can't open include file '%s'", p); 1080 | } else { 1081 | makefile = p; 1082 | input(ifd, ilevel + 1); 1083 | fclose(ifd); 1084 | makefile = old_makefile; 1085 | lineno = old_lineno; 1086 | } 1087 | #if ENABLE_FEATURE_MAKE_POSIX_2024 1088 | if (POSIX_2017) 1089 | break; 1090 | #endif 1091 | } 1092 | #if ENABLE_FEATURE_MAKE_POSIX_2024 1093 | if (POSIX_2017) { 1094 | // In POSIX 2017 zero or more than one include file is 1095 | // unspecified behaviour. 1096 | if (p == NULL || gettok(&q)) { 1097 | error("one include file per line"); 1098 | } 1099 | } else if (count == 0) { 1100 | // In POSIX 2024 no include file is unspecified behaviour. 1101 | # if ENABLE_FEATURE_MAKE_EXTENSIONS 1102 | if (posix) 1103 | # endif 1104 | error("no include file"); 1105 | } 1106 | #endif 1107 | goto end_loop; 1108 | } 1109 | 1110 | // Check for a macro definition 1111 | str = str1; 1112 | #if ENABLE_FEATURE_MAKE_EXTENSIONS || ENABLE_FEATURE_MAKE_POSIX_2024 1113 | // POSIX 2024 seems to allow a tab as the first character of 1114 | // a macro definition, though most implementations don't. 1115 | if (POSIX_2017 && *str == '\t') 1116 | error("command not allowed here"); 1117 | #endif 1118 | if (find_char(str, '=') != NULL) { 1119 | int level = (useenv || fd == NULL) ? 4 : 3; 1120 | // Use a copy of the line: we might need the original 1121 | // if this turns out to be a target rule. 1122 | char *copy2 = xstrdup(str); 1123 | #if ENABLE_FEATURE_MAKE_EXTENSIONS || ENABLE_FEATURE_MAKE_POSIX_2024 1124 | char *newq = NULL; 1125 | char eq = '\0'; 1126 | #endif 1127 | q = find_char(copy2, '='); // q can't be NULL 1128 | 1129 | #if ENABLE_FEATURE_MAKE_EXTENSIONS || ENABLE_FEATURE_MAKE_POSIX_2024 1130 | if (q - 1 > copy2) { 1131 | switch (q[-1]) { 1132 | case ':': 1133 | # if ENABLE_FEATURE_MAKE_POSIX_2024 1134 | // '::=' and ':::=' are from POSIX 2024. 1135 | if (!POSIX_2017 && q - 2 > copy2 && q[-2] == ':') { 1136 | if (q - 3 > copy2 && q[-3] == ':') { 1137 | eq = 'B'; // BSD-style ':=' 1138 | q[-3] = '\0'; 1139 | } else { 1140 | eq = ':'; // GNU-style ':=' 1141 | q[-2] = '\0'; 1142 | } 1143 | break; 1144 | } 1145 | # endif 1146 | # if ENABLE_FEATURE_MAKE_EXTENSIONS 1147 | // ':=' is a non-POSIX extension. 1148 | if (posix) 1149 | break; 1150 | IF_FEATURE_MAKE_POSIX_2024(goto set_eq;) 1151 | # else 1152 | break; 1153 | # endif 1154 | # if ENABLE_FEATURE_MAKE_POSIX_2024 1155 | case '+': 1156 | case '?': 1157 | case '!': 1158 | // '+=', '?=' and '!=' are from POSIX 2024. 1159 | if (POSIX_2017) 1160 | break; 1161 | IF_FEATURE_MAKE_EXTENSIONS(set_eq:) 1162 | # endif 1163 | eq = q[-1]; 1164 | q[-1] = '\0'; 1165 | break; 1166 | } 1167 | } 1168 | #endif 1169 | *q++ = '\0'; // Separate name and value 1170 | while (isblank(*q)) 1171 | q++; 1172 | if ((p = strrchr(q, '\n')) != NULL) 1173 | *p = '\0'; 1174 | 1175 | // Expand left-hand side of assignment 1176 | p = expanded = expand_macros(copy2, FALSE); 1177 | if ((a = gettok(&p)) == NULL) 1178 | error("invalid macro assignment"); 1179 | 1180 | // If the expanded LHS contains ':' and ';' it can't be a 1181 | // macro assignment but it might be a target rule. 1182 | if ((s = strchr(a, ':')) != NULL && strchr(s, ';') != NULL) { 1183 | free(expanded); 1184 | free(copy2); 1185 | goto try_target; 1186 | } 1187 | 1188 | if (gettok(&p)) 1189 | error("invalid macro assignment"); 1190 | 1191 | #if ENABLE_FEATURE_MAKE_EXTENSIONS || ENABLE_FEATURE_MAKE_POSIX_2024 1192 | if (eq == ':') { 1193 | // GNU-style ':='. Expand right-hand side of assignment. 1194 | // Macro is of type immediate-expansion. 1195 | q = newq = expand_macros(q, FALSE); 1196 | level |= M_IMMEDIATE; 1197 | } 1198 | # if ENABLE_FEATURE_MAKE_POSIX_2024 1199 | else if (eq == 'B') { 1200 | // BSD-style ':='. Expand right-hand side of assignment, 1201 | // though not '$$'. Macro is of type delayed-expansion. 1202 | q = newq = expand_macros(q, TRUE); 1203 | } else if (eq == '?' && getmp(a) != NULL) { 1204 | // Skip assignment if macro is already set 1205 | goto end_loop; 1206 | } else if (eq == '+') { 1207 | // Append to current value 1208 | struct macro *mp = getmp(a); 1209 | char *rhs; 1210 | newq = mp && mp->m_val[0] ? xstrdup(mp->m_val) : NULL; 1211 | if (mp && mp->m_immediate) { 1212 | // Expand right-hand side of assignment (GNU make 1213 | // compatibility) 1214 | rhs = expand_macros(q, FALSE); 1215 | level |= M_IMMEDIATE; 1216 | } else { 1217 | rhs = q; 1218 | } 1219 | newq = xappendword(newq, rhs); 1220 | if (rhs != q) 1221 | free(rhs); 1222 | q = newq; 1223 | } else if (eq == '!') { 1224 | char *cmd = expand_macros(q, FALSE); 1225 | q = newq = run_command(cmd); 1226 | free(cmd); 1227 | } 1228 | # endif 1229 | #endif 1230 | setmacro(a, q, level); 1231 | #if ENABLE_FEATURE_MAKE_EXTENSIONS || ENABLE_FEATURE_MAKE_POSIX_2024 1232 | free(newq); 1233 | #endif 1234 | free(copy2); 1235 | goto end_loop; 1236 | } 1237 | 1238 | // If we get here it must be a target rule 1239 | try_target: 1240 | if (*str == '\t') // Command without target 1241 | error("command not allowed here"); 1242 | p = expanded = expand_macros(str, FALSE); 1243 | 1244 | // Look for colon separator 1245 | q = find_colon(p); 1246 | if (q == NULL) 1247 | error("expected separator"); 1248 | 1249 | *q++ = '\0'; // Separate targets and prerequisites 1250 | 1251 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 1252 | // Double colon 1253 | dbl = !posix && *q == ':'; 1254 | if (dbl) 1255 | q++; 1256 | #endif 1257 | 1258 | // Look for semicolon separator 1259 | cp = NULL; 1260 | s = strchr(q, ';'); 1261 | if (s) { 1262 | // Retrieve command from original or expanded copy of line 1263 | char *copy3 = expand_macros(copy, FALSE); 1264 | if ((p = inline_command(copy)) || (p = inline_command(copy3))) 1265 | cp = newcmd(process_command(p + 1), cp); 1266 | free(copy3); 1267 | *s = '\0'; 1268 | } 1269 | semicolon_cmd = cp != NULL && cp->c_cmd[0] != '\0'; 1270 | 1271 | // Create list of prerequisites 1272 | dp = NULL; 1273 | while (((p = gettok(&q)) != NULL)) { 1274 | #if !ENABLE_FEATURE_MAKE_EXTENSIONS 1275 | # if ENABLE_FEATURE_MAKE_POSIX_2024 1276 | if (!POSIX_2017 && strcmp(p, ".WAIT") == 0) 1277 | continue; 1278 | # endif 1279 | np = newname(p); 1280 | dp = newdep(np, dp); 1281 | #else 1282 | char *newp = NULL; 1283 | 1284 | if (!posix) { 1285 | // Allow prerequisites of form library(member1 member2). 1286 | // Leading and trailing spaces in the brackets are skipped. 1287 | if (!lib) { 1288 | s = strchr(p, '('); 1289 | if (s && !ends_with_bracket(s) && strchr(q, ')')) { 1290 | // Looks like an unterminated archive member 1291 | // with a terminator later on the line. 1292 | lib = p; 1293 | if (s[1] != '\0') { 1294 | p = newp = xconcat3(lib, ")", ""); 1295 | s[1] = '\0'; 1296 | } else { 1297 | continue; 1298 | } 1299 | } 1300 | } else if (ends_with_bracket(p)) { 1301 | if (*p != ')') 1302 | p = newp = xconcat3(lib, p, ""); 1303 | lib = NULL; 1304 | if (newp == NULL) 1305 | continue; 1306 | } else { 1307 | p = newp = xconcat3(lib, p, ")"); 1308 | } 1309 | } 1310 | 1311 | // If not in POSIX mode expand wildcards in the name. 1312 | nfile = 1; 1313 | files = &p; 1314 | if (!posix && wildcard(p, &gd)) { 1315 | nfile = gd.gl_pathc; 1316 | files = gd.gl_pathv; 1317 | } 1318 | for (i = 0; i < nfile; ++i) { 1319 | # if ENABLE_FEATURE_MAKE_POSIX_2024 1320 | if (!POSIX_2017 && strcmp(files[i], ".WAIT") == 0) 1321 | continue; 1322 | # endif 1323 | np = newname(files[i]); 1324 | dp = newdep(np, dp); 1325 | } 1326 | if (files != &p) 1327 | globfree(&gd); 1328 | free(newp); 1329 | #endif /* ENABLE_FEATURE_MAKE_EXTENSIONS */ 1330 | } 1331 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 1332 | lib = NULL; 1333 | #endif 1334 | 1335 | // Create list of commands 1336 | startno = dispno; 1337 | while ((str2 = readline(fd, TRUE)) && *str2 == '\t') { 1338 | cp = newcmd(process_command(str2), cp); 1339 | free(str2); 1340 | } 1341 | dispno = startno; 1342 | 1343 | // Create target names and attach rule to them 1344 | q = expanded; 1345 | count = 0; 1346 | seen_inference = FALSE; 1347 | while ((p = gettok(&q)) != NULL) { 1348 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 1349 | // If not in POSIX mode expand wildcards in the name. 1350 | nfile = 1; 1351 | files = &p; 1352 | if (!posix && wildcard(p, &gd)) { 1353 | nfile = gd.gl_pathc; 1354 | files = gd.gl_pathv; 1355 | } 1356 | for (i = 0; i < nfile; ++i) 1357 | # define p files[i] 1358 | #endif 1359 | { 1360 | int ttype = target_type(p); 1361 | 1362 | np = newname(p); 1363 | if (ttype != T_NORMAL) { 1364 | // Enforce prerequisites/commands in POSIX mode 1365 | if (IF_FEATURE_MAKE_EXTENSIONS(posix &&) 1) { 1366 | if ((ttype & T_NOPREREQ) && dp) 1367 | error_not_allowed("prerequisites", p); 1368 | if ((ttype & T_INFERENCE)) { 1369 | if (semicolon_cmd) 1370 | error_in_inference_rule("'; command'"); 1371 | seen_inference = TRUE; 1372 | } 1373 | if ((ttype & T_COMMAND) && !cp && 1374 | !((ttype & T_INFERENCE) && !semicolon_cmd)) 1375 | error("commands required for %s", p); 1376 | if (!(ttype & T_COMMAND) && cp) 1377 | error_not_allowed("commands", p); 1378 | } 1379 | 1380 | if ((ttype & T_INFERENCE)) { 1381 | np->n_flag |= N_INFERENCE; 1382 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 1383 | } else if (strcmp(p, ".DEFAULT") == 0) { 1384 | // .DEFAULT rule is a special case 1385 | np->n_flag |= N_SPECIAL | N_INFERENCE; 1386 | #endif 1387 | } else { 1388 | np->n_flag |= N_SPECIAL; 1389 | } 1390 | } else if (!firstname) { 1391 | firstname = np; 1392 | } 1393 | addrule(np, dp, cp, dbl); 1394 | count++; 1395 | } 1396 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 1397 | # undef p 1398 | if (files != &p) 1399 | globfree(&gd); 1400 | #endif 1401 | } 1402 | if (IF_FEATURE_MAKE_EXTENSIONS(posix &&) seen_inference && count != 1) 1403 | error_in_inference_rule("multiple targets"); 1404 | 1405 | // Prerequisites and commands will be unused if there were 1406 | // no targets. Avoid leaking memory. 1407 | if (count == 0) { 1408 | freedeps(dp); 1409 | freecmds(cp); 1410 | } 1411 | 1412 | end_loop: 1413 | free(str1); 1414 | dispno = lineno; 1415 | str1 = str2 ? str2 : readline(fd, FALSE); 1416 | free(copy); 1417 | free(expanded); 1418 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 1419 | if (!seen_first && fd) { 1420 | if (findname(".POSIX")) { 1421 | // The first non-comment line from a real makefile 1422 | // defined the .POSIX special target. 1423 | setenv("PDPMAKE_POSIXLY_CORRECT", "", 1); 1424 | posix = TRUE; 1425 | } 1426 | seen_first = TRUE; 1427 | } 1428 | #endif 1429 | } 1430 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 1431 | // Conditionals aren't allowed to span files 1432 | if (clevel != old_clevel) 1433 | error("invalid conditional"); 1434 | #endif 1435 | } 1436 | -------------------------------------------------------------------------------- /macro.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Macro control for make 3 | */ 4 | #include "make.h" 5 | 6 | struct macro *macrohead[HTABSIZE]; 7 | 8 | struct macro * 9 | getmp(const char *name) 10 | { 11 | struct macro *mp; 12 | 13 | for (mp = macrohead[getbucket(name)]; mp; mp = mp->m_next) 14 | if (strcmp(name, mp->m_name) == 0) 15 | return mp; 16 | return NULL; 17 | } 18 | 19 | static int 20 | is_valid_macro(const char *name) 21 | { 22 | const char *s; 23 | for (s = name; *s; ++s) { 24 | // In POSIX mode only a limited set of characters are guaranteed 25 | // to be allowed in macro names. 26 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 27 | if (posix) 28 | #endif 29 | { 30 | // Find the appropriate character set 31 | if ((( 32 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 33 | (pragma & P_MACRO_NAME) || 34 | #endif 35 | #if ENABLE_FEATURE_MAKE_POSIX_2024 36 | !POSIX_2017 37 | #else 38 | FALSE 39 | #endif 40 | ) ? !isfname(*s) : !ispname(*s))) 41 | return FALSE; 42 | } 43 | // As an extension allow anything that can get through the 44 | // input parser, apart from the following. 45 | if (*s == '=') 46 | return FALSE; 47 | #if ENABLE_FEATURE_MAKE_POSIX_2024 48 | if (isblank(*s) || iscntrl(*s)) 49 | return FALSE; 50 | #endif 51 | } 52 | return TRUE; 53 | } 54 | 55 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 56 | static int 57 | potentially_valid_macro(const char *name) 58 | { 59 | int ret = FALSE; 60 | 61 | if (!(pragma & P_MACRO_NAME)) { 62 | pragma |= P_MACRO_NAME; 63 | ret = is_valid_macro(name); 64 | pragma &= ~P_MACRO_NAME; 65 | } 66 | return ret; 67 | } 68 | #endif 69 | 70 | void 71 | setmacro(const char *name, const char *val, int level) 72 | { 73 | struct macro *mp; 74 | bool valid = level & M_VALID; 75 | bool from_env = level & M_ENVIRON; 76 | #if ENABLE_FEATURE_MAKE_EXTENSIONS || ENABLE_FEATURE_MAKE_POSIX_2024 77 | bool immediate = level & M_IMMEDIATE; 78 | #endif 79 | 80 | level &= ~(M_IMMEDIATE | M_VALID | M_ENVIRON); 81 | mp = getmp(name); 82 | if (mp) { 83 | // Don't replace existing macro from a lower level 84 | if (level > mp->m_level) 85 | return; 86 | 87 | // Replace existing macro 88 | free(mp->m_val); 89 | } else { 90 | // If not defined, allocate space for new 91 | unsigned int bucket; 92 | 93 | if (!valid && !is_valid_macro(name)) { 94 | // Silently drop invalid names from the environment 95 | if (from_env) 96 | return; 97 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 98 | error("invalid macro name '%s'%s", name, 99 | potentially_valid_macro(name) ? 100 | ": allow with pragma macro_name" : ""); 101 | #else 102 | error("invalid macro name '%s'", name); 103 | #endif 104 | } 105 | 106 | bucket = getbucket(name); 107 | mp = xmalloc(sizeof(struct macro)); 108 | mp->m_next = macrohead[bucket]; 109 | macrohead[bucket] = mp; 110 | mp->m_flag = FALSE; 111 | mp->m_name = xstrdup(name); 112 | } 113 | #if ENABLE_FEATURE_MAKE_EXTENSIONS || ENABLE_FEATURE_MAKE_POSIX_2024 114 | mp->m_immediate = immediate; 115 | #endif 116 | mp->m_level = level; 117 | mp->m_val = xstrdup(val ? val : ""); 118 | } 119 | 120 | #if ENABLE_FEATURE_CLEAN_UP 121 | void 122 | freemacros(void) 123 | { 124 | int i; 125 | struct macro *mp, *nextmp; 126 | 127 | for (i = 0; i < HTABSIZE; i++) { 128 | for (mp = macrohead[i]; mp; mp = nextmp) { 129 | nextmp = mp->m_next; 130 | free(mp->m_name); 131 | free(mp->m_val); 132 | free(mp); 133 | } 134 | } 135 | } 136 | #endif 137 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * make [--posix] [-C path] [-f makefile] [-j num] [-x pragma] 3 | * [-ehiknpqrsSt] [macro[:[:[:]]]=val ...] [target ...] 4 | * 5 | * --posix Enforce POSIX mode (non-POSIX) 6 | * -C Change directory to path (non-POSIX) 7 | * -f Makefile name 8 | * -j Number of jobs to run in parallel (not implemented) 9 | * -x Pragma to make POSIX mode less strict (non-POSIX) 10 | * -e Environment variables override macros in makefiles 11 | * -h Display help information (non-POSIX) 12 | * -i Ignore exit status 13 | * -k Continue on error 14 | * -n Pretend to make 15 | * -p Print all macros & targets 16 | * -q Question up-to-dateness of target. Return exit status 1 if not 17 | * -r Don't use inbuilt rules 18 | * -s Make silently 19 | * -S Stop on error 20 | * -t Touch files instead of making them 21 | */ 22 | #include "make.h" 23 | 24 | uint32_t opts; 25 | const char *myname; 26 | const char *makefile; 27 | struct file *makefiles; 28 | #if ENABLE_FEATURE_MAKE_POSIX_2024 29 | char *numjobs = NULL; 30 | #endif 31 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 32 | bool posix; 33 | bool seen_first; 34 | unsigned char pragma = 0; 35 | unsigned char posix_level = DEFAULT_POSIX_LEVEL; 36 | #endif 37 | 38 | static void 39 | usage(int exit_code) 40 | { 41 | FILE *fp = ENABLE_FEATURE_MAKE_EXTENSIONS && exit_code == 0 ? 42 | stdout : stderr; 43 | 44 | fprintf(fp, 45 | "Usage: %s" 46 | IF_FEATURE_MAKE_EXTENSIONS(" [--posix] [-C path]") 47 | " [-f makefile]" 48 | IF_FEATURE_MAKE_POSIX_2024(" [-j num]") 49 | IF_FEATURE_MAKE_EXTENSIONS(" [-x pragma]") 50 | IF_FEATURE_MAKE_EXTENSIONS("\n\t") 51 | IF_NOT_FEATURE_MAKE_EXTENSIONS(" [-eiknpqrsSt] ") 52 | IF_FEATURE_MAKE_EXTENSIONS(" [-ehiknpqrsSt] ") 53 | IF_NOT_FEATURE_MAKE_POSIX_2024( 54 | IF_FEATURE_MAKE_EXTENSIONS("[macro[:]=val ...]") 55 | IF_NOT_FEATURE_MAKE_EXTENSIONS("[macro=val ...]") 56 | ) 57 | IF_FEATURE_MAKE_POSIX_2024( 58 | IF_FEATURE_MAKE_EXTENSIONS("[macro[:[:[:]]]=val ...]") 59 | IF_NOT_FEATURE_MAKE_EXTENSIONS("[macro[::[:]]=val ...]") 60 | ) 61 | " [target ...]\n", myname); 62 | 63 | fprintf(fp, "\nThis build supports:" 64 | IF_FEATURE_MAKE_EXTENSIONS( 65 | " non-POSIX extensions," 66 | IF_FEATURE_MAKE_POSIX_2024(" POSIX 2024,") 67 | " POSIX 2017\n" 68 | ) 69 | IF_NOT_FEATURE_MAKE_EXTENSIONS( 70 | IF_FEATURE_MAKE_POSIX_2024(" POSIX 2024") 71 | IF_NOT_FEATURE_MAKE_POSIX_2024(" POSIX 2017") 72 | ) 73 | ); 74 | #if ENABLE_FEATURE_MAKE_EXTENSIONS && ENABLE_FEATURE_MAKE_POSIX_2024 75 | fprintf(fp, 76 | "In strict POSIX mode the %s standard is enforced by default.\n", 77 | DEFAULT_POSIX_LEVEL == STD_POSIX_2017 ? "2017" : "2024"); 78 | #endif 79 | #if !ENABLE_FEATURE_MAKE_EXTENSIONS 80 | # if ENABLE_FEATURE_MAKE_POSIX_2024 81 | fprintf(fp, "\nFor details see:\n" 82 | " https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/utilities/make.html\n"); 83 | # else 84 | fprintf(fp, "\nFor details see:\n" 85 | " https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/utilities/make.html\n"); 86 | # endif 87 | #endif 88 | exit(exit_code); 89 | } 90 | 91 | /* 92 | * Process options from an argv array. If from_env is non-zero we're 93 | * handling options from MAKEFLAGS so skip '-C', '-f', '-p' and '-x'. 94 | */ 95 | static uint32_t 96 | process_options(int argc, char **argv, int from_env) 97 | { 98 | int opt; 99 | uint32_t flags = 0; 100 | 101 | while ((opt = getopt(argc, argv, OPTSTR1 OPTSTR2 OPT_OFFSET)) != -1) { 102 | switch(opt) { 103 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 104 | case 'C': 105 | if (!posix && !from_env) { 106 | if (chdir(optarg) == -1) { 107 | error("can't chdir to %s: %s", optarg, strerror(errno)); 108 | } 109 | flags |= OPT_C; 110 | break; 111 | } 112 | error("-C not allowed"); 113 | break; 114 | #endif 115 | case 'f': // Alternate file name 116 | if (!from_env) { 117 | makefiles = newfile(optarg, makefiles); 118 | flags |= OPT_f; 119 | } 120 | break; 121 | case 'e': // Prefer env vars to macros in makefiles 122 | flags |= OPT_e; 123 | break; 124 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 125 | case 'h': // Print usage message and exit 126 | if (posix) 127 | error("-h not allowed"); 128 | usage(0); 129 | break; 130 | #endif 131 | case 'i': // Ignore fault mode 132 | flags |= OPT_i; 133 | break; 134 | #if ENABLE_FEATURE_MAKE_POSIX_2024 135 | case 'j': 136 | if (!POSIX_2017) { 137 | const char *s; 138 | 139 | for (s = optarg; *s; ++s) { 140 | if (!isdigit(*s)) { 141 | usage(2); 142 | } 143 | } 144 | free(numjobs); 145 | numjobs = xstrdup(optarg); 146 | flags |= OPT_j; 147 | break; 148 | } 149 | error("-j not allowed"); 150 | break; 151 | #endif 152 | case 'k': // Continue on error 153 | flags |= OPT_k; 154 | flags &= ~OPT_S; 155 | break; 156 | case 'n': // Pretend mode 157 | flags |= OPT_n; 158 | break; 159 | case 'p': 160 | if (!from_env) 161 | flags |= OPT_p; 162 | break; 163 | case 'q': 164 | flags |= OPT_q; 165 | break; 166 | case 'r': 167 | flags |= OPT_r; 168 | break; 169 | case 't': 170 | flags |= OPT_t; 171 | break; 172 | case 's': // Silent about commands 173 | flags |= OPT_s; 174 | break; 175 | case 'S': // Stop on error 176 | flags |= OPT_S; 177 | flags &= ~OPT_k; 178 | break; 179 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 180 | case 'x': // Pragma 181 | if (!from_env) { 182 | set_pragma(optarg); 183 | flags |= OPT_x; 184 | } 185 | break; 186 | #endif 187 | default: 188 | if (from_env) 189 | error("invalid MAKEFLAGS"); 190 | else 191 | usage(2); 192 | } 193 | } 194 | return flags; 195 | } 196 | 197 | /* 198 | * Split the contents of MAKEFLAGS into an argv array. If the return 199 | * value (call it fargv) isn't NULL the caller should free fargv[1] and 200 | * fargv. 201 | */ 202 | static char ** 203 | expand_makeflags(int *fargc) 204 | { 205 | const char *m, *makeflags = getenv("MAKEFLAGS"); 206 | char *p, *argstr; 207 | int argc; 208 | char **argv; 209 | 210 | if (makeflags == NULL) 211 | return NULL; 212 | 213 | while (isblank(*makeflags)) 214 | makeflags++; 215 | 216 | if (*makeflags == '\0') 217 | return NULL; 218 | 219 | p = argstr = xmalloc(strlen(makeflags) + 2); 220 | 221 | // If MAKEFLAGS doesn't start with a hyphen, doesn't look like 222 | // a macro definition and only contains valid option characters, 223 | // add a hyphen. 224 | argc = 3; 225 | if (makeflags[0] != '-' && strchr(makeflags, '=') == NULL) { 226 | if (strspn(makeflags, OPTSTR1 + 1) != strlen(makeflags)) 227 | error("invalid MAKEFLAGS"); 228 | *p++ = '-'; 229 | } else { 230 | // MAKEFLAGS may need to be split, estimate size of argv array. 231 | for (m = makeflags; *m; ++m) { 232 | if (isblank(*m)) 233 | argc++; 234 | } 235 | } 236 | 237 | argv = xmalloc(argc * sizeof(char *)); 238 | argc = 0; 239 | argv[argc++] = (char *)myname; 240 | argv[argc++] = argstr; 241 | 242 | // Copy MAKEFLAGS into argstr, splitting at non-escaped blanks. 243 | m = makeflags; 244 | do { 245 | if (*m == '\\' && m[1] != '\0') 246 | m++; // Skip backslash, copy next character unconditionally. 247 | else if (isblank(*m)) { 248 | // Terminate current argument and start a new one. 249 | *p++ = '\0'; 250 | argv[argc++] = p; 251 | do { 252 | m++; 253 | } while (isblank(*m)); 254 | continue; 255 | } 256 | *p++ = *m++; 257 | } while (*m != '\0'); 258 | *p = '\0'; 259 | argv[argc] = NULL; 260 | 261 | *fargc = argc; 262 | return argv; 263 | } 264 | 265 | /* 266 | * Instantiate all macros in an argv-style array of pointers. Stop 267 | * processing at the first string that doesn't contain an equal sign. 268 | * As an extension, target arguments on the command line (level 1) 269 | * are skipped and will be processed later. 270 | */ 271 | static char ** 272 | process_macros(char **argv, int level) 273 | { 274 | char *equal; 275 | 276 | for (; *argv; argv++) { 277 | #if ENABLE_FEATURE_MAKE_EXTENSIONS || ENABLE_FEATURE_MAKE_POSIX_2024 278 | char *colon = NULL; 279 | int immediate = 0; 280 | #endif 281 | #if ENABLE_FEATURE_MAKE_POSIX_2024 282 | int except_dollar = FALSE; 283 | #endif 284 | 285 | if (!(equal = strchr(*argv, '='))) { 286 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 287 | // Skip targets on the command line 288 | if (!posix && level == 1) 289 | continue; 290 | else 291 | #endif 292 | // Stop at first target 293 | break; 294 | } 295 | 296 | #if ENABLE_FEATURE_MAKE_EXTENSIONS || ENABLE_FEATURE_MAKE_POSIX_2024 297 | if (equal - 1 > *argv && equal[-1] == ':') { 298 | # if ENABLE_FEATURE_MAKE_POSIX_2024 299 | if (equal - 2 > *argv && equal[-2] == ':') { 300 | if (POSIX_2017) 301 | error("invalid macro assignment"); 302 | if (equal - 3 > *argv && equal[-3] == ':') { 303 | // BSD-style ':='. Expand RHS, but not '$$', 304 | // resulting macro is delayed expansion. 305 | colon = equal - 3; 306 | except_dollar = TRUE; 307 | } else { 308 | // GNU-style ':='. Expand RHS, including '$$', 309 | // resulting macro is immediate expansion. 310 | colon = equal - 2; 311 | immediate = M_IMMEDIATE; 312 | } 313 | *colon = '\0'; 314 | } else 315 | # endif 316 | { 317 | # if ENABLE_FEATURE_MAKE_EXTENSIONS 318 | if (posix) 319 | error("invalid macro assignment"); 320 | colon = equal - 1; 321 | immediate = M_IMMEDIATE; 322 | *colon = '\0'; 323 | # else 324 | error("invalid macro assignment"); 325 | # endif 326 | } 327 | } else 328 | #endif 329 | *equal = '\0'; 330 | 331 | /* We want to process _most_ macro assignments. 332 | * There are exceptions for particular values from the 333 | * environment. */ 334 | if (!((level & M_ENVIRON) && 335 | (strcmp(*argv, "MAKEFLAGS") == 0 336 | || strcmp(*argv, "SHELL") == 0 337 | #if ENABLE_FEATURE_MAKE_POSIX_2024 338 | || (strcmp(*argv, "CURDIR") == 0 && !useenv && !POSIX_2017) 339 | #endif 340 | 341 | ))) { 342 | #if ENABLE_FEATURE_MAKE_EXTENSIONS || ENABLE_FEATURE_MAKE_POSIX_2024 343 | if (colon) { 344 | char *exp = expand_macros(equal + 1, except_dollar); 345 | setmacro(*argv, exp, level | immediate); 346 | free(exp); 347 | } else 348 | #endif 349 | setmacro(*argv, equal + 1, level); 350 | } 351 | 352 | #if ENABLE_FEATURE_MAKE_EXTENSIONS || ENABLE_FEATURE_MAKE_POSIX_2024 353 | if (colon) 354 | *colon = ':'; 355 | else 356 | #endif 357 | *equal = '='; 358 | } 359 | return argv; 360 | } 361 | 362 | /* 363 | * Update the MAKEFLAGS macro and environment variable to include any 364 | * command line options that don't have their default value (apart from 365 | * -f, -p and -S). Also add any macros defined on the command line or 366 | * by the MAKEFLAGS environment variable (apart from MAKEFLAGS itself). 367 | * Add macros that were defined on the command line to the environment. 368 | */ 369 | static void 370 | update_makeflags(void) 371 | { 372 | int i; 373 | char optbuf[] = "-?"; 374 | char *makeflags = NULL; 375 | char *macro, *s; 376 | const char *t; 377 | struct macro *mp; 378 | 379 | t = OPTSTR1 + 1; 380 | for (i = 0; *t; t++) { 381 | #if ENABLE_FEATURE_MAKE_POSIX_2024 382 | if (*t == ':') 383 | continue; 384 | #endif 385 | if ((opts & OPT_MASK & (1 << i))) { 386 | optbuf[1] = *t; 387 | makeflags = xappendword(makeflags, optbuf); 388 | #if ENABLE_FEATURE_MAKE_POSIX_2024 389 | if (*t == 'j') { 390 | makeflags = xappendword(makeflags, numjobs); 391 | } 392 | #endif 393 | } 394 | i++; 395 | } 396 | 397 | for (i = 0; i < HTABSIZE; ++i) { 398 | for (mp = macrohead[i]; mp; mp = mp->m_next) { 399 | if ((mp->m_level == 1 || mp->m_level == 2) && 400 | strcmp(mp->m_name, "MAKEFLAGS") != 0) { 401 | macro = xmalloc(strlen(mp->m_name) + 2 * strlen(mp->m_val) + 1); 402 | s = stpcpy(macro, mp->m_name); 403 | *s++ = '='; 404 | for (t = mp->m_val; *t; t++) { 405 | if (*t == '\\' || isblank(*t)) 406 | *s++ = '\\'; 407 | *s++ = *t; 408 | } 409 | *s = '\0'; 410 | 411 | makeflags = xappendword(makeflags, macro); 412 | free(macro); 413 | 414 | // Add command line macro definitions to the environment 415 | if (mp->m_level == 1 && strcmp(mp->m_name, "SHELL") != 0) 416 | setenv(mp->m_name, mp->m_val, 1); 417 | } 418 | } 419 | } 420 | 421 | if (makeflags) { 422 | setmacro("MAKEFLAGS", makeflags, 0); 423 | setenv("MAKEFLAGS", makeflags, 1); 424 | free(makeflags); 425 | } 426 | } 427 | 428 | static void 429 | make_handler(int sig) 430 | { 431 | signal(sig, SIG_DFL); 432 | remove_target(); 433 | kill(getpid(), sig); 434 | } 435 | 436 | static void 437 | init_signal(int sig) 438 | { 439 | struct sigaction sa, new_action; 440 | 441 | sigemptyset(&new_action.sa_mask); 442 | new_action.sa_flags = 0; 443 | new_action.sa_handler = make_handler; 444 | 445 | sigaction(sig, NULL, &sa); 446 | if (sa.sa_handler != SIG_IGN) 447 | sigaction(sig, &new_action, NULL); 448 | } 449 | 450 | /* 451 | * If the global option flag associated with a special target hasn't 452 | * been set mark all prerequisites of the target with a flag. If the 453 | * target had no prerequisites set the global option flag. 454 | */ 455 | static void 456 | mark_special(const char *special, uint32_t oflag, uint16_t nflag) 457 | { 458 | struct name *np; 459 | struct rule *rp; 460 | struct depend *dp; 461 | int marked = FALSE; 462 | 463 | if (!(opts & oflag) && (np = findname(special))) { 464 | for (rp = np->n_rule; rp; rp = rp->r_next) { 465 | for (dp = rp->r_dep; dp; dp = dp->d_next) { 466 | dp->d_name->n_flag |= nflag; 467 | marked = TRUE; 468 | } 469 | } 470 | 471 | if (!marked) 472 | opts |= oflag; 473 | } 474 | } 475 | 476 | int 477 | main(int argc, char **argv) 478 | { 479 | #if ENABLE_FEATURE_MAKE_POSIX_2024 480 | const char *path, *newpath = NULL; 481 | #else 482 | const char *path = "make"; 483 | #endif 484 | char **fargv, **fargv0; 485 | int fargc, estat; 486 | bool found_target; 487 | FILE *ifd; 488 | struct file *fp; 489 | 490 | if (argc == 0) { 491 | return EXIT_FAILURE; 492 | } 493 | 494 | myname = basename(*argv); 495 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 496 | if (argv[1] && strcmp(argv[1], "--posix") == 0) { 497 | argv[1] = argv[0]; 498 | ++argv; 499 | --argc; 500 | setenv("PDPMAKE_POSIXLY_CORRECT", "", 1); 501 | posix = TRUE; 502 | } else { 503 | posix = getenv("PDPMAKE_POSIXLY_CORRECT") != NULL; 504 | } 505 | pragmas_from_env(); 506 | #endif 507 | 508 | #if ENABLE_FEATURE_MAKE_POSIX_2024 509 | if (!POSIX_2017) { 510 | path = argv[0]; 511 | if (argv[0][0] != '/' && strchr(argv[0], '/')) { 512 | // Make relative path absolute 513 | path = newpath = realpath(argv[0], NULL); 514 | if (!path) { 515 | error("can't resolve path for %s: %s", argv[0], strerror(errno)); 516 | } 517 | } 518 | } else { 519 | path = "make"; 520 | } 521 | #endif 522 | 523 | // Process options from MAKEFLAGS 524 | fargv = fargv0 = expand_makeflags(&fargc); 525 | if (fargv0) { 526 | opts = process_options(fargc, fargv, TRUE); 527 | fargv = fargv0 + optind; 528 | // Reset getopt(3) so we can call it again 529 | GETOPT_RESET(); 530 | } 531 | 532 | // Process options from the command line 533 | opts |= process_options(argc, argv, FALSE); 534 | argv += optind; 535 | 536 | init_signal(SIGHUP); 537 | init_signal(SIGTERM); 538 | 539 | setmacro("$", "$", 0 | M_VALID); 540 | 541 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 542 | pragmas_to_env(); 543 | 544 | // Process macro definitions from the command line 545 | if (!posix) 546 | process_macros(argv, 1); 547 | else 548 | #endif 549 | // In POSIX mode macros must appear before targets. 550 | // argv should now point to targets only. 551 | argv = process_macros(argv, 1); 552 | 553 | // Process macro definitions from MAKEFLAGS 554 | if (fargv) { 555 | process_macros(fargv, 2); 556 | free(fargv0[1]); 557 | free(fargv0); 558 | } 559 | 560 | // Process macro definitions from the environment 561 | process_macros(environ, 3 | M_ENVIRON); 562 | 563 | // Update MAKEFLAGS and environment 564 | update_makeflags(); 565 | 566 | // Read built-in rules 567 | input(NULL, 0); 568 | 569 | setmacro("SHELL", "/bin/sh", 4); 570 | setmacro("MAKE", path, 4); 571 | #if ENABLE_FEATURE_MAKE_POSIX_2024 572 | if (!POSIX_2017) { 573 | char *cwd = NULL; 574 | size_t len = 0; 575 | 576 | do { 577 | len += 256; 578 | cwd = xrealloc(cwd, len); 579 | if (getcwd(cwd, len)) { 580 | if (!useenv) { 581 | // Export cwd to environment, if necessary 582 | char *envcwd = getenv("CURDIR"); 583 | if (envcwd && strcmp(cwd, envcwd) != 0) 584 | setenv("CURDIR", cwd, 1); 585 | } 586 | setmacro("CURDIR", cwd, 4); 587 | break; 588 | } 589 | } while (errno == ERANGE); 590 | free(cwd); 591 | } 592 | free((void *)newpath); 593 | #endif 594 | 595 | fp = makefiles; 596 | if (!fp) { // Look for a default Makefile 597 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 598 | if (!posix && (ifd = fopen("PDPmakefile", "r")) != NULL) 599 | makefile = "PDPmakefile"; 600 | else 601 | #endif 602 | if ((ifd = fopen("makefile", "r")) != NULL) 603 | makefile = "makefile"; 604 | else if ((ifd = fopen("Makefile", "r")) != NULL) 605 | makefile = "Makefile"; 606 | else 607 | error("no makefile found"); 608 | goto read_makefile; 609 | } 610 | 611 | while (fp) { 612 | if (strcmp(fp->f_name, "-") == 0) { // Can use stdin as makefile 613 | ifd = stdin; 614 | makefile = "stdin"; 615 | } else { 616 | if ((ifd = fopen(fp->f_name, "r")) == NULL) 617 | error("can't open %s: %s", fp->f_name, strerror(errno)); 618 | makefile = fp->f_name; 619 | } 620 | fp = fp->f_next; 621 | read_makefile: 622 | input(ifd, 0); 623 | fclose(ifd); 624 | makefile = NULL; 625 | } 626 | 627 | if (print) 628 | print_details(); 629 | 630 | mark_special(".SILENT", OPT_s, N_SILENT); 631 | mark_special(".IGNORE", OPT_i, N_IGNORE); 632 | mark_special(".PRECIOUS", OPT_precious, N_PRECIOUS); 633 | #if ENABLE_FEATURE_MAKE_POSIX_2024 634 | if (!POSIX_2017) 635 | mark_special(".PHONY", OPT_phony, N_PHONY); 636 | #endif 637 | 638 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 639 | if (posix) 640 | #endif 641 | { 642 | // In POSIX mode only targets should now be in argv. 643 | found_target = FALSE; 644 | for (char **a = argv; *a; a++) { 645 | if (!strchr(*a, '=')) 646 | found_target = TRUE; 647 | else if (found_target) 648 | error("macro assignments must precede targets"); 649 | } 650 | } 651 | 652 | estat = 0; 653 | found_target = FALSE; 654 | for (; *argv; argv++) { 655 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 656 | // Skip macro assignments. 657 | if (strchr(*argv, '=')) 658 | continue; 659 | #endif 660 | found_target = TRUE; 661 | estat |= make(newname(*argv), 0); 662 | } 663 | if (!found_target) { 664 | if (!firstname) 665 | error("no targets defined"); 666 | estat = make(firstname, 0); 667 | } 668 | 669 | #if ENABLE_FEATURE_CLEAN_UP 670 | # if ENABLE_FEATURE_MAKE_POSIX_2024 671 | free((void *)numjobs); 672 | # endif 673 | freenames(); 674 | freemacros(); 675 | freefiles(makefiles); 676 | #endif 677 | 678 | return estat & MAKE_FAILURE; 679 | } 680 | -------------------------------------------------------------------------------- /make.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Do the actual making for make 3 | */ 4 | #include "make.h" 5 | 6 | struct name *target; 7 | 8 | void 9 | remove_target(void) 10 | { 11 | if (!dryrun && !print && !precious && 12 | target && !(target->n_flag & (N_PRECIOUS | N_PHONY)) && 13 | unlink(target->n_name) == 0) { 14 | diagnostic("'%s' removed", target->n_name); 15 | } 16 | } 17 | 18 | /* 19 | * Update the modification time of a file to now. 20 | */ 21 | static void 22 | touch(struct name *np) 23 | { 24 | if (dryrun || !silent) 25 | printf("touch %s\n", np->n_name); 26 | 27 | if (!dryrun) { 28 | const struct timespec timebuf[2] = {{0, UTIME_NOW}, {0, UTIME_NOW}}; 29 | 30 | if (utimensat(AT_FDCWD, np->n_name, timebuf, 0) < 0) { 31 | if (errno == ENOENT) { 32 | int fd = open(np->n_name, O_RDWR | O_CREAT, 0666); 33 | if (fd >= 0) { 34 | close(fd); 35 | return; 36 | } 37 | } 38 | warning("touch %s failed: %s\n", np->n_name, strerror(errno)); 39 | } 40 | } 41 | } 42 | 43 | /* 44 | * Do commands to make a target 45 | */ 46 | static int 47 | docmds(struct name *np, struct cmd *cp) 48 | { 49 | int estat = 0; 50 | char *q, *command; 51 | 52 | for (; cp; cp = cp->c_next) { 53 | uint32_t ssilent, signore, sdomake; 54 | 55 | // Location of command in makefile (for use in error messages) 56 | makefile = cp->c_makefile; 57 | dispno = cp->c_dispno; 58 | #if ENABLE_FEATURE_MAKE_POSIX_2024 59 | opts &= ~OPT_make; // We want to know if $(MAKE) is expanded 60 | #endif 61 | q = command = expand_macros(cp->c_cmd, FALSE); 62 | ssilent = silent || (np->n_flag & N_SILENT) || dotouch; 63 | signore = ignore || (np->n_flag & N_IGNORE); 64 | sdomake = (!dryrun || doinclude || domake) && !dotouch; 65 | for (;;) { 66 | if (*q == '@') // Specific silent 67 | ssilent = TRUE + 1; 68 | else if (*q == '-') // Specific ignore 69 | signore = TRUE; 70 | else if (*q == '+') // Specific domake 71 | sdomake = TRUE + 1; 72 | else 73 | break; 74 | do { 75 | q++; 76 | } while (isblank(*q)); 77 | } 78 | 79 | if (sdomake > TRUE) { 80 | // '+' must not override '@' or .SILENT 81 | if (ssilent != TRUE + 1 && !(np->n_flag & N_SILENT)) 82 | ssilent = FALSE; 83 | } else if (!sdomake) 84 | ssilent = dotouch; 85 | 86 | if (!ssilent && *q != '\0') { // Ignore empty commands 87 | puts(q); 88 | fflush(stdout); 89 | } 90 | 91 | if (quest && sdomake != TRUE + 1) { 92 | // MAKE_FAILURE means rebuild is needed 93 | estat |= MAKE_FAILURE | MAKE_DIDSOMETHING; 94 | continue; 95 | } 96 | 97 | if (sdomake && *q != '\0') { // Ignore empty commands 98 | // Get the shell to execute it 99 | int status; 100 | char *cmd = !signore IF_FEATURE_MAKE_EXTENSIONS(&& posix) ? 101 | xconcat3("set -e;", q, "") : q; 102 | 103 | target = np; 104 | status = system(cmd); 105 | if (!signore IF_FEATURE_MAKE_EXTENSIONS(&& posix)) 106 | free(cmd); 107 | // If this command was being run to create an include file 108 | // or bring it up-to-date errors should be ignored and a 109 | // failure status returned. 110 | if (status == -1 && !doinclude) { 111 | error("couldn't execute '%s'", q); 112 | } else if (status != 0 && !signore) { 113 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 114 | if (!posix && WIFSIGNALED(status)) 115 | remove_target(); 116 | #endif 117 | if (doinclude) { 118 | warning("failed to build '%s'", np->n_name); 119 | } else { 120 | const char *err_type = NULL; 121 | int err_value = 1; 122 | 123 | if (WIFEXITED(status)) { 124 | err_type = "exit"; 125 | err_value = WEXITSTATUS(status); 126 | } else if (WIFSIGNALED(status)) { 127 | err_type = "signal"; 128 | err_value = WTERMSIG(status); 129 | } 130 | 131 | if (!quest || err_value == 127) { 132 | if (err_type) 133 | diagnostic("failed to build '%s' %s %d", 134 | np->n_name, err_type, err_value); 135 | else 136 | diagnostic("failed to build '%s'", np->n_name); 137 | } 138 | 139 | if (errcont) { 140 | estat |= MAKE_FAILURE; 141 | free(command); 142 | break; 143 | } 144 | exit(2); 145 | } 146 | } 147 | target = NULL; 148 | } 149 | if (sdomake || dryrun) 150 | estat = MAKE_DIDSOMETHING; 151 | free(command); 152 | } 153 | 154 | if (dotouch && !(np->n_flag & N_PHONY) && !(estat & MAKE_DIDSOMETHING)) { 155 | touch(np); 156 | estat = MAKE_DIDSOMETHING; 157 | } 158 | 159 | makefile = NULL; 160 | return estat; 161 | } 162 | 163 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 164 | /* 165 | * Remove the suffix from a name, either the one provided in 'tsuff' 166 | * or, if 'tsuff' is NULL, one of the known suffixes. 167 | */ 168 | static char * 169 | remove_suffix(const char *name, const char *tsuff) 170 | { 171 | char *base = NULL; 172 | 173 | if (tsuff != NULL) { 174 | base = has_suffix(name, tsuff); 175 | } else { 176 | struct name *xp = newname(".SUFFIXES"); 177 | for (struct rule *rp = xp->n_rule; rp; rp = rp->r_next) { 178 | for (struct depend *dp = rp->r_dep; dp; dp = dp->d_next) { 179 | base = has_suffix(name, dp->d_name->n_name); 180 | if (base) { 181 | return base; 182 | } 183 | } 184 | } 185 | } 186 | return base; 187 | } 188 | #endif 189 | 190 | #if !ENABLE_FEATURE_MAKE_POSIX_2024 && !ENABLE_FEATURE_MAKE_EXTENSIONS 191 | # define make1(n, c, o, a, d, i, t) make1(n, c, o, i) 192 | #elif ENABLE_FEATURE_MAKE_POSIX_2024 && !ENABLE_FEATURE_MAKE_EXTENSIONS 193 | # define make1(n, c, o, a, d, i, t) make1(n, c, a, d, o, i) 194 | #elif !ENABLE_FEATURE_MAKE_POSIX_2024 && ENABLE_FEATURE_MAKE_EXTENSIONS 195 | # define make1(n, c, o, a, d, i, t) make1(n, c, o, i, t) 196 | #endif 197 | static int 198 | make1(struct name *np, struct cmd *cp, char *oodate, char *allsrc, 199 | char *dedup, struct name *implicit, const char *tsuff) 200 | { 201 | char *name, *member = NULL, *base = NULL, *prereq = NULL; 202 | 203 | name = splitlib(np->n_name, &member); 204 | setmacro("?", oodate, 0 | M_VALID); 205 | #if ENABLE_FEATURE_MAKE_POSIX_2024 206 | if (!POSIX_2017) { 207 | setmacro("+", allsrc, 0 | M_VALID); 208 | setmacro("^", dedup, 0 | M_VALID); 209 | } 210 | #endif 211 | setmacro("%", member, 0 | M_VALID); 212 | setmacro("@", name, 0 | M_VALID); 213 | if (implicit IF_FEATURE_MAKE_EXTENSIONS(|| !posix)) { 214 | char *s; 215 | 216 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 217 | // As an extension, if we're not dealing with an implicit 218 | // prerequisite set $< to the first out-of-date prerequisite. 219 | if (implicit == NULL) { 220 | if (oodate) { 221 | s = strchr(oodate, ' '); 222 | if (s) 223 | *s = '\0'; 224 | prereq = oodate; 225 | } 226 | } else 227 | #endif 228 | prereq = implicit->n_name; 229 | 230 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 231 | if (!posix && member == NULL) { 232 | // As an extension remove a suffix that doesn't necessarily 233 | // start with a period from a target, but not for targets 234 | // of the form lib.a(member.o). 235 | base = remove_suffix(name, tsuff); 236 | if (base) { 237 | free(name); 238 | name = base; 239 | } 240 | } else 241 | #endif 242 | { 243 | base = member ? member : name; 244 | s = suffix(base); 245 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 246 | // As an extension, if we're not dealing with an implicit 247 | // prerequisite and the target ends with a known suffix, 248 | // remove it and set $* to the stem, else to an empty string. 249 | if (implicit == NULL && !is_suffix(s)) 250 | base = NULL; 251 | else 252 | #endif 253 | *s = '\0'; 254 | } 255 | } 256 | setmacro("<", prereq, 0 | M_VALID); 257 | setmacro("*", base, 0 | M_VALID); 258 | free(name); 259 | 260 | return docmds(np, cp); 261 | } 262 | 263 | /* 264 | * Determine if the modification time of a target, t, is less than 265 | * that of a prerequisite, p. If the tv_nsec member of either is 266 | * exactly 0 we assume (possibly incorrectly) that the time resolution 267 | * is 1 second and only compare tv_sec values. 268 | */ 269 | static int 270 | timespec_le(const struct timespec *t, const struct timespec *p) 271 | { 272 | if (t->tv_nsec == 0 || p->tv_nsec == 0) 273 | return t->tv_sec <= p->tv_sec; 274 | else if (t->tv_sec < p->tv_sec) 275 | return TRUE; 276 | else if (t->tv_sec == p->tv_sec) 277 | return t->tv_nsec <= p->tv_nsec; 278 | return FALSE; 279 | } 280 | 281 | /* 282 | * Return the greater of two struct timespecs 283 | */ 284 | static const struct timespec * 285 | timespec_max(const struct timespec *t, const struct timespec *p) 286 | { 287 | return timespec_le(t, p) ? p : t; 288 | } 289 | 290 | /* 291 | * Recursive routine to make a target. 292 | */ 293 | int 294 | make(struct name *np, int level) 295 | { 296 | struct depend *dp; 297 | struct rule *rp; 298 | struct name *impdep = NULL; // implicit prerequisite 299 | struct rule infrule; 300 | struct cmd *sc_cmd = NULL; // commands for single-colon rule 301 | char *oodate = NULL; 302 | #if ENABLE_FEATURE_MAKE_POSIX_2024 303 | char *allsrc = NULL; 304 | char *dedup = NULL; 305 | #endif 306 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 307 | const char *tsuff = NULL; 308 | #endif 309 | struct timespec dtim = {1, 0}; 310 | int estat = 0; 311 | 312 | if (np->n_flag & N_DONE) 313 | return 0; 314 | if (np->n_flag & N_DOING) 315 | error("circular dependency for %s", np->n_name); 316 | np->n_flag |= N_DOING; 317 | 318 | if (!np->n_tim.tv_sec) 319 | modtime(np); // Get modtime of this file 320 | 321 | if (!(np->n_flag & N_DOUBLE)) { 322 | // Find the commands needed for a single-colon rule, using 323 | // an inference rule or .DEFAULT rule if necessary (but, 324 | // as an extension, not for phony targets) 325 | sc_cmd = getcmd(np); 326 | if (!sc_cmd 327 | #if ENABLE_FEATURE_MAKE_EXTENSIONS && ENABLE_FEATURE_MAKE_POSIX_2024 328 | && (posix || !(np->n_flag & N_PHONY)) 329 | #endif 330 | ) { 331 | impdep = dyndep(np, &infrule, &tsuff); 332 | if (impdep) { 333 | sc_cmd = infrule.r_cmd; 334 | addrule(np, infrule.r_dep, NULL, FALSE); 335 | } 336 | } 337 | 338 | // As a last resort check for a default rule 339 | if (!(np->n_flag & N_TARGET) && np->n_tim.tv_sec == 0) { 340 | #if ENABLE_FEATURE_MAKE_EXTENSIONS && ENABLE_FEATURE_MAKE_POSIX_2024 341 | if (posix || !(np->n_flag & N_PHONY)) 342 | #endif 343 | sc_cmd = getcmd(findname(".DEFAULT")); 344 | if (!sc_cmd) { 345 | if (doinclude) 346 | return 1; 347 | error("don't know how to make %s", np->n_name); 348 | } 349 | impdep = np; 350 | } 351 | } 352 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 353 | else { 354 | // If any double-colon rule has no commands we need 355 | // an inference rule. 356 | for (rp = np->n_rule; rp; rp = rp->r_next) { 357 | if (!rp->r_cmd) { 358 | # if ENABLE_FEATURE_MAKE_POSIX_2024 359 | // Phony targets don't need an inference rule. 360 | if (!posix && (np->n_flag & N_PHONY)) 361 | continue; 362 | # endif 363 | impdep = dyndep(np, &infrule, &tsuff); 364 | if (!impdep) { 365 | if (doinclude) 366 | return 1; 367 | error("don't know how to make %s", np->n_name); 368 | } 369 | break; 370 | } 371 | } 372 | } 373 | #endif 374 | 375 | #if ENABLE_FEATURE_MAKE_EXTENSIONS || ENABLE_FEATURE_MAKE_POSIX_2024 376 | // Reset flag to detect duplicate prerequisites 377 | if (!(np->n_flag & N_DOUBLE)) { 378 | for (rp = np->n_rule; rp; rp = rp->r_next) { 379 | for (dp = rp->r_dep; dp; dp = dp->d_next) { 380 | dp->d_name->n_flag &= ~N_MARK; 381 | } 382 | } 383 | } 384 | #endif 385 | 386 | for (rp = np->n_rule; rp; rp = rp->r_next) { 387 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 388 | struct name *locdep = NULL; 389 | 390 | // Each double-colon rule is handled separately. 391 | if ((np->n_flag & N_DOUBLE)) { 392 | // If the rule has no commands use the inference rule. 393 | // Unless there isn't one, as allowed for phony targets. 394 | if (!rp->r_cmd) { 395 | # if ENABLE_FEATURE_MAKE_POSIX_2024 396 | if (impdep) 397 | # endif 398 | { 399 | locdep = impdep; 400 | infrule.r_dep->d_next = rp->r_dep; 401 | rp->r_dep = infrule.r_dep; 402 | rp->r_cmd = infrule.r_cmd; 403 | } 404 | } 405 | // A rule with no prerequisities is executed unconditionally. 406 | if (!rp->r_dep) 407 | dtim = np->n_tim; 408 | // Reset flag to detect duplicate prerequisites 409 | for (dp = rp->r_dep; dp; dp = dp->d_next) { 410 | dp->d_name->n_flag &= ~N_MARK; 411 | } 412 | } 413 | #endif 414 | for (dp = rp->r_dep; dp; dp = dp->d_next) { 415 | // Make prerequisite 416 | estat |= make(dp->d_name, level + 1); 417 | 418 | // Make strings of out-of-date prerequisites (for $?), 419 | // all prerequisites (for $+) and deduplicated prerequisites 420 | // (for $^). 421 | if (timespec_le(&np->n_tim, &dp->d_name->n_tim)) { 422 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 423 | if (posix || !(dp->d_name->n_flag & N_MARK)) 424 | #endif 425 | oodate = xappendword(oodate, dp->d_name->n_name); 426 | } 427 | #if ENABLE_FEATURE_MAKE_POSIX_2024 428 | allsrc = xappendword(allsrc, dp->d_name->n_name); 429 | if (!(dp->d_name->n_flag & N_MARK)) 430 | dedup = xappendword(dedup, dp->d_name->n_name); 431 | #endif 432 | #if ENABLE_FEATURE_MAKE_EXTENSIONS || ENABLE_FEATURE_MAKE_POSIX_2024 433 | dp->d_name->n_flag |= N_MARK; 434 | #endif 435 | dtim = *timespec_max(&dtim, &dp->d_name->n_tim); 436 | } 437 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 438 | if ((np->n_flag & N_DOUBLE)) { 439 | if (((np->n_flag & N_PHONY) || timespec_le(&np->n_tim, &dtim))) { 440 | if (!(estat & MAKE_FAILURE)) { 441 | estat |= make1(np, rp->r_cmd, oodate, allsrc, 442 | dedup, locdep, tsuff); 443 | dtim = (struct timespec){1, 0}; 444 | } 445 | free(oodate); 446 | oodate = NULL; 447 | } 448 | #if ENABLE_FEATURE_MAKE_POSIX_2024 449 | free(allsrc); 450 | free(dedup); 451 | allsrc = dedup = NULL; 452 | #endif 453 | if (locdep) { 454 | rp->r_dep = rp->r_dep->d_next; 455 | rp->r_cmd = NULL; 456 | } 457 | } 458 | #endif 459 | } 460 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 461 | if ((np->n_flag & N_DOUBLE) && impdep) 462 | free(infrule.r_dep); 463 | #endif 464 | 465 | np->n_flag |= N_DONE; 466 | np->n_flag &= ~N_DOING; 467 | 468 | if (!(np->n_flag & N_DOUBLE) && 469 | ((np->n_flag & N_PHONY) || (timespec_le(&np->n_tim, &dtim)))) { 470 | if (!(estat & MAKE_FAILURE)) { 471 | if (sc_cmd) 472 | estat |= make1(np, sc_cmd, oodate, allsrc, dedup, 473 | impdep, tsuff); 474 | else if (!doinclude && level == 0 && !(estat & MAKE_DIDSOMETHING)) 475 | warning("nothing to be done for %s", np->n_name); 476 | } else if (!doinclude && !quest) { 477 | diagnostic("'%s' not built due to errors", np->n_name); 478 | } 479 | free(oodate); 480 | } 481 | 482 | if (estat & MAKE_DIDSOMETHING) { 483 | modtime(np); 484 | if (!np->n_tim.tv_sec) 485 | clock_gettime(CLOCK_REALTIME, &np->n_tim); 486 | } else if (!quest && level == 0 && !timespec_le(&np->n_tim, &dtim)) 487 | printf("%s: '%s' is up to date\n", myname, np->n_name); 488 | 489 | #if ENABLE_FEATURE_MAKE_POSIX_2024 490 | free(allsrc); 491 | free(dedup); 492 | #endif 493 | return estat; 494 | } 495 | -------------------------------------------------------------------------------- /make.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Include header for make 3 | */ 4 | #define _XOPEN_SOURCE 700 5 | #if defined(__sun__) 6 | # define __EXTENSIONS__ 7 | #endif 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | extern char **environ; 29 | 30 | #define NORETURN __attribute__ ((__noreturn__)) 31 | 32 | // Resetting getopt(3) is hopelessly platform-dependent. If command 33 | // line options don't work as expected you may need to tweak this. 34 | // The default should work for GNU libc and OpenBSD. 35 | #if defined(__FreeBSD__) || defined(__NetBSD__) 36 | # define GETOPT_RESET() do { \ 37 | extern int optreset; \ 38 | optind = 1; \ 39 | optreset = 1; \ 40 | } while (0) 41 | #elif defined(__sun__) 42 | # define GETOPT_RESET() (optind = 1) 43 | #else 44 | # define GETOPT_RESET() (optind = 0) 45 | #endif 46 | 47 | // Supported POSIX levels 48 | #define STD_POSIX_2017 0 49 | #define STD_POSIX_2024 1 50 | 51 | // If ENABLE_FEATURE_MAKE_EXTENSIONS is non-zero some non-POSIX extensions 52 | // are enabled. 53 | // 54 | #ifndef ENABLE_FEATURE_MAKE_EXTENSIONS 55 | # define ENABLE_FEATURE_MAKE_EXTENSIONS 1 56 | #endif 57 | 58 | #ifndef DEFAULT_POSIX_LEVEL 59 | # define DEFAULT_POSIX_LEVEL STD_POSIX_2024 60 | #endif 61 | 62 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 63 | # define IF_FEATURE_MAKE_EXTENSIONS(...) __VA_ARGS__ 64 | # define IF_NOT_FEATURE_MAKE_EXTENSIONS(...) 65 | # define POSIX_2017 (posix && posix_level == STD_POSIX_2017) 66 | #else 67 | # define IF_FEATURE_MAKE_EXTENSIONS(...) 68 | # define IF_NOT_FEATURE_MAKE_EXTENSIONS(...) __VA_ARGS__ 69 | #endif 70 | 71 | // IF ENABLE_FEATURE_MAKE_POSIX_2024 is non-zero POSIX 2024 features 72 | // are enabled. 73 | #ifndef ENABLE_FEATURE_MAKE_POSIX_2024 74 | # define ENABLE_FEATURE_MAKE_POSIX_2024 ENABLE_FEATURE_MAKE_EXTENSIONS 75 | #endif 76 | 77 | #if ENABLE_FEATURE_MAKE_POSIX_2024 78 | # define IF_FEATURE_MAKE_POSIX_2024(...) __VA_ARGS__ 79 | # define IF_NOT_FEATURE_MAKE_POSIX_2024(...) 80 | #else 81 | # define IF_FEATURE_MAKE_POSIX_2024(...) 82 | # define IF_NOT_FEATURE_MAKE_POSIX_2024(...) __VA_ARGS__ 83 | #endif 84 | 85 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 86 | # define POSIX_2017 (posix && posix_level == STD_POSIX_2017) 87 | #elif ENABLE_FEATURE_MAKE_POSIX_2024 88 | # define POSIX_2017 FALSE 89 | #endif 90 | 91 | // If ENABLE_FEATURE_CLEAN_UP is non-zero all allocated structures are 92 | // freed at the end of main(). This isn't necessary but it's a nice test. 93 | #ifndef ENABLE_FEATURE_CLEAN_UP 94 | # define ENABLE_FEATURE_CLEAN_UP 0 95 | #endif 96 | 97 | #define TRUE (1) 98 | #define FALSE (0) 99 | #define MAX(a,b) ((a)>(b)?(a):(b)) 100 | 101 | #if defined(__GLIBC__) && ENABLE_FEATURE_MAKE_EXTENSIONS 102 | // By default GNU libc getopt(3) allows options and non-options to be 103 | // mixed. Turn this off in POSIX mode. The '+' prefix in OPTSTR1 is 104 | // otherwise unused and should be skipped. 105 | # define OPT_OFFSET + !posix 106 | #else 107 | # define OPT_OFFSET 108 | #endif 109 | 110 | #ifdef __clang__ 111 | #pragma clang diagnostic ignored "-Wstring-plus-int" 112 | #endif 113 | 114 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 115 | #define OPTSTR1 "+ehij:knqrsSt" 116 | #elif ENABLE_FEATURE_MAKE_POSIX_2024 117 | #define OPTSTR1 "+eij:knqrsSt" 118 | #else 119 | #define OPTSTR1 "+eiknqrsSt" 120 | #endif 121 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 122 | #define OPTSTR2 "pf:C:x:" 123 | #else 124 | #define OPTSTR2 "pf:" 125 | #endif 126 | 127 | enum { 128 | OPTBIT_e = 0, 129 | IF_FEATURE_MAKE_EXTENSIONS(OPTBIT_h,) 130 | OPTBIT_i, 131 | IF_FEATURE_MAKE_POSIX_2024(OPTBIT_j,) 132 | OPTBIT_k, 133 | OPTBIT_n, 134 | OPTBIT_q, 135 | OPTBIT_r, 136 | OPTBIT_s, 137 | OPTBIT_S, 138 | OPTBIT_t, 139 | OPTBIT_p, 140 | OPTBIT_f, 141 | IF_FEATURE_MAKE_EXTENSIONS(OPTBIT_C,) 142 | IF_FEATURE_MAKE_EXTENSIONS(OPTBIT_x,) 143 | OPTBIT_precious, 144 | IF_FEATURE_MAKE_POSIX_2024(OPTBIT_phony,) 145 | IF_FEATURE_MAKE_POSIX_2024(OPTBIT_include,) 146 | IF_FEATURE_MAKE_POSIX_2024(OPTBIT_make,) 147 | 148 | OPT_e = (1 << OPTBIT_e), 149 | OPT_h = IF_FEATURE_MAKE_EXTENSIONS((1 << OPTBIT_h)) + 0, 150 | OPT_i = (1 << OPTBIT_i), 151 | OPT_j = IF_FEATURE_MAKE_POSIX_2024((1 << OPTBIT_j)) + 0, 152 | OPT_k = (1 << OPTBIT_k), 153 | OPT_n = (1 << OPTBIT_n), 154 | OPT_q = (1 << OPTBIT_q), 155 | OPT_r = (1 << OPTBIT_r), 156 | OPT_s = (1 << OPTBIT_s), 157 | OPT_S = (1 << OPTBIT_S), 158 | OPT_t = (1 << OPTBIT_t), 159 | // These options aren't allowed in MAKEFLAGS 160 | OPT_p = (1 << OPTBIT_p), 161 | OPT_f = (1 << OPTBIT_f), 162 | OPT_C = IF_FEATURE_MAKE_EXTENSIONS((1 << OPTBIT_C)) + 0, 163 | OPT_x = IF_FEATURE_MAKE_EXTENSIONS((1 << OPTBIT_x)) + 0, 164 | // The following aren't command line options and must be last 165 | OPT_precious = (1 << OPTBIT_precious), 166 | OPT_phony = IF_FEATURE_MAKE_POSIX_2024((1 << OPTBIT_phony)) + 0, 167 | OPT_include = IF_FEATURE_MAKE_POSIX_2024((1 << OPTBIT_include)) + 0, 168 | OPT_make = IF_FEATURE_MAKE_POSIX_2024((1 << OPTBIT_make)) + 0, 169 | }; 170 | 171 | // Options in OPTSTR1 that aren't included in MAKEFLAGS 172 | #define OPT_MASK (~OPT_S) 173 | 174 | #define useenv (opts & OPT_e) 175 | #define ignore (opts & OPT_i) 176 | #define errcont (opts & OPT_k) 177 | #define dryrun (opts & OPT_n) 178 | #define print (opts & OPT_p) 179 | #define quest (opts & OPT_q) 180 | #define norules (opts & OPT_r) 181 | #define silent (opts & OPT_s) 182 | #define dotouch (opts & OPT_t) 183 | #define precious (opts & OPT_precious) 184 | #define doinclude (opts & OPT_include) 185 | #define domake (opts & OPT_make) 186 | 187 | // A name. This represents a file, either to be made, or pre-existing. 188 | struct name { 189 | struct name *n_next; // Next in the list of names 190 | char *n_name; // Called 191 | struct rule *n_rule; // Rules to build this (prerequisites/commands) 192 | struct timespec n_tim; // Modification time of this name 193 | uint16_t n_flag; // Info about the name 194 | }; 195 | 196 | #define N_DOING 0x01 // Name in process of being built 197 | #define N_DONE 0x02 // Name looked at 198 | #define N_TARGET 0x04 // Name is a target 199 | #define N_PRECIOUS 0x08 // Target is precious 200 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 201 | #define N_DOUBLE 0x10 // Double-colon target 202 | #else 203 | #define N_DOUBLE 0x00 // No support for double-colon target 204 | #endif 205 | #define N_SILENT 0x20 // Build target silently 206 | #define N_IGNORE 0x40 // Ignore build errors 207 | #define N_SPECIAL 0x80 // Special target 208 | #if ENABLE_FEATURE_MAKE_EXTENSIONS || ENABLE_FEATURE_MAKE_POSIX_2024 209 | #define N_MARK 0x100 // Mark for deduplication 210 | #endif 211 | #if ENABLE_FEATURE_MAKE_POSIX_2024 212 | #define N_PHONY 0x200 // Name is a phony target 213 | #else 214 | #define N_PHONY 0 // No support for phony targets 215 | #endif 216 | #define N_INFERENCE 0x400 // Inference rule 217 | 218 | // List of rules to build a target 219 | struct rule { 220 | struct rule *r_next; // Next rule 221 | struct depend *r_dep; // Prerequisites for this rule 222 | struct cmd *r_cmd; // Commands for this rule 223 | }; 224 | 225 | // NOTE: the layout of the following two structures must be compatible. 226 | 227 | // List of prerequisites for a rule 228 | struct depend { 229 | struct depend *d_next; // Next prerequisite 230 | struct name *d_name; // Name of prerequisite 231 | int d_refcnt; // Reference count 232 | }; 233 | 234 | // List of commands for a rule 235 | struct cmd { 236 | struct cmd *c_next; // Next command line 237 | char *c_cmd; // Text of command line 238 | int c_refcnt; // Reference count 239 | const char *c_makefile; // Makefile in which command was defined 240 | int c_dispno; // Line number within makefile 241 | }; 242 | 243 | // Macro storage 244 | struct macro { 245 | struct macro *m_next; // Next variable 246 | char *m_name; // Its name 247 | char *m_val; // Its value 248 | #if ENABLE_FEATURE_MAKE_EXTENSIONS || ENABLE_FEATURE_MAKE_POSIX_2024 249 | bool m_immediate; // Immediate-expansion macro set using ::= 250 | #endif 251 | bool m_flag; // Infinite loop check 252 | uint8_t m_level; // Level at which macro was created 253 | }; 254 | 255 | // List of file names 256 | struct file { 257 | struct file *f_next; 258 | char *f_name; 259 | }; 260 | 261 | // Flags passed to setmacro() 262 | #define M_IMMEDIATE 0x08 // immediate-expansion macro is being defined 263 | #define M_VALID 0x10 // assert macro name is valid 264 | #define M_ENVIRON 0x20 // macro imported from environment 265 | 266 | #define HTABSIZE 199 267 | 268 | // Constants for PRAGMA. Order must match strings in set_pragma(). 269 | enum { 270 | BIT_MACRO_NAME = 0, 271 | BIT_TARGET_NAME, 272 | BIT_COMMAND_COMMENT, 273 | BIT_EMPTY_SUFFIX, 274 | #if defined(__CYGWIN__) 275 | BIT_WINDOWS, 276 | #endif 277 | BIT_POSIX_2017, 278 | BIT_POSIX_2024, 279 | BIT_POSIX_202X, 280 | 281 | P_MACRO_NAME = (1 << BIT_MACRO_NAME), 282 | P_TARGET_NAME = (1 << BIT_TARGET_NAME), 283 | P_COMMAND_COMMENT = (1 << BIT_COMMAND_COMMENT), 284 | P_EMPTY_SUFFIX = (1 << BIT_EMPTY_SUFFIX), 285 | #if defined(__CYGWIN__) 286 | P_WINDOWS = (1 << BIT_WINDOWS), 287 | #endif 288 | }; 289 | 290 | // Status of make() 291 | #define MAKE_FAILURE 0x01 292 | #define MAKE_DIDSOMETHING 0x02 293 | 294 | extern const char *myname; 295 | extern const char *makefile; 296 | extern struct name *namehead[HTABSIZE]; 297 | extern struct macro *macrohead[HTABSIZE]; 298 | extern struct name *firstname; 299 | extern struct name *target; 300 | extern uint32_t opts; 301 | extern int lineno; 302 | extern int dispno; 303 | #if ENABLE_FEATURE_MAKE_POSIX_2024 304 | extern char *numjobs; 305 | #endif 306 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 307 | extern bool posix; 308 | extern bool seen_first; 309 | extern unsigned char pragma; 310 | extern unsigned char posix_level; 311 | #endif 312 | 313 | // Return TRUE if c is allowed in a POSIX 2017 macro or target name 314 | #define ispname(c) (isalpha(c) || isdigit(c) || c == '.' || c == '_') 315 | // Return TRUE if c is in the POSIX 'portable filename character set' 316 | #define isfname(c) (ispname(c) || c == '-') 317 | 318 | void print_details(void); 319 | #if !ENABLE_FEATURE_MAKE_POSIX_2024 320 | #define expand_macros(s, e) expand_macros(s) 321 | #endif 322 | #if !ENABLE_FEATURE_MAKE_EXTENSIONS 323 | #define dyndep(n, i, p) dyndep(n, i) 324 | #endif 325 | char *expand_macros(const char *str, int except_dollar); 326 | void input(FILE *fd, int ilevel); 327 | struct macro *getmp(const char *name); 328 | void setmacro(const char *name, const char *val, int level); 329 | void freemacros(void); 330 | void remove_target(void); 331 | int make(struct name *np, int level); 332 | char *splitlib(const char *name, char **member); 333 | void modtime(struct name *np); 334 | char *suffix(const char *name); 335 | const char *is_suffix(const char *s); 336 | char *has_suffix(const char *name, const char *suffix); 337 | struct name *dyndep(struct name *np, struct rule *infrule, const char **ptsuff); 338 | char *getrules(char *s, int size); 339 | struct name *findname(const char *name); 340 | struct name *newname(const char *name); 341 | struct cmd *getcmd(struct name *np); 342 | void freenames(void); 343 | struct depend *newdep(struct name *np, struct depend *dp); 344 | void freedeps(struct depend *dp); 345 | struct cmd *newcmd(char *str, struct cmd *cp); 346 | void freecmds(struct cmd *cp); 347 | void freerules(struct rule *rp); 348 | void set_pragma(const char *name); 349 | void addrule(struct name *np, struct depend *dp, struct cmd *cp, int flag); 350 | void diagnostic(const char *msg, ...); 351 | void error(const char *msg, ...) NORETURN; 352 | void error_unexpected(const char *s) NORETURN; 353 | void error_in_inference_rule(const char *s) NORETURN; 354 | void error_not_allowed(const char *s, const char *t); 355 | void warning(const char *msg, ...); 356 | void *xmalloc(size_t len); 357 | void *xrealloc(void *ptr, size_t len); 358 | char *xconcat3(const char *s1, const char *s2, const char *s3); 359 | char *xstrdup(const char *s); 360 | char *xstrndup(const char *s, size_t n); 361 | char *xappendword(const char *str, const char *word); 362 | unsigned int getbucket(const char *name); 363 | struct file *newfile(char *str, struct file *fphead); 364 | void freefiles(struct file *fp); 365 | int is_valid_target(const char *name); 366 | void pragmas_from_env(void); 367 | void pragmas_to_env(void); 368 | -------------------------------------------------------------------------------- /modtime.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Get modification time of file or archive member 3 | */ 4 | #include "make.h" 5 | #include 6 | 7 | /* 8 | * Read a number from an archive header. 9 | */ 10 | static size_t 11 | argetnum(const char *str, int len) 12 | { 13 | const char *s; 14 | size_t val = 0; 15 | 16 | for (s = str; s < str + len && isdigit(*s); s++) { 17 | // Half-hearted attempt to avoid overflow 18 | if (val > (INT_MAX - 1)/10) 19 | break; 20 | val = val * 10 + *s - '0'; 21 | } 22 | if (s != str + len && *s != ' ') 23 | error("invalid archive"); 24 | return val; 25 | } 26 | 27 | /* 28 | * Search an archive for the given member and return its timestamp or 0. 29 | * This code assumes System V/GNU archive format. 30 | */ 31 | static time_t 32 | arsearch(FILE *fd, const char *member) 33 | { 34 | struct ar_hdr hdr; 35 | char *s, *t, *names = NULL; 36 | size_t len, offset, max_offset = 0; 37 | time_t mtime = 0; 38 | 39 | do { 40 | top: 41 | len = fread(&hdr, 1, sizeof(hdr), fd); 42 | if (len < sizeof(hdr) || 43 | memcmp(hdr.ar_fmag, ARFMAG, sizeof(hdr.ar_fmag)) != 0) { 44 | if (feof(fd)) 45 | break; 46 | error("invalid archive"); 47 | } 48 | 49 | // Get length of this member. Length in the file is padded 50 | // to an even number of bytes. 51 | len = argetnum(hdr.ar_size, sizeof(hdr.ar_size)); 52 | if (len % 2 == 1) 53 | len++; 54 | 55 | t = hdr.ar_name; 56 | if (hdr.ar_name[0] == '/') { 57 | if (hdr.ar_name[1] == ' ') { 58 | // Skip symbol table 59 | continue; 60 | } else if (hdr.ar_name[1] == '/' && names == NULL) { 61 | // Save list of extended filenames for later use 62 | names = xmalloc(len); 63 | if (fread(names, 1, len, fd) != len) 64 | error("invalid archive"); 65 | // Replace newline separators with NUL 66 | for (s = names; s < names + len; s++) { 67 | if (*s == '\n') 68 | *s = '\0'; 69 | } 70 | max_offset = len; 71 | goto top; 72 | } else if (isdigit(hdr.ar_name[1]) && names) { 73 | // An extended filename, get its offset in the names list 74 | offset = argetnum(hdr.ar_name + 1, sizeof(hdr.ar_name) - 1); 75 | if (offset > max_offset) 76 | error("invalid archive"); 77 | t = names + offset; 78 | } else { 79 | error("invalid archive"); 80 | } 81 | } 82 | 83 | s = strchr(t, '/'); 84 | if (s == NULL) 85 | error("invalid archive"); 86 | *s = '\0'; 87 | 88 | if (strcmp(t, member) == 0) { 89 | mtime = argetnum(hdr.ar_date, sizeof(hdr.ar_date)); 90 | break; 91 | } 92 | } while (fseek(fd, len, SEEK_CUR) == 0); 93 | free(names); 94 | return mtime; 95 | } 96 | 97 | static time_t 98 | artime(const char *archive, const char *member) 99 | { 100 | FILE *fd; 101 | char magic[SARMAG]; 102 | size_t len; 103 | time_t mtime; 104 | 105 | fd = fopen(archive, "r"); 106 | if (fd == NULL) 107 | return 0; 108 | 109 | len = fread(magic, 1, sizeof(magic), fd); 110 | if (len < sizeof(magic) || memcmp(magic, ARMAG, SARMAG) != 0) 111 | error("%s: not an archive", archive); 112 | 113 | mtime = arsearch(fd, member); 114 | fclose(fd); 115 | return mtime; 116 | } 117 | 118 | /* 119 | * If the name is of the form 'libname(member.o)' split it into its 120 | * name and member parts and set the member pointer to point to the 121 | * latter. Otherwise just take a copy of the name and don't alter 122 | * the member pointer. 123 | * 124 | * In either case the return value is an allocated string which must 125 | * be freed by the caller. 126 | */ 127 | char * 128 | splitlib(const char *name, char **member) 129 | { 130 | char *s, *t; 131 | size_t len; 132 | 133 | t = xstrdup(name); 134 | s = strchr(t, '('); 135 | if (s) { 136 | // We have 'libname(member.o)' 137 | *s++ = '\0'; 138 | len = strlen(s); 139 | if (len <= 1 || s[len - 1] != ')' || *t == '\0') 140 | error("invalid name '%s'", name); 141 | s[len - 1] = '\0'; 142 | *member = s; 143 | } 144 | return t; 145 | } 146 | 147 | /* 148 | * Get the modification time of a file. Set it to 0 if the file 149 | * doesn't exist. 150 | */ 151 | void 152 | modtime(struct name *np) 153 | { 154 | char *name, *member = NULL; 155 | struct stat info; 156 | 157 | name = splitlib(np->n_name, &member); 158 | if (member) { 159 | // Looks like library(member) 160 | np->n_tim.tv_sec = artime(name, member); 161 | np->n_tim.tv_nsec = 0; 162 | } else if (stat(name, &info) < 0) { 163 | if (errno != ENOENT) 164 | error("can't open %s: %s", name, strerror(errno)); 165 | np->n_tim.tv_sec = 0; 166 | np->n_tim.tv_nsec = 0; 167 | } else { 168 | np->n_tim.tv_sec = info.st_mtim.tv_sec; 169 | np->n_tim.tv_nsec = info.st_mtim.tv_nsec; 170 | } 171 | free(name); 172 | } 173 | -------------------------------------------------------------------------------- /pdpmake.1: -------------------------------------------------------------------------------- 1 | .TH PDPMAKE 1 "19 January 2025" "Ron Yorston" "Usage Manual" 2 | .SH NAME 3 | .B pdpmake 4 | \(en public domain POSIX make 5 | .SH SYNOPSIS 6 | 7 | \fBpdpmake\fP 8 | .RB [ --posix ] 9 | .RB [ -ehiknpqrSst ] 10 | .RB [ -C 11 | .IR dir ] 12 | .RB [ -f 13 | .IR file ] 14 | .RB [ -j 15 | .IR num_jobs ] 16 | .RB [ -x \ \fIpragma\fP] 17 | .RI [ macro [:[:[:]]]= value \0...] 18 | .RI [ target \0...] 19 | 20 | .SH DESCRIPTION 21 | 22 | The 23 | .B pdpmake 24 | utility creates or updates files following a set of rules. The created or 25 | updated files, called targets, are typically derived from other files, called 26 | prerequisites. Targets are described by rules which list commands to be 27 | executed. Rules may be inferred by the utility or explicitly defined in one or 28 | more files. A target may also depend on other targets, whose rules must be 29 | executed before its. Files which contain these targets are known as makefiles. 30 | .SH OPTIONS 31 | 32 | .IP \fB--posix\fP 33 | Enable strict POSIX-compliant mode. This option must be the first given on the 34 | command line. 35 | .IP \fB-C\fP\ \fIdir\fP 36 | Before execution, switch to 37 | .IR dir . 38 | If specified multiple times, each invocation is interpreted relative to the 39 | previous one: 40 | \(oq\fB-C\fP 41 | / 42 | .B -C 43 | etc\(cq is equivalent to 44 | \(oq\fB-C\fP /etc\(cq. 45 | .IP \fB-e\fP 46 | Allow environment variables to override macro assignments. 47 | .IP \fB-f\fP\ \fIfile\fP 48 | Specify a makefile 49 | .I file 50 | from which to read. If invoked multiple times, specified files are read in order 51 | of invocation. See 52 | .B INPUT FILES 53 | section. 54 | .IP \fB-h\fP 55 | Display help information. 56 | .IP \fB-i\fP 57 | If commands in rules exit unsuccessfully, ignore them and continue. This 58 | option is equivalent to specifying the special target 59 | .B .IGNORE 60 | without prerequisites. 61 | .IP \fB-j\fP\ \fInum_jobs\fP 62 | Ignored. Exists for compatibility with other 63 | .BR make (1) 64 | implementations. 65 | .IP \fB-k\fP 66 | If an error is encountered, continue processing rules. Recipes for targets which 67 | depend on other targets that have caused errors are not executed. 68 | .IP \fB-n\fP 69 | Print without executing commands that are not prefixed with \(oq+\(cq. 70 | .IP \fB-p\fP 71 | Print macro definitions and rules during execution. 72 | .IP \fB-q\fP 73 | If specified targets are up to date, exit successfully. Otherwise, exit with an 74 | error code of 1. Do not execute any rules. 75 | .IP \fB-r\fP 76 | Do not use the built-in rules. Clear the suffix list. 77 | .IP \fB-S\fP 78 | Stop processing if an error is encountered. This is the default behaviour and 79 | the opposite of 80 | .BR -k . 81 | .IP \fB-s\fP 82 | Do not print commands as they are executed. This is equivalent to using the 83 | special target 84 | .B .SILENT 85 | without prerequisites. 86 | .IP \fB-t\fP 87 | Instead of executing rules, touch target files. Ignores targets that are 88 | up-to-date or those which have no rules specified. 89 | .IP \fB-x\fP\ \fIpragma\fP 90 | Allow certain extensions when using strict POSIX-compliant mode. For a list of 91 | supported pragmas, see the 92 | .B PRAGMAS 93 | section. This option may be specified more than once to utilize multiple 94 | extensions. 95 | .IP \fImacro\fP[:[:[:]]]=\fIvalue\fP 96 | Assign 97 | .I value 98 | to 99 | .IR macro , 100 | overriding the value of 101 | .I macro 102 | in the input if it exists. Macro assignments and targets may be mixed on the 103 | command line. Assignments will be processed before targets. 104 | .SH INPUT FILES 105 | 106 | If an input makefile is unspecified, the working directory will be searched for 107 | a file named \(oqPDPmakefile\(cq, \(oqmakefile\(cq or \(oqMakefile\(cq, in 108 | that order. The first found will be used. 109 | 110 | If the input file is \(oq-\(cq, the standard input shall be used as the input. 111 | .SH PRAGMAS 112 | 113 | While in strict POSIX-compliant mode, pragmas may be utilized to selectively 114 | enable certain extensions. 115 | 116 | .IP \fBmacro_name\fP 117 | Allow \(oq-\(cq in macro names. 118 | .IP \fBtarget_name\fP 119 | Allow \(oq-\(cq and \(oq/\(cq in target names. 120 | .IP \fBcommand_comment\fP 121 | Don't treat the \(oq#\(cq character as introducing a comment in commands or in 122 | target and inference rules. 123 | .IP \fBempty_suffix\fP 124 | Permit an empty suffix in macro expansions. This takes the form of $(VAR:=.c). 125 | .IP \fBposix_2017\fP 126 | Enforce the old POSIX.1-2017 standard. 127 | .IP \fBposix_2024\fP 128 | Enforce the current POSIX.1-2024 standard. In this case the 129 | .B macro_name 130 | and 131 | .B target_name 132 | pragmas aren\(cqt required as this standard allows these additional characters. 133 | .IP \fBposix_202x\fP 134 | An alias for the 135 | .B posix_2024 136 | pragma. 137 | .IP \fBwindows\fP 138 | Allow Microsoft Windows-style paths as target names. This may also require 139 | specifying the 140 | .B target_name 141 | pragma. 142 | .SH STANDARDS 143 | 144 | This utility follows the 145 | .I Section 12.2, Utility Syntax Guidelines 146 | of the POSIX.1-2024 standard for the 147 | .B make 148 | utility. 149 | 150 | The behavior in this utility should match the behavior described in this 151 | section but also includes a set of extensions. Its behavior may be fine-tuned 152 | using command line options, environment variables, or special targets. See the 153 | .B PRAGMAS 154 | section. 155 | 156 | By default, all extensions are enabled. The following list contains all of the 157 | methods for disabling all extensions and enabling strict POSIX-compliant mode: 158 | .RS 159 | .IP \(bu 3 160 | Add the special target 161 | .B .POSIX 162 | as the first non-comment line in the first input to be processed. This is the 163 | standard approach for declaring a makefile to be POSIX-compliant. 164 | .IP \(bu 3 165 | Add the 166 | .B --posix 167 | flag as the first command line option given to 168 | .BR pdpmake .\ This 169 | flag is unique to 170 | .B pdpmake 171 | and may not be available in other 172 | .BR make (1) 173 | implementations. 174 | .IP \(bu 3 175 | Set the 176 | .B PDPMAKE_POSIXLY_CORRECT 177 | environment variable to any value. This environment variable is unique to 178 | .B pdpmake 179 | and may not be available in other 180 | .BR make (1) 181 | implementations. 182 | .RE 183 | 184 | A set of extensions to the POSIX standard are implemented for this 185 | utility. These may be compatible with other 186 | .BR make (1) 187 | implementations. The available extensions are: 188 | .RS 189 | .IP \(bu 3 190 | The 191 | .B -C 192 | directory command line option changes the current working directory. 193 | .IP \(bu 3 194 | Double-colon rules are allowed. 195 | .IP \(bu 3 196 | The conditional keywords 197 | .BR ifdef , 198 | .BR ifndef , 199 | .BR ifeq , 200 | .BR ifneq , 201 | .BR else ,\ and 202 | .B endif 203 | are implemented. 204 | .IP \(bu 3 205 | Archive members can be specified using the form 206 | .BR lib.a (mem1.o\ mem2.o...). 207 | .IP \(bu 3 208 | The macro assignment operator 209 | .B := 210 | is implemented. It is equivalent to the POSIX-specified 211 | .BR ::= 212 | macro assignment operator. 213 | .IP \(bu 3 214 | The suffixes used in the definition of inference rules can contain any 215 | number of periods or none. 216 | .IP \(bu 3 217 | Chained inference rules can be used when searching for the prerequisites of a 218 | target. This means that if there are inference rules 219 | .I .p.q 220 | and 221 | .I .q.r 222 | and the file 223 | .I thing.p 224 | exists, the method by which the file 225 | .I thing.r 226 | is created can be deduced. 227 | .IP \(bu 3 228 | The wildcards \(oq*\(cq, \(oq?\(cq and \(oq[]\(cq can be used in the targets 229 | and prerequisites of target rules. 230 | .IP \(bu 3 231 | The \(oq#\(cq character on a command line or in a macro expansion doesn\(cqt 232 | indicate the start of a comment. In other locations, \(oq#\(cq can be escaped by 233 | preceding it with a backslash. 234 | .IP \(bu 3 235 | Duplicated prerequisites are removed when the internal macro 236 | .B $? 237 | is expanded. 238 | .IP \(bu 3 239 | An 240 | .B include 241 | line with no files specified is silently ignored. At least one blank must follow 242 | the 243 | .B include 244 | for the line to be valid. 245 | .IP \(bu 3 246 | The shell used to process build commands isn\(cqt started with the 247 | .B -e 248 | option when errors aren\(cqt being ignored. 249 | .IP \(bu 3 250 | Macro definitions and targets may be mixed on the command line. The macro 251 | definitions are processed first, then the targets. 252 | .IP \(bu 3 253 | The 254 | .B $< 255 | and 256 | .B $* 257 | internal macros are given values in target rules. 258 | .IP \(bu 3 259 | When a build command receives a signal, the target is removed. 260 | .IP \(bu 3 261 | Inference rules and the 262 | .B .DEFAULT 263 | target aren't used for phony targets. 264 | .IP \(bu 3 265 | Pragmas are propagated to recursive invocations of 266 | .B pdpmake. 267 | 268 | 269 | .RE 270 | 271 | The following implementation details apply only to the POSIX.1-2024 standard: 272 | .RS 273 | .IP \(bu 3 274 | Nested macro expansion syntax: $(FOO$(BAR)) 275 | .IP \(bu 3 276 | Prerequisites of the 277 | .B .PHONY 278 | special target are always considered out-of-date. 279 | .IP \(bu 3 280 | More than one file can be specified on each include line. 281 | .IP \(bu 3 282 | Missing include files can be ignored by using 283 | .B -include 284 | .I file 285 | instead of 286 | .B include 287 | .IR file . 288 | .IP \(bu 3 289 | Missing or out-of-date include files are rebuilt if an appropriate 290 | rule can be found. 291 | .IP \(bu 3 292 | The 293 | .B $^ 294 | and 295 | .B $+ 296 | internal macros evaluate to all prerequisites of the current target (not just 297 | out-of-date ones, as with 298 | .BR $?). 299 | .B $^ 300 | removes duplicated prerequisites from the list, 301 | .B $+ 302 | doesn\(cqt. 303 | .IP \(bu 3 304 | If no 305 | .B MAKE 306 | environment variable is provided the 307 | .B MAKE 308 | macro is initialised from argv[0], with a relative path converted to absolute. 309 | .IP \(bu 3 310 | The 311 | .BR ::= , 312 | .BR :::= , 313 | .BR += , 314 | .BR ?= , 315 | and 316 | .BR != 317 | macro assignments. 318 | .IP \(bu 3 319 | Pattern macros extend the standard suffix substitution in macro expansion to 320 | allow changes to prefixes. 321 | .IP \(bu 3 322 | An escaped newline within a macro expansion in a rule is replaced by a space. 323 | .IP \(bu 3 324 | The 325 | .B CURDIR 326 | macro is set to the current directory during program start up. 327 | .SH COPYRIGHT 328 | 329 | .B pdpmake 330 | is free and unencumbered software released into the public domain. See 331 | . 332 | .SH SEE ALSO 333 | .BR make (1p) 334 | -------------------------------------------------------------------------------- /rules.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Control of the implicit suffix rules 3 | */ 4 | #include "make.h" 5 | 6 | /* 7 | * Return a pointer to the suffix of a name (which may be the 8 | * terminating NUL if there's no suffix). 9 | */ 10 | char * 11 | suffix(const char *name) 12 | { 13 | char *p = strrchr(name, '.'); 14 | return p ? p : (char *)name + strlen(name); 15 | } 16 | 17 | /* 18 | * Find a name structure whose name is formed by concatenating two 19 | * strings. If 'create' is TRUE the name is created if necessary. 20 | */ 21 | static struct name * 22 | namecat(const char *s, const char *t, int create) 23 | { 24 | char *p; 25 | struct name *np; 26 | 27 | p = xconcat3(s, t, ""); 28 | np = create ? newname(p) : findname(p); 29 | free(p); 30 | return np; 31 | } 32 | 33 | /* 34 | * Search for an inference rule to convert some suffix ('psuff') 35 | * to the target suffix 'tsuff'. The basename of the prerequisite 36 | * is 'base'. 37 | */ 38 | struct name * 39 | dyndep0(char *base, const char *tsuff, struct rule *infrule) 40 | { 41 | char *psuff; 42 | struct name *xp; // Suffixes 43 | struct name *sp; // Suffix rule 44 | struct rule *rp; 45 | struct depend *dp; 46 | IF_NOT_FEATURE_MAKE_EXTENSIONS(const) bool chain = FALSE; 47 | 48 | xp = newname(".SUFFIXES"); 49 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 50 | retry: 51 | #endif 52 | for (rp = xp->n_rule; rp; rp = rp->r_next) { 53 | for (dp = rp->r_dep; dp; dp = dp->d_next) { 54 | // Generate new suffix rule to try 55 | psuff = dp->d_name->n_name; 56 | sp = namecat(psuff, tsuff, FALSE); 57 | if (sp && sp->n_rule) { 58 | struct name *ip; 59 | int got_ip; 60 | 61 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 62 | // Has rule already been used in this chain? 63 | if ((sp->n_flag & N_MARK)) 64 | continue; 65 | #endif 66 | // Generate a name for an implicit prerequisite 67 | ip = namecat(base, psuff, TRUE); 68 | if ((ip->n_flag & N_DOING)) 69 | continue; 70 | 71 | if (!ip->n_tim.tv_sec) 72 | modtime(ip); 73 | 74 | if (!chain) { 75 | got_ip = ip->n_tim.tv_sec || (ip->n_flag & N_TARGET); 76 | } 77 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 78 | else { 79 | sp->n_flag |= N_MARK; 80 | got_ip = dyndep(ip, NULL, NULL) != NULL; 81 | sp->n_flag &= ~N_MARK; 82 | } 83 | #endif 84 | 85 | if (got_ip) { 86 | // Prerequisite exists or we know how to make it 87 | if (infrule) { 88 | infrule->r_dep = newdep(ip, NULL); 89 | infrule->r_cmd = sp->n_rule->r_cmd; 90 | } 91 | return ip; 92 | } 93 | } 94 | } 95 | } 96 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 97 | // If we didn't find an existing file or an explicit rule try 98 | // again, this time looking for a chained inference rule. 99 | if (!posix && !chain) { 100 | chain = TRUE; 101 | goto retry; 102 | } 103 | #endif 104 | return NULL; 105 | } 106 | 107 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 108 | /* 109 | * If 'name' ends with 'suffix' return an allocated string containing 110 | * the name with the suffix removed, else return NULL. 111 | */ 112 | char * 113 | has_suffix(const char *name, const char *suffix) 114 | { 115 | ssize_t delta = strlen(name) - strlen(suffix); 116 | char *base = NULL; 117 | 118 | if (delta > 0 && strcmp(name + delta, suffix) == 0) { 119 | base = xstrdup(name); 120 | base[delta] = '\0'; 121 | } 122 | 123 | return base; 124 | } 125 | #endif 126 | 127 | /* 128 | * Dynamic dependency. This routine applies the suffix rules 129 | * to try and find a source and a set of rules for a missing 130 | * target. NULL is returned on failure. On success the name of 131 | * the implicit prerequisite is returned and the rule used is 132 | * placed in the infrule structure provided by the caller. 133 | */ 134 | struct name * 135 | dyndep(struct name *np, struct rule *infrule, const char **ptsuff) 136 | { 137 | const char *tsuff; 138 | char *base, *name, *member; 139 | struct name *pp = NULL; // Implicit prerequisite 140 | 141 | member = NULL; 142 | name = splitlib(np->n_name, &member); 143 | 144 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 145 | // POSIX only allows inference rules with one or two periods. 146 | // As an extension this restriction is lifted, but not for 147 | // targets of the form lib.a(member.o). 148 | if (!posix && member == NULL) { 149 | struct name *xp = newname(".SUFFIXES"); 150 | int found_suffix = FALSE; 151 | 152 | for (struct rule *rp = xp->n_rule; rp; rp = rp->r_next) { 153 | for (struct depend *dp = rp->r_dep; dp; dp = dp->d_next) { 154 | tsuff = dp->d_name->n_name; 155 | base = has_suffix(name, tsuff); 156 | if (base) { 157 | found_suffix = TRUE; 158 | pp = dyndep0(base, tsuff, infrule); 159 | free(base); 160 | if (pp) { 161 | goto done; 162 | } 163 | } 164 | } 165 | } 166 | 167 | if (!found_suffix) { 168 | // The name didn't have a known suffix. Try single-suffix rule. 169 | tsuff = ""; 170 | pp = dyndep0(name, tsuff, infrule); 171 | if (pp) { 172 | done: 173 | if (ptsuff) { 174 | *ptsuff = tsuff; 175 | } 176 | } 177 | } 178 | } else 179 | #endif 180 | { 181 | tsuff = xstrdup(suffix(name)); 182 | base = member ? member : name; 183 | *suffix(base) = '\0'; 184 | 185 | pp = dyndep0(base, tsuff, infrule); 186 | free((void *)tsuff); 187 | } 188 | free(name); 189 | 190 | return pp; 191 | } 192 | 193 | #define RULES \ 194 | ".c.o:\n" \ 195 | " $(CC) $(CFLAGS) -c $<\n" \ 196 | ".y.o:\n" \ 197 | " $(YACC) $(YFLAGS) $<\n" \ 198 | " $(CC) $(CFLAGS) -c y.tab.c\n" \ 199 | " rm -f y.tab.c\n" \ 200 | " mv y.tab.o $@\n" \ 201 | ".y.c:\n" \ 202 | " $(YACC) $(YFLAGS) $<\n" \ 203 | " mv y.tab.c $@\n" \ 204 | ".l.o:\n" \ 205 | " $(LEX) $(LFLAGS) $<\n" \ 206 | " $(CC) $(CFLAGS) -c lex.yy.c\n" \ 207 | " rm -f lex.yy.c\n" \ 208 | " mv lex.yy.o $@\n" \ 209 | ".l.c:\n" \ 210 | " $(LEX) $(LFLAGS) $<\n" \ 211 | " mv lex.yy.c $@\n" \ 212 | ".c.a:\n" \ 213 | " $(CC) -c $(CFLAGS) $<\n" \ 214 | " $(AR) $(ARFLAGS) $@ $*.o\n" \ 215 | " rm -f $*.o\n" \ 216 | ".c:\n" \ 217 | " $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<\n" \ 218 | ".sh:\n" \ 219 | " cp $< $@\n" \ 220 | " chmod a+x $@\n" 221 | 222 | #define RULES_2017 \ 223 | ".SUFFIXES:.o .c .y .l .a .sh .f\n" \ 224 | ".f.o:\n" \ 225 | " $(FC) $(FFLAGS) -c $<\n" \ 226 | ".f.a:\n" \ 227 | " $(FC) -c $(FFLAGS) $<\n" \ 228 | " $(AR) $(ARFLAGS) $@ $*.o\n" \ 229 | " rm -f $*.o\n" \ 230 | ".f:\n" \ 231 | " $(FC) $(FFLAGS) $(LDFLAGS) -o $@ $<\n" 232 | 233 | #define RULES_2024 \ 234 | ".SUFFIXES:.o .c .y .l .a .sh\n" 235 | 236 | #define MACROS \ 237 | "CFLAGS=-O1\n" \ 238 | "YACC=yacc\n" \ 239 | "YFLAGS=\n" \ 240 | "LEX=lex\n" \ 241 | "LFLAGS=\n" \ 242 | "AR=ar\n" \ 243 | "ARFLAGS=-rv\n" \ 244 | "LDFLAGS=\n" 245 | 246 | #define MACROS_2017 \ 247 | "CC=c99\n" \ 248 | "FC=fort77\n" \ 249 | "FFLAGS=-O1\n" \ 250 | 251 | #define MACROS_2024 \ 252 | "CC=c17\n" 253 | 254 | #define MACROS_EXT \ 255 | "CC=cc\n" 256 | 257 | /* 258 | * Read the built-in rules using a fake fgets-like interface. 259 | */ 260 | char * 261 | getrules(char *s, int size) 262 | { 263 | char *r = s; 264 | static const char *rulepos = NULL; 265 | static int rule_idx = 0; 266 | 267 | if (rulepos == NULL || *rulepos == '\0') { 268 | if (rule_idx == 0) { 269 | rulepos = MACROS; 270 | rule_idx++; 271 | } else if (rule_idx == 1) { 272 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 273 | if (POSIX_2017) 274 | rulepos = MACROS_2017; 275 | else if (posix) 276 | rulepos = MACROS_2024; 277 | else 278 | rulepos = MACROS_EXT; 279 | #elif ENABLE_FEATURE_MAKE_POSIX_2024 280 | rulepos = MACROS_2024; 281 | #else 282 | rulepos = MACROS_2017; 283 | #endif 284 | rule_idx++; 285 | } else if (!norules) { 286 | if (rule_idx == 2) { 287 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 288 | rulepos = POSIX_2017 ? RULES_2017 : RULES_2024; 289 | #elif ENABLE_FEATURE_MAKE_POSIX_2024 290 | rulepos = RULES_2024; 291 | #else 292 | rulepos = RULES_2017; 293 | #endif 294 | rule_idx++; 295 | } else if (rule_idx == 3) { 296 | rulepos = RULES; 297 | rule_idx++; 298 | } 299 | } 300 | } 301 | 302 | if (*rulepos == '\0') 303 | return NULL; 304 | 305 | while (--size) { 306 | if ((*r++ = *rulepos++) == '\n') 307 | break; 308 | } 309 | *r = '\0'; 310 | return s; 311 | } 312 | -------------------------------------------------------------------------------- /target.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Process name, rule, command and prerequisite structures 3 | */ 4 | #include "make.h" 5 | 6 | /* 7 | * Add a prerequisite to the end of the supplied list. 8 | * Return the new head pointer for that list. 9 | */ 10 | struct depend * 11 | newdep(struct name *np, struct depend *dphead) 12 | { 13 | struct depend *dpnew; 14 | struct depend *dp; 15 | 16 | dpnew = xmalloc(sizeof(struct depend)); 17 | dpnew->d_next = NULL; 18 | dpnew->d_name = np; 19 | dpnew->d_refcnt = 0; 20 | 21 | if (dphead == NULL) 22 | return dpnew; 23 | 24 | for (dp = dphead; dp->d_next; dp = dp->d_next) 25 | ; 26 | 27 | dp->d_next = dpnew; 28 | 29 | return dphead; 30 | } 31 | 32 | void 33 | freedeps(struct depend *dp) 34 | { 35 | struct depend *nextdp; 36 | 37 | if (dp && --dp->d_refcnt <= 0) { 38 | for (; dp; dp = nextdp) { 39 | nextdp = dp->d_next; 40 | free(dp); 41 | } 42 | } 43 | } 44 | 45 | /* 46 | * Add a command to the end of the supplied list of commands. 47 | * Return the new head pointer for that list. 48 | */ 49 | struct cmd * 50 | newcmd(char *str, struct cmd *cphead) 51 | { 52 | struct cmd *cpnew; 53 | struct cmd *cp; 54 | 55 | while (isspace(*str)) 56 | str++; 57 | 58 | cpnew = xmalloc(sizeof(struct cmd)); 59 | cpnew->c_next = NULL; 60 | cpnew->c_cmd = xstrdup(str); 61 | cpnew->c_refcnt = 0; 62 | cpnew->c_makefile = xstrdup(makefile); 63 | cpnew->c_dispno = dispno; 64 | 65 | if (cphead == NULL) 66 | return cpnew; 67 | 68 | for (cp = cphead; cp->c_next; cp = cp->c_next) 69 | ; 70 | 71 | cp->c_next = cpnew; 72 | 73 | return cphead; 74 | } 75 | 76 | void 77 | freecmds(struct cmd *cp) 78 | { 79 | struct cmd *nextcp; 80 | 81 | if (cp && --cp->c_refcnt <= 0) { 82 | for (; cp; cp = nextcp) { 83 | nextcp = cp->c_next; 84 | free(cp->c_cmd); 85 | free((void *)cp->c_makefile); 86 | free(cp); 87 | } 88 | } 89 | } 90 | 91 | struct name *namehead[HTABSIZE]; 92 | struct name *firstname; 93 | 94 | struct name * 95 | findname(const char *name) 96 | { 97 | struct name *np; 98 | 99 | for (np = namehead[getbucket(name)]; np; np = np->n_next) { 100 | if (strcmp(name, np->n_name) == 0) 101 | return np; 102 | } 103 | return NULL; 104 | } 105 | 106 | static int 107 | check_name(const char *name) 108 | { 109 | const char *s; 110 | 111 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 112 | # if defined(__CYGWIN__) 113 | if (!posix || (pragma & P_WINDOWS)) { 114 | if (isalpha(name[0]) && name[1] == ':' && name[2] == '/') { 115 | name += 3; 116 | } 117 | } 118 | # endif 119 | if (!posix) { 120 | for (s = name; *s; ++s) { 121 | if (*s == '=') 122 | return FALSE; 123 | } 124 | return TRUE; 125 | } 126 | #endif 127 | 128 | for (s = name; *s; ++s) { 129 | if (( 130 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 131 | (pragma & P_TARGET_NAME) || 132 | #endif 133 | #if ENABLE_FEATURE_MAKE_POSIX_2024 134 | !POSIX_2017 135 | #else 136 | FALSE 137 | #endif 138 | ) ? !(isfname(*s) || *s == '/') : !ispname(*s)) 139 | return FALSE; 140 | } 141 | return TRUE; 142 | } 143 | 144 | int 145 | is_valid_target(const char *name) 146 | { 147 | char *archive, *member = NULL; 148 | int ret; 149 | 150 | /* Names of the form 'lib(member)' are referred to as 'expressions' 151 | * in POSIX and are subjected to special treatment. The 'lib' 152 | * and 'member' elements must each be a valid target name. */ 153 | archive = splitlib(name, &member); 154 | ret = check_name(archive) && (member == NULL || check_name(member)); 155 | free(archive); 156 | 157 | return ret; 158 | } 159 | 160 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 161 | static int 162 | potentially_valid_target(const char *name) 163 | { 164 | int ret = FALSE; 165 | 166 | if (!(pragma & P_TARGET_NAME)) { 167 | pragma |= P_TARGET_NAME; 168 | ret = is_valid_target(name); 169 | pragma &= ~P_TARGET_NAME; 170 | } 171 | return ret; 172 | } 173 | #endif 174 | 175 | /* 176 | * Intern a name. Return a pointer to the name struct 177 | */ 178 | struct name * 179 | newname(const char *name) 180 | { 181 | struct name *np = findname(name); 182 | 183 | if (np == NULL) { 184 | unsigned int bucket; 185 | 186 | if (!is_valid_target(name)) 187 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 188 | error("invalid target name '%s'%s", name, 189 | potentially_valid_target(name) ? 190 | ": allow with pragma target_name" : ""); 191 | #else 192 | error("invalid target name '%s'", name); 193 | #endif 194 | 195 | bucket = getbucket(name); 196 | np = xmalloc(sizeof(struct name)); 197 | np->n_next = namehead[bucket]; 198 | namehead[bucket] = np; 199 | np->n_name = xstrdup(name); 200 | np->n_rule = NULL; 201 | np->n_tim = (struct timespec){0, 0}; 202 | np->n_flag = 0; 203 | } 204 | return np; 205 | } 206 | 207 | /* 208 | * Return the commands on the first rule that has them or NULL. 209 | */ 210 | struct cmd * 211 | getcmd(struct name *np) 212 | { 213 | struct rule *rp; 214 | 215 | if (np == NULL) 216 | return NULL; 217 | 218 | for (rp = np->n_rule; rp; rp = rp->r_next) 219 | if (rp->r_cmd) 220 | return rp->r_cmd; 221 | return NULL; 222 | } 223 | 224 | #if ENABLE_FEATURE_CLEAN_UP 225 | void 226 | freenames(void) 227 | { 228 | int i; 229 | struct name *np, *nextnp; 230 | 231 | for (i = 0; i < HTABSIZE; i++) { 232 | for (np = namehead[i]; np; np = nextnp) { 233 | nextnp = np->n_next; 234 | free(np->n_name); 235 | freerules(np->n_rule); 236 | free(np); 237 | } 238 | } 239 | } 240 | #endif 241 | 242 | void 243 | freerules(struct rule *rp) 244 | { 245 | struct rule *nextrp; 246 | 247 | for (; rp; rp = nextrp) { 248 | nextrp = rp->r_next; 249 | freedeps(rp->r_dep); 250 | freecmds(rp->r_cmd); 251 | free(rp); 252 | } 253 | } 254 | 255 | static void * 256 | inc_ref(void *vp) 257 | { 258 | if (vp) { 259 | struct depend *dp = vp; 260 | if (dp->d_refcnt == INT_MAX) 261 | error("out of memory"); 262 | dp->d_refcnt++; 263 | } 264 | return vp; 265 | } 266 | 267 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 268 | // Order must match constants in make.h 269 | // POSIX levels must be last and in increasing order 270 | static const char *p_name[] = { 271 | "macro_name", 272 | "target_name", 273 | "command_comment", 274 | "empty_suffix", 275 | #if defined(__CYGWIN__) 276 | "windows", 277 | #endif 278 | "posix_2017", 279 | "posix_2024", 280 | "posix_202x" 281 | }; 282 | 283 | void 284 | set_pragma(const char *name) 285 | { 286 | int i; 287 | 288 | // posix_202x is an alias for posix_2024 289 | for (i = 0; i < sizeof(p_name)/sizeof(p_name[0]); ++i) { 290 | if (strcmp(name, p_name[i]) == 0) { 291 | #if !ENABLE_FEATURE_MAKE_POSIX_2024 292 | if (i == BIT_POSIX_2024 || i == BIT_POSIX_202X) { 293 | break; 294 | } 295 | #endif 296 | if (i >= BIT_POSIX_2017) { 297 | // POSIX level is stored in a separate variable. 298 | // No bits in 'pragma' are used. 299 | if (posix_level == DEFAULT_POSIX_LEVEL) { 300 | posix_level = i - BIT_POSIX_2017; 301 | if (posix_level > STD_POSIX_2024) 302 | posix_level = STD_POSIX_2024; 303 | } else if (posix_level != i - BIT_POSIX_2017) 304 | warning("unable to change POSIX level"); 305 | } else { 306 | pragma |= 1 << i; 307 | } 308 | return; 309 | } 310 | } 311 | warning("invalid pragma '%s'", name); 312 | } 313 | 314 | void 315 | pragmas_to_env(void) 316 | { 317 | int i; 318 | char *val = NULL; 319 | 320 | for (i = 0; i < BIT_POSIX_2017; ++i) { 321 | if ((pragma & (1 << i))) 322 | val = xappendword(val, p_name[i]); 323 | } 324 | 325 | if (posix_level != DEFAULT_POSIX_LEVEL) 326 | val = xappendword(val, p_name[BIT_POSIX_2017 + posix_level]); 327 | 328 | if (val) { 329 | setenv("PDPMAKE_PRAGMAS", val, 1); 330 | free(val); 331 | } 332 | } 333 | #endif 334 | 335 | /* 336 | * Add a new rule to a target. This checks to see if commands already 337 | * exist for the target. If flag is TRUE the target can have multiple 338 | * rules with commands (double-colon rules). 339 | * 340 | * i) If the name is a special target and there are no prerequisites 341 | * or commands to be added remove all prerequisites and commands. 342 | * This is necessary when clearing a built-in inference rule. 343 | * ii) If name is a special target and has commands, replace them. 344 | * This is for redefining commands for an inference rule. 345 | */ 346 | void 347 | addrule(struct name *np, struct depend *dp, struct cmd *cp, int flag) 348 | { 349 | struct rule *rp; 350 | struct rule **rpp; 351 | 352 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 353 | // Can't mix single-colon and double-colon rules 354 | if (!posix && (np->n_flag & N_TARGET)) { 355 | if (!(np->n_flag & N_DOUBLE) != !flag) // like xor 356 | error("inconsistent rules for target %s", np->n_name); 357 | } 358 | #endif 359 | 360 | // Clear out prerequisites and commands 361 | if ((np->n_flag & N_SPECIAL) && !dp && !cp) { 362 | #if ENABLE_FEATURE_MAKE_POSIX_2024 363 | if (strcmp(np->n_name, ".PHONY") == 0) 364 | return; 365 | #endif 366 | freerules(np->n_rule); 367 | np->n_rule = NULL; 368 | return; 369 | } 370 | 371 | if (cp && !(np->n_flag & N_DOUBLE) && getcmd(np)) { 372 | // Handle the inference rule redefinition case 373 | // .DEFAULT rule can also be redefined (as an extension). 374 | if ((np->n_flag & N_INFERENCE) 375 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 376 | && !(posix && (np->n_flag & N_SPECIAL)) 377 | #endif 378 | ) { 379 | freerules(np->n_rule); 380 | np->n_rule = NULL; 381 | } else { 382 | error("commands defined twice for target %s", np->n_name); 383 | } 384 | } 385 | 386 | rpp = &np->n_rule; 387 | while (*rpp) 388 | rpp = &(*rpp)->r_next; 389 | 390 | *rpp = rp = xmalloc(sizeof(struct rule)); 391 | rp->r_next = NULL; 392 | rp->r_dep = inc_ref(dp); 393 | rp->r_cmd = inc_ref(cp); 394 | 395 | np->n_flag |= N_TARGET; 396 | if (flag) 397 | np->n_flag |= N_DOUBLE; 398 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 399 | if (strcmp(np->n_name, ".PRAGMA") == 0) { 400 | for (; dp; dp = dp->d_next) { 401 | set_pragma(dp->d_name->n_name); 402 | } 403 | pragmas_to_env(); 404 | } 405 | #endif 406 | } 407 | -------------------------------------------------------------------------------- /testsuite/LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /testsuite/make.tests: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | . ./testing.sh 4 | unset MAKEFLAGS 5 | rm -rf make.tempdir 6 | 7 | # testing "test name" "command" "expected result" "file input" "stdin" 8 | 9 | # ================================================================= 10 | # The following tests work in POSIX mode or with extensions enabled 11 | # ================================================================= 12 | 13 | testing "Basic makefile" \ 14 | "make -f -" "target\n" "" ' 15 | target: 16 | @echo target 17 | ' 18 | 19 | testing ".DEFAULT rule for prerequisite" \ 20 | "make -f - 2>/dev/null" "source\n" "" ' 21 | .DEFAULT: 22 | @echo $@ 23 | target: source 24 | ' 25 | 26 | mkdir make.tempdir && cd make.tempdir || exit 1 27 | touch target.xyz 28 | testing "Empty command overrides inference rule" \ 29 | "make -f - target 2>/dev/null" "" "" ' 30 | .SUFFIXES: .xyz 31 | .xyz: 32 | @echo xyz 33 | target: ; 34 | ' 35 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null 36 | 37 | # Early versions of the code didn't properly implement skipping 38 | # certain macro expansions in POSIX 2017 mode. This is a design 39 | # decision: other implementations may justifiably do this 40 | # differently and fail this test. 41 | optional FEATURE_MAKE_POSIX_2024 42 | if [ "$SKIP" = "1" ] 43 | then 44 | SKIP= 45 | else 46 | SKIP=1 47 | fi 48 | testing "Macro skipping in POSIX 2017" \ 49 | "make -f -" "0 bc\n1\n2\n3\n4\n5\n" "" ' 50 | .POSIX: 51 | a = b 52 | b = c 53 | c = d 54 | $(a:.q=.v)$(b:.z=.v) = bc 55 | bcd = bcd 56 | target: 57 | @echo 0 $(bc) 58 | @echo 1 $($($(a))) 59 | @echo 2 $($(a) $(b) $(c)) 60 | @echo 3 $($a $b $c) 61 | @echo 4 $($(a)$(b)$(c)) 62 | @echo 5 $($a$b$c) 63 | ' 64 | SKIP= 65 | 66 | # Macros should be expanded before suffix substitution. The suffixes 67 | # can be obtained by macro expansion. 68 | testing "Macro expansion and suffix substitution" \ 69 | "make -f -" "src1.o src2.o\n" "" ' 70 | DOTC = .c 71 | DOTO = .o 72 | SRC1 = src1.c 73 | SRCS = $(SRC1) src2.c 74 | target: 75 | @echo $(SRCS:$(DOTC)=$(DOTO)) 76 | ' 77 | 78 | # Indeed, everything after the can be obtained by macro 79 | # macro expansion. 80 | testing "Macro expansion and suffix substitution 2" \ 81 | "make -f -" "src1.o src2.o\n" "" ' 82 | DOTS = .c=.o 83 | SRC1 = src1.c 84 | SRCS = $(SRC1) src2.c 85 | target: 86 | @echo $(SRCS:$(DOTS)) 87 | ' 88 | 89 | # It should be possible for an inference rule to determine that a 90 | # prerequisite can be created using an explicit rule. 91 | mkdir make.tempdir && cd make.tempdir || exit 1 92 | testing "Inference rule with explicit rule for prerequisite" \ 93 | "make -f -" "touch x.p\ncat x.p >x.q\n" "" ' 94 | .SUFFIXES: .p .q 95 | x.q: 96 | x.p: 97 | touch $@ 98 | .p.q: 99 | cat $< >$@ 100 | ' 101 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null 102 | 103 | # Austin Group defect report 875 clarifies certain aspects of the 104 | # behaviour of inference rules. Study of this resulted in a number 105 | # of changes to pdpmake, though this test passed anyway. 106 | mkdir make.tempdir && cd make.tempdir || exit 1 107 | touch test.j test.k 108 | testing "Proper handling of inference rules 1" \ 109 | "make -f -" \ 110 | ".j.l\n" "" ' 111 | .SUFFIXES: .j .k .l 112 | .j.l: 113 | @echo .j.l 114 | .k.l: 115 | @echo .k.l 116 | test.l: test.k 117 | test.j: 118 | test.k: 119 | ' 120 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null 121 | 122 | # Check that single-suffix inference rules work. 123 | mkdir make.tempdir && cd make.tempdir || exit 1 124 | touch test.sh 125 | testing "Single-suffix inference rule works" \ 126 | "make -f - -s; echo $?" \ 127 | "0\n" "" ' 128 | test: 129 | ' 130 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null 131 | 132 | # There was a bug where the failure of a build command didn't result 133 | # in make returning a non-zero exit status. 134 | testing "Return error if command fails" \ 135 | "make -f - >/dev/null 2>&1; test \$? -gt 0 && echo OK" "OK\n" "" ' 136 | target: 137 | @exit 42 138 | ' 139 | 140 | # An equal sign in a command on a target rule was detected as a 141 | # macro assignment. 142 | testing "Equal sign in inline command" \ 143 | "make -f -" "a = a\n" "" ' 144 | a = a 145 | target:;@echo a = $(a) 146 | ' 147 | 148 | # Ensure an inline command on a target rule can be detected even if 149 | # the semicolon is obfuscated. 150 | testing "Equal sign in obfuscated inline command" \ 151 | "make -f -" "a = a\n" "" ' 152 | a = a 153 | semi = ; 154 | target:$(semi)@echo a = $(a) 155 | ' 156 | 157 | # When a build command fails and the '-k' option has been provided 158 | # (continue execution on error) no further commands should be executed 159 | # for the current target. 160 | testing "Failure of build command with -k" \ 161 | "make -k -f - 2>/dev/null" "OK\n" "" ' 162 | all: bar baz 163 | bar: 164 | @echo OK 165 | @false 166 | @echo Not reached 167 | baz: 168 | @: 169 | ' 170 | # Build commands with a '+' prefix are executed even with the -q option. 171 | testing "Execute build command with + prefix and -q" \ 172 | "make -q -f - 2>/dev/null" "OK\n" "" ' 173 | all: bar 174 | bar: 175 | @+echo OK 176 | ' 177 | 178 | # The -t option touches files that are out-of-date unless the target 179 | # has no commands or they're already up-to-date. 180 | mkdir make.tempdir && cd make.tempdir || exit 1 181 | touch baz 182 | testing "Check -t option" \ 183 | "make -t -f - 2>/dev/null" "touch bar\n" "" ' 184 | all: foo bar baz 185 | foo: 186 | bar: 187 | @echo bar 188 | baz: 189 | @echo baz 190 | ' 191 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null 192 | 193 | # Build commands with a '+' prefix are executed even with the -t option. 194 | mkdir make.tempdir && cd make.tempdir || exit 1 195 | testing "Execute build command with + prefix and -t" \ 196 | "make -t -f - 2>/dev/null" "OK\n" "" ' 197 | all: bar 198 | bar: 199 | @+echo OK 200 | ' 201 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null 202 | 203 | # ================================================================= 204 | # The following tests require POSIX 2024 features to be enabled. 205 | # They may fail in POSIX 2017 mode. 206 | # ================================================================= 207 | 208 | optional FEATURE_MAKE_POSIX_2024 209 | # A macro created using ::= remembers it's of type immediate-expansion. 210 | # Immediate expansion also occurs when += is used to append to such a macro. 211 | testing "Appending to immediate-expansion macro" \ 212 | "make -f -" \ 213 | "hello 1 2 3\nhello 4 4\n" "" ' 214 | world = 1 215 | hello ::= hello $(world) 216 | world = 2 217 | hello += $(world) 218 | world = 3 219 | hello += $(world) 220 | world = 4 221 | 222 | world = 1 223 | reset ::= hello $(world) 224 | world = 2 225 | # No longer immediate-expansion 226 | reset = hello $(world) 227 | world = 3 228 | reset += $(world) 229 | world = 4 230 | 231 | target: 232 | @echo $(hello) 233 | @echo $(reset) 234 | ' 235 | 236 | # Since GNU make and bmake interpret := macro assignments differently, 237 | # POSIX has ::= for the GNU variant and :::= for BSD. 238 | testing "Different styles of := macro assignment" \ 239 | "make -f -" \ 240 | '65 a a $A\n' "" ' 241 | A = a 242 | GNU ::= $A 243 | BSD1 :::= $A 244 | BSD2 :::= $$A 245 | A = 65 246 | 247 | target: 248 | @echo '\''$(A) $(GNU) $(BSD1) $(BSD2)'\'' 249 | ' 250 | 251 | # Similar to the above but for macro assignments on the command line. 252 | # POSIX has ::= for the GNU variant and :::= for BSD. 253 | testing ":= macro assignment on command line" \ 254 | "make -f - A=a 'GNU::=\$A' 'BSD1:::=\$A' 'BSD2:::=\$\$A' A=65" \ 255 | '65 a a $A\n' "" ' 256 | target: 257 | @echo '\''$(A) $(GNU) $(BSD1) $(BSD2)'\'' 258 | ' 259 | 260 | # basic pattern macro expansion 261 | testing "Basic pattern macro expansion" \ 262 | "make -f -" \ 263 | "obj/util.o obj/main.o\n" "" ' 264 | SRC = src/util.c src/main.c 265 | OBJ = $(SRC:src/%.c=obj/%.o) 266 | 267 | target: 268 | @echo $(OBJ) 269 | ' 270 | 271 | # pattern macro expansion; match any value 272 | testing "Pattern macro expansion; match any value" \ 273 | "make -f -" \ 274 | "any_value.o\n" "" ' 275 | SRC = any_value 276 | OBJ = $(SRC:%=%.o) 277 | 278 | target: 279 | @echo $(OBJ) 280 | ' 281 | 282 | # pattern macro expansion with empty rvalue 283 | testing "Pattern macro expansion with empty rvalue" \ 284 | "make -f -" \ 285 | "\n" "" ' 286 | SRC = util.c main.c 287 | OBJ = $(SRC:%.c=) 288 | 289 | target: 290 | @echo $(OBJ) 291 | ' 292 | 293 | # pattern macro expansion with multiple in rvalue 294 | # POSIX requires the first to be expanded, others 295 | # may or may not be expanded. Permit either case. 296 | testing "Pattern macro expansion with multiple in rvalue" \ 297 | "make -f - | sed 's/mainmainmain/main%%/'" \ 298 | "main%%\n" "" ' 299 | SRC = main.c 300 | OBJ = $(SRC:%.c=%%%) 301 | 302 | target: 303 | @echo $(OBJ) 304 | ' 305 | 306 | # pattern macro expansion; zero match 307 | testing "Pattern macro expansion; zero match" \ 308 | "make -f -" \ 309 | "nsnp\n" "" ' 310 | WORD = osop 311 | REPL = $(WORD:os%op=ns%np) 312 | 313 | target: 314 | @echo $(REPL) 315 | ' 316 | 317 | # Check that MAKE will contain argv[0], e.g make in this case 318 | testing "Basic MAKE macro expansion" \ 319 | "make -f -" \ 320 | "make\n" "" ' 321 | target: 322 | @echo $(MAKE) 323 | ' 324 | 325 | # Check that MAKE defined as environment variable will overwrite default MAKE 326 | testing "MAKE macro expansion; overwrite with env macro" \ 327 | "MAKE=hello make -f -" \ 328 | "hello\n" "" ' 329 | target: 330 | @echo $(MAKE) 331 | ' 332 | 333 | # Check that MAKE defined on the command-line will overwrite MAKE defined in 334 | # Makefile 335 | testing "MAKE macro expansion; overwrite with command-line macro" \ 336 | "make -f - MAKE=hello" \ 337 | "hello\n" "" ' 338 | MAKE = test 339 | 340 | target: 341 | @echo $(MAKE) 342 | ' 343 | 344 | # POSIX draft states that if make was invoked using relative path, MAKE must 345 | # contain absolute path, not just argv[0] 346 | testing "MAKE macro expansion; turn relative path into absolute" \ 347 | "../make -f -" \ 348 | "ok\n" "" ' 349 | target: 350 | @case $(MAKE) in /*) test -e $(MAKE) && echo ok; esac 351 | ' 352 | 353 | # $? contains prerequisites newer than target, file2 in this case 354 | # $^ has all prerequisites, file1 and file2 355 | touch -t 202206171200 file1 356 | touch -t 202206171201 target 357 | touch -t 202206171202 file2 358 | testing "Compare \$? and \$^ internal macros" \ 359 | "make -f -" \ 360 | "file2\nfile1 file2\n" "" ' 361 | target: file1 file2 362 | @echo $? 363 | @echo $^ 364 | ' 365 | rm -f target file1 file2 366 | 367 | # Phony targets are executed (once) even if a matching file exists. 368 | # A .PHONY target with no prerequisites is ignored. 369 | touch -t 202206171201 target 370 | testing "Phony target" \ 371 | "make -f -" \ 372 | "phony\n" "" ' 373 | .PHONY: target 374 | .PHONY: 375 | target: 376 | @echo phony 377 | ' 378 | rm -f target 379 | 380 | # Phony targets aren't touched with -t 381 | testing "Phony target not touched" \ 382 | "make -t -f - >/dev/null && test -f target && echo target" \ 383 | "" "" ' 384 | .PHONY: target 385 | target: 386 | @: 387 | ' 388 | rm -f target 389 | 390 | # Include files are created or brought up-to-date 391 | mkdir make.tempdir && cd make.tempdir || exit 1 392 | testing "Create include file" \ 393 | "make -f -" \ 394 | "made\n" "" ' 395 | target: 396 | @echo $(VAR) 397 | mk: 398 | @echo "VAR = made" >mk 399 | include mk 400 | ' 401 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null 402 | 403 | # Include files are created or brought up-to-date even when the -n 404 | # option is given. 405 | mkdir make.tempdir && cd make.tempdir || exit 1 406 | testing "Create include file even with -n" \ 407 | "make -n -f -" \ 408 | "echo made\n" "" ' 409 | target: 410 | @echo $(VAR) 411 | mk: 412 | @echo "VAR = made" >mk 413 | include mk 414 | ' 415 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null 416 | 417 | # Failure to create an include file isn't an error. (Provided the 418 | # include line is ignoring non-existent files.) 419 | testing "Failure to create include file is OK" \ 420 | "make -f -" \ 421 | "OK\n" "" ' 422 | target: 423 | @echo OK 424 | mk: 425 | @: 426 | -include mk 427 | ' 428 | 429 | # $^ skips duplicate prerequisites, $+ doesn't 430 | mkdir make.tempdir && cd make.tempdir || exit 1 431 | touch file1 file2 file3 432 | testing "Skip duplicate entries in \$^ but not \$+" \ 433 | "make -f -" \ 434 | "file1 file2 file3\nfile1 file2 file2 file3 file3\n" "" ' 435 | target: file1 file2 file2 file3 file3 436 | @echo $^ 437 | @echo $+ 438 | ' 439 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null 440 | 441 | # Assign the output of a shell command to a macro. 442 | testing "Shell assignment" \ 443 | "make -f -" \ 444 | "1 2 3 4\n" "" ' 445 | hello != echo 1; echo 2; echo 3; echo; echo 446 | 447 | target: 448 | @echo "$(hello) 4" 449 | ' 450 | 451 | # Nested macro expansion is allowed. This should be compatible 452 | # with other implementations. 453 | testing "Nested macro expansion" \ 454 | "make -f -" "0 bc\n1 d\n2\n3\n4 bcd\n5 bcd\n" "" ' 455 | a = b 456 | b = c 457 | c = d 458 | $(a:.q=.v)$(b:.z=.v) = bc 459 | bcd = bcd 460 | target: 461 | @echo 0 $(bc) 462 | @echo 1 $($($(a))) 463 | @echo 2 $($(a) $(b) $(c)) 464 | @echo 3 $($a $b $c) 465 | @echo 4 $($(a)$(b)$(c)) 466 | @echo 5 $($a$b$c) 467 | ' 468 | 469 | # .WAIT is allowed as a prerequisite. Since parallel builds aren't 470 | # implemented it doesn't have any effect. 471 | mkdir make.tempdir && cd make.tempdir || exit 1 472 | touch file1 file2 473 | testing ".WAIT is allowed as a prerequisite" \ 474 | "make -f -" \ 475 | "file1 file2\n" "" ' 476 | target: file1 .WAIT file2 477 | @echo $? 478 | ' 479 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null 480 | 481 | # Escaped newlines inside macro expansions in commands get different 482 | # treatment than those outside. In POSIX 2017 the output is 'a b ab'. 483 | testing "Replace escaped NL in macro in command with space" \ 484 | "make -f -" \ 485 | "a b a b\n" "" ' 486 | M=word 487 | N=${M:word=a\\ 488 | b} 489 | target: 490 | @echo ${N} ${M:word=a\\ 491 | b} 492 | ' 493 | 494 | # The CURDIR macro is supported in POSIX 2024. 495 | testing "CURDIR macro" \ 496 | "make -f -" \ 497 | "OK\n" "" ' 498 | target: 499 | @test "$(CURDIR)" = "$$(pwd -P)" && echo OK 500 | ' 501 | # The CURDIR environment variable doesn't affect the macro 502 | export CURDIR=/you/are/here 503 | testing "CURDIR macro not affected by environment" \ 504 | "make -f -" \ 505 | "OK\n" "" ' 506 | target: 507 | @test "$(CURDIR)" != "/you/are/here" && echo OK 508 | ' 509 | 510 | # The -e option makes the CURDIR macro match the environment 511 | testing "With -e CURDIR macro is affected by the environment" \ 512 | "make -e -f -" \ 513 | "/you/are/here\n" "" ' 514 | target: 515 | @echo $(CURDIR) 516 | ' 517 | unset CURDIR 518 | 519 | # The fix for an equal sign in an inline command on a target rule broke 520 | # a complex chain of macro assignments generated by autotools. 521 | testing "Complex chain of macro assignments" \ 522 | "make -f -" "flag 1\n" "" ' 523 | FLAG_ = $(FLAG_$(VALUE)) 524 | FLAG_0 = flag 0 525 | FLAG_1 = flag 1 526 | MYFLAG = $(FLAG_$(VALUE)) 527 | VALUE = 1 528 | 529 | target: 530 | @echo $(MYFLAG) 531 | ' 532 | 533 | # POSIX 2024 permits additional characters in macro and target names 534 | testing "Allow - and / in target names, - in macro names" \ 535 | "make -f -" \ 536 | "/hello\nhel-lo\nmac-ro\n" "" ' 537 | target: ./hello hel-lo 538 | @echo $(mac-ro) 539 | ./hello: 540 | @echo /hello 541 | hel-lo: 542 | @echo hel-lo 543 | mac-ro = mac-ro 544 | ' 545 | SKIP= 546 | 547 | # ================================================================= 548 | # The following tests require non-POSIX extensions to be enabled. 549 | # They may fail in POSIX mode. 550 | # ================================================================= 551 | 552 | optional FEATURE_MAKE_EXTENSIONS 553 | # .DEFAULT rules with no commands or some prerequisites are ignored. 554 | # .DEFAULT rules with commands can be redefined. 555 | testing ".DEFAULT rule" \ 556 | "make -f - default" "default2\n" "" ' 557 | .DEFAULT: ignored 558 | .DEFAULT: 559 | @echo default1 560 | .DEFAULT: 561 | @echo default2 562 | target: 563 | ' 564 | 565 | testing "Double-colon rule" \ 566 | "make -f -" "target1\ntarget2\n" "" ' 567 | target:: 568 | @echo target1 569 | target:: 570 | @echo target2 571 | ' 572 | 573 | # There was a bug whereby the modification time of a file created by 574 | # double-colon rules wasn't correctly updated. This test checks that 575 | # the bug is now fixed. 576 | mkdir make.tempdir && cd make.tempdir || exit 1 577 | touch -t 202206171200 file1 578 | touch -t 202206171201 intermediate 579 | touch -t 202206171202 target 580 | touch -t 202206171203 file2 581 | testing "Target depends on prerequisite updated by double-colon rule" \ 582 | "make -f -" \ 583 | "file2\n" "" ' 584 | target: intermediate 585 | @cat intermediate 586 | intermediate:: file1 587 | @echo file1 >>intermediate 588 | intermediate:: file2 589 | @echo file2 >>intermediate 590 | ' 591 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null 592 | 593 | # Use chained inference rules to determine prerequisites. 594 | mkdir make.tempdir && cd make.tempdir || exit 1 595 | touch target.p 596 | testing "Chained inference rules" \ 597 | "make -s -f - target.s" \ 598 | "target.q\ntarget.r\ntarget.s\n" "" ' 599 | .SUFFIXES: .p .q .r .s 600 | .p.q: 601 | @cp $< $*.q 602 | @echo $*.q 603 | .q.r: 604 | @cp $< $*.r 605 | @echo $*.r 606 | .r.s: 607 | @cp $< $*.s 608 | @echo $*.s 609 | ' 610 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null 611 | 612 | # Suffixes with multiple periods are supported 613 | mkdir make.tempdir && cd make.tempdir || exit 1 614 | touch x.c.c 615 | testing "Support multi-period suffixes" \ 616 | "make -f -" "cat x.c.c >x.o.o\nx\n" "" ' 617 | .SUFFIXES: .c.c .o.o 618 | x.o.o: 619 | .c.c.o.o: 620 | cat $< >$@ 621 | @echo $* 622 | ' 623 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null 624 | 625 | # Suffixes with no periods are supported 626 | mkdir make.tempdir && cd make.tempdir || exit 1 627 | touch filex 628 | testing "Support suffixes without any periods" \ 629 | "make -f -" "cp filex fileh\nfile\ncp filex filez\nfile\n" "" ' 630 | .SUFFIXES: x h z 631 | all: fileh filez 632 | fileh: 633 | filez: filex 634 | cp filex filez 635 | @echo $* 636 | xh: 637 | cp $< $@ 638 | @echo $* 639 | ' 640 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null 641 | 642 | # make supports *, ? and [] wildcards in targets and prerequisites 643 | mkdir make.tempdir && cd make.tempdir || exit 1 644 | touch -t 202206171201 t1a t2aa t3b 645 | touch s1a s2aa s3b 646 | testing "Expand wildcards in filenames" \ 647 | "make -f - t1a t2aa t3b" \ 648 | "t1a s1a s2aa s3b\nt2aa s1a s2aa s3b\nt3b s1a s2aa s3b\n" "" ' 649 | t1? t2* t3[abc]: s1? s2* s3[abc] 650 | @echo $@ $? 651 | ' 652 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null 653 | 654 | # A '#' character in a macro expansion doesn't start a comment 655 | testing "Hash in macro expansion isn't a comment" \ 656 | "make -f -" \ 657 | ": hash # hash\n" "" ' 658 | HASH = hash 659 | hash = $(HASH:hash=#) 660 | target: 661 | : hash $(hash) hash 662 | ' 663 | 664 | # A '#' character can be escaped with a backslash 665 | testing "Backslash-escaped hash isn't a comment" \ 666 | "make -f -" \ 667 | ": hash # hash\n" "" ' 668 | hash = \\# 669 | target: 670 | : hash $(hash) hash 671 | ' 672 | 673 | # A '#' character in a command line doesn't start a comment 674 | testing "Hash in command line isn't a comment" \ 675 | "make -f -" \ 676 | ": hash # hash\n" "" ' 677 | target: 678 | : hash # hash 679 | ' 680 | 681 | # Austin Group defect report 875 (mentioned above) actually used 682 | # suffixes '.a .b .c'. This doesn't matter in POSIX mode but it 683 | # caused a failure (now fixed) when chained inference rules were 684 | # allowed. The '.a.c' and the built-in '.c.a' inference rules 685 | # resulted in a loop. 686 | mkdir make.tempdir && cd make.tempdir || exit 1 687 | touch test.a test.b 688 | testing "Proper handling of inference rules 2" \ 689 | "make -f -" \ 690 | ".a.c\n" "" ' 691 | .SUFFIXES: .a .b .c 692 | .a.c: 693 | @echo .a.c 694 | .b.c: 695 | @echo .b.c 696 | test.c: test.b 697 | test.a: 698 | test.b: 699 | ' 700 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null 701 | 702 | # Don't use the shell -e option when running commands. 703 | testing "No shell -e option when running commands" \ 704 | "make -f -" "OK\n" "" ' 705 | target: 706 | @false; echo OK 707 | ' 708 | 709 | # Macros and targets may be mixed on the command line 710 | testing "Allow mixed macros and targets" \ 711 | "make -f - FOO=foo foo BAR=bar bar" "foo\nbar\nfoo\nbar\n" "" ' 712 | foo: 713 | @echo $(FOO) 714 | @echo $(BAR) 715 | bar: 716 | @echo $(FOO) 717 | @echo $(BAR) 718 | ' 719 | 720 | # $* and $< are supported for target rules 721 | mkdir make.tempdir && cd make.tempdir || exit 1 722 | touch src.c src.h 723 | testing 'Support $* and $< for target rules' \ 724 | "make -f -" "src.c src.h\nsrc.o\nsrc\nsrc.c\n" "" ' 725 | src.o: src.c src.h 726 | @echo "$?" 727 | @echo "$@" 728 | @echo "$*" 729 | @echo "$<" 730 | ' 731 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null 732 | 733 | # ifeq/ifneq conditionals are supported 734 | testing 'Support ifeq and ifneq conditionals' \ 735 | "make -f -" "A OK\nB OK\n" "" ' 736 | A = a 737 | B = b 738 | target: 739 | ifeq ($(A),a) 740 | @echo A OK 741 | else 742 | @echo A not OK 743 | endif 744 | ifneq "a" "$B" 745 | @echo B OK 746 | endif 747 | ' 748 | 749 | # An empty original suffix indicates that every word should have 750 | # the new suffix added. If neither suffix is provided the words 751 | # remain unchanged. 752 | testing "Macro expansion and suffix substitution 3" \ 753 | "make -f -" "src1.c src2.c\nsrc1 src2\n" "" ' 754 | SRCS = src1 src2 755 | target: 756 | @echo $(SRCS:=.c) 757 | @echo $(SRCS:=) 758 | ' 759 | SKIP= 760 | 761 | # ================================================================= 762 | # The following tests require non-POSIX extensions and POSIX 2024 763 | # features to be enabled. 764 | # They're expected to fail in POSIX mode. 765 | # ================================================================= 766 | 767 | optional FEATURE_MAKE_EXTENSIONS 768 | test "$SKIP" = "" && optional FEATURE_MAKE_POSIX_2024 769 | # Skip duplicate entries in $? and $^ 770 | mkdir make.tempdir && cd make.tempdir || exit 1 771 | touch -t 202206171200 file1 file3 772 | touch -t 202206171201 target 773 | touch -t 202206171202 file2 774 | testing "Skip duplicate entries in \$? and \$^" \ 775 | "make -f -" \ 776 | "file2\nfile1 file2 file3\n" "" ' 777 | target: file1 file2 file2 file3 file3 778 | @echo $? 779 | @echo $^ 780 | ' 781 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null 782 | 783 | # Skip duplicate entries in $? and $^, with each double-colon rule 784 | # handled separately 785 | mkdir make.tempdir && cd make.tempdir || exit 1 786 | touch -t 202206171200 file1 file3 787 | touch -t 202206171201 target 788 | touch -t 202206171202 file2 789 | testing "Skip duplicate entries: double-colon rules" \ 790 | "make -f -" \ 791 | "file2\nfile1 file3 file2\nfile2\nfile2 file3\n" "" ' 792 | target:: file1 file3 file1 file2 file3 793 | @echo $? 794 | @echo $^ 795 | target:: file2 file3 file3 796 | @echo $? 797 | @echo $^ 798 | ' 799 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null 800 | 801 | # Skip duplicate entries in $? and $^, with each double-colon rule 802 | # handled separately. No prerequisites out-of-date in the first. 803 | mkdir make.tempdir && cd make.tempdir || exit 1 804 | touch -t 202206171200 file1 file3 805 | touch -t 202206171201 target 806 | touch -t 202206171202 file2 807 | testing "Skip duplicate entries: double-colon rules, only second invoked" \ 808 | "make -f -" \ 809 | "file2\nfile2 file3\n" "" ' 810 | target:: file1 file3 file1 file3 811 | @echo $? 812 | @echo $^ 813 | target:: file2 file3 file3 814 | @echo $? 815 | @echo $^ 816 | ' 817 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null 818 | 819 | # Double-colon rules didn't work properly if their target was phony: 820 | # - they didn't ignore the presence of a file matching the target name; 821 | # - they were also invoked as if they were a single-colon rule. 822 | mkdir make.tempdir && cd make.tempdir || exit 1 823 | touch -t 202206171200 file1 824 | touch -t 202206171201 target 825 | testing "Phony target of double-colon rule" \ 826 | "make -f - 2>&1" \ 827 | "unconditional\nconditional\n" "" ' 828 | .PHONY: target 829 | target:: 830 | @echo unconditional 831 | target:: file1 832 | @echo conditional 833 | file1: 834 | @touch file1 835 | ' 836 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null 837 | 838 | # GNU make and BSD make don't allow the use of inference rules 839 | # for phony targets. In POSIX mode the output is "phony.xyz\n". 840 | mkdir make.tempdir && cd make.tempdir || exit 1 841 | touch phony.xyz 842 | testing "Don't use inference rule for phony target" \ 843 | "make -f -" "make: nothing to be done for phony\n" "" ' 844 | .PHONY: phony 845 | .SUFFIXES: .xyz 846 | .xyz: 847 | @echo $< 848 | phony: 849 | ' 850 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null 851 | SKIP= 852 | 853 | exit $FAILCOUNT 854 | -------------------------------------------------------------------------------- /testsuite/runtest: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Usage: 3 | # runtest [-v] 4 | 5 | . ./testing.sh 6 | 7 | total_failed=0 8 | 9 | : "${tsdir:=$PWD}" 10 | : "${bindir:=${PWD%/*}}" # one directory up from $PWD 11 | 12 | if [ "${1-}" = "-v" ]; then 13 | VERBOSE=1 14 | shift 15 | fi 16 | 17 | export VERBOSE="${VERBOSE-}" 18 | 19 | # Test whether the binary has non-POSIX extensions enabled. 20 | # Specifically, test whether double-colon rules are supported. 21 | unset PDPMAKE_POSIXLY_CORRECT 22 | unset MAKEFLAGS 23 | export OPTIONFLAGS=":" 24 | if ../make -f - 2>/dev/null </dev/null <&2 63 | exit 1 64 | fi 65 | 66 | [ -z "$DEBUG" ] || set -x 67 | 68 | if [ -n "$SKIP" ] 69 | then 70 | echo "SKIPPED: $NAME" 71 | return 0 72 | fi 73 | 74 | printf '%b' "$3" > expected 75 | printf '%b' "$4" > input 76 | [ -z "$VERBOSE" ] || echo ====================== 77 | [ -z "$VERBOSE" ] || echo "printf '%b' '$4' >input" 78 | [ -z "$VERBOSE" ] || echo "printf '%b' '$5' | $2" 79 | printf '%b' "$5" | eval "$2" > actual 80 | RETVAL=$? 81 | 82 | if cmp expected actual >/dev/null 2>/dev/null 83 | then 84 | echo "PASS: $NAME" 85 | else 86 | FAILCOUNT=$(($FAILCOUNT + 1)) 87 | echo "FAIL: $NAME" 88 | [ -z "$VERBOSE" ] || diff -u expected actual 89 | fi 90 | rm -f input expected actual 91 | 92 | [ -z "$DEBUG" ] || set +x 93 | 94 | return $RETVAL 95 | } 96 | -------------------------------------------------------------------------------- /utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Utility functions. 3 | */ 4 | #include "make.h" 5 | 6 | /* 7 | * Print message, with makefile and line number if possible. 8 | */ 9 | static void 10 | vwarning(FILE *stream, const char *msg, va_list list) 11 | { 12 | if (makefile) 13 | fprintf(stream, "%s: (%s:%d): ", myname, makefile, dispno); 14 | else 15 | fprintf(stream, "%s: ", myname); 16 | vfprintf(stream, msg, list); 17 | fputc('\n', stream); 18 | } 19 | 20 | /* 21 | * Diagnostic handler. Print message to standard error. 22 | */ 23 | void 24 | diagnostic(const char *msg, ...) 25 | { 26 | va_list list; 27 | 28 | va_start(list, msg); 29 | vwarning(stderr, msg, list); 30 | va_end(list); 31 | } 32 | 33 | /* 34 | * Error handler. Print message and exit. 35 | */ 36 | void 37 | error(const char *msg, ...) 38 | { 39 | va_list list; 40 | 41 | va_start(list, msg); 42 | vwarning(stderr, msg, list); 43 | va_end(list); 44 | exit(2); 45 | } 46 | 47 | #if ENABLE_FEATURE_MAKE_EXTENSIONS 48 | void 49 | error_unexpected(const char *s) 50 | { 51 | error("unexpected %s", s); 52 | } 53 | #endif 54 | 55 | void 56 | error_in_inference_rule(const char *s) 57 | { 58 | error("%s in inference rule", s); 59 | } 60 | 61 | void 62 | error_not_allowed(const char *s, const char *t) 63 | { 64 | error("%s not allowed for %s", s, t); 65 | } 66 | 67 | void 68 | warning(const char *msg, ...) 69 | { 70 | va_list list; 71 | 72 | va_start(list, msg); 73 | vwarning(stdout, msg, list); 74 | va_end(list); 75 | } 76 | 77 | void * 78 | xmalloc(size_t len) 79 | { 80 | void *ret = malloc(len); 81 | if (ret == NULL) 82 | error("out of memory"); 83 | return ret; 84 | } 85 | 86 | void * 87 | xrealloc(void *ptr, size_t len) 88 | { 89 | void *ret = realloc(ptr, len); 90 | if (ret == NULL) 91 | error("out of memory"); 92 | return ret; 93 | } 94 | 95 | char * 96 | xconcat3(const char *s1, const char *s2, const char *s3) 97 | { 98 | const size_t len1 = strlen(s1); 99 | const size_t len2 = strlen(s2); 100 | const size_t len3 = strlen(s3); 101 | 102 | char *t = xmalloc(len1 + len2 + len3 + 1); 103 | char *s = t; 104 | 105 | s = (char *)memcpy(s, s1, len1) + len1; 106 | s = (char *)memcpy(s, s2, len2) + len2; 107 | s = (char *)memcpy(s, s3, len3) + len3; 108 | *s = '\0'; 109 | 110 | return t; 111 | } 112 | 113 | char * 114 | xstrdup(const char *s) 115 | { 116 | size_t len; 117 | char *t; 118 | 119 | if (s == NULL) 120 | return NULL; 121 | 122 | len = strlen(s) + 1; 123 | t = xmalloc(len); 124 | return memcpy(t, s, len); 125 | } 126 | 127 | char * 128 | xstrndup(const char *s, size_t n) 129 | { 130 | char *t = strndup(s, n); 131 | if (t == NULL) 132 | error("out of memory"); 133 | return t; 134 | } 135 | 136 | /* 137 | * Append a word to a space-separated string of words. The first 138 | * call should use a NULL pointer for str, subsequent calls should 139 | * pass an allocated string which will be freed. 140 | */ 141 | char * 142 | xappendword(const char *str, const char *word) 143 | { 144 | char *newstr = str ? xconcat3(str, " ", word) : xstrdup(word); 145 | free((void *)str); 146 | return newstr; 147 | } 148 | 149 | unsigned int 150 | getbucket(const char *name) 151 | { 152 | unsigned int hashval = 0; 153 | const unsigned char *p = (unsigned char *)name; 154 | 155 | while (*p) 156 | hashval ^= (hashval << 5) + (hashval >> 2) + *p++; 157 | return hashval % HTABSIZE; 158 | } 159 | 160 | /* 161 | * Add a file to the end of the supplied list of files. 162 | * Return the new head pointer for that list. 163 | */ 164 | struct file * 165 | newfile(char *str, struct file *fphead) 166 | { 167 | struct file *fpnew; 168 | struct file *fp; 169 | 170 | fpnew = xmalloc(sizeof(struct file)); 171 | fpnew->f_next = NULL; 172 | fpnew->f_name = xstrdup(str); 173 | 174 | if (fphead == NULL) 175 | return fpnew; 176 | 177 | for (fp = fphead; fp->f_next; fp = fp->f_next) 178 | ; 179 | 180 | fp->f_next = fpnew; 181 | 182 | return fphead; 183 | } 184 | 185 | #if ENABLE_FEATURE_CLEAN_UP 186 | void 187 | freefiles(struct file *fp) 188 | { 189 | struct file *nextfp; 190 | 191 | for (; fp; fp = nextfp) { 192 | nextfp = fp->f_next; 193 | free(fp->f_name); 194 | free(fp); 195 | } 196 | } 197 | #endif 198 | --------------------------------------------------------------------------------