mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-03-09 15:50:02 +00:00
EPL 1.0 says, in section 7: "The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version." The Eclipse Foundation also encourage everyone to upgrade: https://www.eclipse.org/legal/epl-2.0/faq.php#h.60mjudroo8e5 https://www.eclipse.org/legal/epl-2.0/faq.php#h.tci84nlsqpgw Unfortunately the new Secondary License option is not available to us as we're not the original copyright holders and don't have the legal power to add one. So, no GPL compatibility. Sorry.
2463 lines
48 KiB
C
2463 lines
48 KiB
C
/***********************************************************************
|
|
* *
|
|
* This software is part of the ast package *
|
|
* Copyright (c) 1990-2013 AT&T Intellectual Property *
|
|
* Copyright (c) 2020-2022 Contributors to ksh 93u+m *
|
|
* and is licensed under the *
|
|
* Eclipse Public License, Version 2.0 *
|
|
* *
|
|
* A copy of the License is available at *
|
|
* https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html *
|
|
* (with md5 checksum 84283fa8859daf213bdda5a9f8d1be1d) *
|
|
* *
|
|
* Glenn Fowler <gsf@research.att.com> *
|
|
* *
|
|
***********************************************************************/
|
|
#pragma clang diagnostic ignored "-Wdeprecated-register"
|
|
#pragma clang diagnostic ignored "-Wparentheses"
|
|
|
|
/*
|
|
* mamake -- MAM make
|
|
*
|
|
* coded for portability
|
|
*/
|
|
|
|
#define RELEASE_DATE "2022-07-15"
|
|
static char id[] = "\n@(#)$Id: mamake (ksh 93u+m) " RELEASE_DATE " $\0\n";
|
|
|
|
#if _PACKAGE_ast
|
|
|
|
#include <ast.h>
|
|
#include <error.h>
|
|
|
|
static const char usage[] =
|
|
"[-?\n@(#)$Id: mamake (ksh 93u+m) " RELEASE_DATE " $\n]"
|
|
"[-author?Glenn Fowler <gsf@research.att.com>]"
|
|
"[-author?Contributors to https://github.com/ksh93/ksh]"
|
|
"[-copyright?(c) 1994-2012 AT&T Intellectual Property]"
|
|
"[-copyright?(c) 2020-2022 Contributors to ksh 93u+m]"
|
|
"[-license?https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html]"
|
|
"[+NAME?mamake - make abstract machine make]"
|
|
"[+DESCRIPTION?\bmamake\b reads \amake abstract machine\a target and"
|
|
" prerequisite file descriptions from a mamfile (see \b-f\b) and executes"
|
|
" actions to update targets that are older than their prerequisites."
|
|
" Mamfiles are portable to environments that only have"
|
|
" \bsh\b(1) and \bcc\b(1).]"
|
|
"[+?Mamfiles are used rather than"
|
|
" old-\bmake\b makefiles because some features are not reliably supported"
|
|
" across all \bmake\b variants:]{"
|
|
" [+action execution?Multi-line actions are executed as a"
|
|
" unit by \b$SHELL\b. There are some shell constructs"
|
|
" that cannot be expressed in an old-\bmake\b makefile.]"
|
|
" [+viewpathing?\bVPATH\b is properly interpreted. This allows"
|
|
" source to be separate from generated files.]"
|
|
" [+recursion?Ordered subdirectory recursion over unrelated"
|
|
" makefiles.]"
|
|
" }"
|
|
"[+?\bmamprobe\b(1) is called to probe and generate system specific variable"
|
|
" definitions. The probe information is regenerated when it is older"
|
|
" than the \bmamprobe\b command.]"
|
|
"[+?For compatibility with \bnmake\b(1) the \b-K\b option and the"
|
|
" \brecurse\b and \bcc-*\b command line targets are ignored.]"
|
|
"[e:?Explain reason for triggering action. Ignored if -F is on.]"
|
|
"[f:?Read \afile\a instead of the default.]:[file:=Mamfile]"
|
|
"[i:?Ignore action errors.]"
|
|
"[k:?Continue after error with sibling prerequisites.]"
|
|
"[n:?Print actions but do not execute. Recursion actions (see \b-r\b) are still"
|
|
" executed. Use \b-N\b to disable recursion actions too.]"
|
|
"[r:?Recursively make leaf directories matching \apattern\a. Only leaf"
|
|
" directories containing a file named \bMamfile\b"
|
|
" are considered.]:[pattern]"
|
|
"[C:?Do all work in \adirectory\a. All messages will mention"
|
|
" \adirectory\a.]:[directory]"
|
|
"[D:?Set the debug trace level to \alevel\a. Higher levels produce more"
|
|
" output.]#[level]"
|
|
"[F:?Force all targets to be out of date.]"
|
|
"[K:?Ignored.]"
|
|
"[N:?Like \b-n\b but recursion actions (see \b-r\b) are also disabled.]"
|
|
"[V:?Print the program version and exit.]"
|
|
"[G:debug-symbols?Compile and link with debugging symbol options enabled.]"
|
|
"[S:strip-symbols?Strip link-time static symbols from executables.]"
|
|
|
|
"\n"
|
|
"\n[ target ... ] [ name=value ... ]\n"
|
|
"\n"
|
|
|
|
"[+SEE ALSO?\bgmake\b(1), \bmake\b(1), \bmamprobe\b(1),"
|
|
" \bnmake\b(1), \bsh\b(1)]"
|
|
;
|
|
|
|
#else
|
|
|
|
#define elementsof(x) (sizeof(x)/sizeof(x[0]))
|
|
#define newof(p,t,n,x) ((p)?(t*)realloc((char*)(p),sizeof(t)*(n)+(x)):(t*)calloc(1,sizeof(t)*(n)+(x)))
|
|
|
|
#define NiL 0
|
|
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <ctype.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <time.h>
|
|
|
|
#if !_PACKAGE_ast
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#endif
|
|
|
|
#define delimiter(c) ((c)==' '||(c)=='\t'||(c)=='\n'||(c)==';'||(c)=='('||(c)==')'||(c)=='`'||(c)=='|'||(c)=='&'||(c)=='=')
|
|
|
|
#define add(b,c) (((b)->nxt >= (b)->end) ? append(b, "") : NiL, *(b)->nxt++ = (c))
|
|
#define get(b) ((b)->nxt-(b)->buf)
|
|
#define set(b,o) ((b)->nxt=(b)->buf+(o))
|
|
#define use(b) (*(b)->nxt=0,(b)->nxt=(b)->buf)
|
|
|
|
#define CHUNK 4096
|
|
#define KEY(a,b,c,d) ((((unsigned long)(a))<<15)|(((unsigned long)(b))<<10)|(((unsigned long)(c))<<5)|(((unsigned long)(d))))
|
|
#define NOW ((unsigned long)time((time_t*)0))
|
|
#define ROTATE(p,l,r,t) ((t)=(p)->l,(p)->l=(t)->r,(t)->r=(p),(p)=(t))
|
|
|
|
#define RULE_active 0x0001 /* active target */
|
|
#define RULE_dontcare 0x0002 /* ok if not found */
|
|
#define RULE_error 0x0004 /* not found or not generated */
|
|
#define RULE_exists 0x0008 /* target file exists */
|
|
#define RULE_generated 0x0010 /* generated target */
|
|
#define RULE_ignore 0x0020 /* ignore time */
|
|
#define RULE_implicit 0x0040 /* implicit prerequisite */
|
|
#define RULE_made 0x0080 /* already made */
|
|
#define RULE_virtual 0x0100 /* not a file */
|
|
|
|
#define STREAM_KEEP 0x0001 /* don't fclose() on pop() */
|
|
#define STREAM_MUST 0x0002 /* push() file must exist */
|
|
#define STREAM_PIPE 0x0004 /* pclose() on pop() */
|
|
|
|
#ifndef S_IXUSR
|
|
#define S_IXUSR 0100 /* owner execute permission */
|
|
#endif
|
|
#ifndef S_IXGRP
|
|
#define S_IXGRP 0010 /* group execute permission */
|
|
#endif
|
|
#ifndef S_IXOTH
|
|
#define S_IXOTH 0001 /* other execute permission */
|
|
#endif
|
|
|
|
struct Rule_s;
|
|
|
|
typedef struct stat Stat_t;
|
|
typedef FILE Stdio_t;
|
|
|
|
typedef struct Buf_s /* buffer stream */
|
|
{
|
|
struct Buf_s* old; /* next dropped buffer */
|
|
char* end; /* 1 past end of buffer */
|
|
char* nxt; /* next char to add */
|
|
char* buf; /* buffer space */
|
|
} Buf_t;
|
|
|
|
typedef struct Dict_item_s /* dictionary item */
|
|
{
|
|
struct Dict_item_s* left; /* left child */
|
|
struct Dict_item_s* right; /* right child */
|
|
void* value; /* user defined value */
|
|
char name[1];/* 0 terminated name */
|
|
} Dict_item_t;
|
|
|
|
typedef struct Dict_s /* dictionary handle */
|
|
{
|
|
Dict_item_t* root; /* root item */
|
|
} Dict_t;
|
|
|
|
typedef struct List_s /* Rule_t list */
|
|
{
|
|
struct List_s* next; /* next in list */
|
|
struct Rule_s* rule; /* list item */
|
|
} List_t;
|
|
|
|
typedef struct Rule_s /* rule item */
|
|
{
|
|
char* name; /* unbound name */
|
|
char* path; /* bound path */
|
|
List_t* prereqs; /* prerequisites */
|
|
struct Rule_s* leaf; /* recursion leaf alias */
|
|
int flags; /* RULE_* flags */
|
|
int making; /* currently make()ing */
|
|
unsigned long time; /* modification time */
|
|
} Rule_t;
|
|
|
|
typedef struct Stream_s /* input file stream stack */
|
|
{
|
|
Stdio_t* fp; /* read stream */
|
|
char* file; /* stream path */
|
|
unsigned long line; /* stream line */
|
|
int flags; /* stream flags */
|
|
} Stream_t;
|
|
|
|
typedef struct View_s /* viewpath level */
|
|
{
|
|
struct View_s* next; /* next level in viewpath */
|
|
int node; /* viewpath node path length */
|
|
char dir[1]; /* viewpath level dir prefix */
|
|
} View_t;
|
|
|
|
static struct /* program state */
|
|
{
|
|
Buf_t* buf; /* work buffer */
|
|
Buf_t* old; /* dropped buffers */
|
|
Buf_t* opt; /* option buffer */
|
|
|
|
Dict_t* leaf; /* recursion leaf dictionary */
|
|
Dict_t* libs; /* library dictionary */
|
|
Dict_t* rules; /* rule dictionary */
|
|
Dict_t* vars; /* variable dictionary */
|
|
|
|
View_t* view; /* viewpath levels */
|
|
|
|
char* directory; /* work in this directory */
|
|
char* id; /* command name */
|
|
char* file; /* first input file */
|
|
char* pwd; /* current directory */
|
|
char* recurse; /* recursion pattern */
|
|
char* shell; /* ${SHELL} */
|
|
|
|
int active; /* targets currently active */
|
|
int debug; /* negative of debug level */
|
|
int errors; /* some error(s) occurred */
|
|
int exec; /* execute actions */
|
|
int explain; /* explain actions */
|
|
int force; /* all targets out of date */
|
|
int ignore; /* ignore command errors */
|
|
int indent; /* debug indent */
|
|
int keepgoing; /* do siblings on error */
|
|
int never; /* never execute */
|
|
int peek; /* next line already in input */
|
|
int probed; /* probe already done */
|
|
int verified; /* don't bother with verify() */
|
|
|
|
Stream_t streams[4]; /* input file stream stack */
|
|
Stream_t* sp; /* input stream stack pointer */
|
|
|
|
char input[8*CHUNK]; /* input buffer */
|
|
} state;
|
|
|
|
static unsigned long make(Rule_t*);
|
|
|
|
static char mamfile[] = "Mamfile";
|
|
static char sh[] = "/bin/sh";
|
|
|
|
extern char** environ;
|
|
|
|
#if !_PACKAGE_ast
|
|
|
|
#if defined(NeXT) || defined(__NeXT)
|
|
#define getcwd(a,b) getwd(a)
|
|
#endif
|
|
|
|
/*
|
|
* emit usage message and exit
|
|
*/
|
|
|
|
static void
|
|
usage()
|
|
{
|
|
fprintf(stderr, "Usage: %s"
|
|
" [-iknFKNV]"
|
|
" [-f Mamfile]"
|
|
" [-r pattern]"
|
|
" [-C directory]"
|
|
" [-D level]"
|
|
" [target ...]"
|
|
" [name=value ...]"
|
|
"\n", state.id);
|
|
exit(2);
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
* output error message identification
|
|
*/
|
|
|
|
static void
|
|
identify(Stdio_t* sp)
|
|
{
|
|
if (state.directory)
|
|
fprintf(sp, "%s [%s]: ", state.id, state.directory);
|
|
else
|
|
fprintf(sp, "%s: ", state.id);
|
|
}
|
|
|
|
/*
|
|
* emit error message
|
|
* level:
|
|
* <0 debug
|
|
* 0 info
|
|
* 1 warning
|
|
* 2 error
|
|
* >2 exit(level-2)
|
|
*/
|
|
|
|
static void
|
|
report(int level, char* text, char* item, unsigned long stamp)
|
|
{
|
|
int i;
|
|
|
|
if (level >= state.debug)
|
|
{
|
|
if (level)
|
|
identify(stderr);
|
|
if (level < 0)
|
|
{
|
|
fprintf(stderr, "debug%d: ", level);
|
|
for (i = 1; i < state.indent; i++)
|
|
fprintf(stderr, " ");
|
|
}
|
|
else
|
|
{
|
|
if (state.sp && state.sp->line)
|
|
{
|
|
if (state.sp->file)
|
|
fprintf(stderr, "%s: ", state.sp->file);
|
|
fprintf(stderr, "%ld: ", state.sp->line);
|
|
}
|
|
if (level == 1)
|
|
fprintf(stderr, "warning: ");
|
|
else if (level > 1)
|
|
state.errors = 1;
|
|
}
|
|
if (item)
|
|
fprintf(stderr, "%s: ", item);
|
|
fprintf(stderr, "%s", text);
|
|
if (stamp && state.debug <= -2)
|
|
fprintf(stderr, " %10lu", stamp);
|
|
fprintf(stderr, "\n");
|
|
if (level > 2)
|
|
exit(level - 2);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* don't know how to make or exit code making
|
|
*/
|
|
|
|
static void
|
|
dont(Rule_t* r, int code, int keepgoing)
|
|
{
|
|
identify(stderr);
|
|
if (!code)
|
|
fprintf(stderr, "don't know how to make %s\n", r->name);
|
|
else
|
|
{
|
|
fprintf(stderr, "*** exit code %d making %s%s\n", code, r->name, state.ignore ? " ignored" : "");
|
|
unlink(r->name);
|
|
if (state.ignore)
|
|
return;
|
|
}
|
|
if (!keepgoing)
|
|
exit(1);
|
|
state.errors++;
|
|
r->flags |= RULE_error;
|
|
}
|
|
|
|
/*
|
|
* local strrchr()
|
|
*/
|
|
|
|
static char*
|
|
last(register char* s, register int c)
|
|
{
|
|
register char* r = 0;
|
|
|
|
for (r = 0; *s; s++)
|
|
if (*s == c)
|
|
r = s;
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* open a buffer stream
|
|
*/
|
|
|
|
static Buf_t*
|
|
buffer(void)
|
|
{
|
|
register Buf_t* buf;
|
|
|
|
if (buf = state.old)
|
|
state.old = state.old->old;
|
|
else if (!(buf = newof(0, Buf_t, 1, 0)) || !(buf->buf = newof(0, char, CHUNK, 0)))
|
|
report(3, "out of memory [buffer]", NiL, (unsigned long)0);
|
|
buf->end = buf->buf + CHUNK;
|
|
buf->nxt = buf->buf;
|
|
return buf;
|
|
}
|
|
|
|
/*
|
|
* close a buffer stream
|
|
*/
|
|
|
|
static void
|
|
drop(Buf_t* buf)
|
|
{
|
|
buf->old = state.old;
|
|
state.old = buf;
|
|
}
|
|
|
|
/*
|
|
* append str length n to buffer and return the buffer base
|
|
*/
|
|
|
|
static char*
|
|
appendn(Buf_t* buf, char* str, int n)
|
|
{
|
|
int m;
|
|
int i;
|
|
|
|
if ((n + 1) >= (buf->end - buf->nxt))
|
|
{
|
|
i = buf->nxt - buf->buf;
|
|
m = (((buf->end - buf->buf) + n + CHUNK + 1) / CHUNK) * CHUNK;
|
|
if (!(buf->buf = newof(buf->buf, char, m, 0)))
|
|
report(3, "out of memory [buffer resize]", NiL, (unsigned long)0);
|
|
buf->end = buf->buf + m;
|
|
buf->nxt = buf->buf + i;
|
|
}
|
|
memcpy(buf->nxt, str, n + 1);
|
|
buf->nxt += n;
|
|
return buf->buf;
|
|
}
|
|
|
|
/*
|
|
* append str to buffer and return the buffer base
|
|
* if str==0 then next pointer reset to base
|
|
*/
|
|
|
|
static char*
|
|
append(Buf_t* buf, char* str)
|
|
{
|
|
if (str)
|
|
return appendn(buf, str, strlen(str));
|
|
buf->nxt = buf->buf;
|
|
return buf->buf;
|
|
}
|
|
|
|
/*
|
|
* allocate space for s and return the copy
|
|
*/
|
|
|
|
static char*
|
|
duplicate(char* s)
|
|
{
|
|
char* t;
|
|
int n;
|
|
|
|
n = strlen(s);
|
|
if (!(t = newof(0, char, n, 1)))
|
|
report(3, "out of memory [duplicate]", s, (unsigned long)0);
|
|
strcpy(t, s);
|
|
return t;
|
|
}
|
|
|
|
/*
|
|
* open a new dictionary
|
|
*/
|
|
|
|
static Dict_t*
|
|
dictionary(void)
|
|
{
|
|
Dict_t* dict;
|
|
|
|
if (!(dict = newof(0, Dict_t, 1, 0)))
|
|
report(3, "out of memory [dictionary]", NiL, (unsigned long)0);
|
|
return dict;
|
|
}
|
|
|
|
/*
|
|
* return the value for item name in dictionary dict
|
|
* if value!=0 then name entry value is created if necessary and set
|
|
* uses top-down splaying (ala Tarjan and Sleator)
|
|
*/
|
|
|
|
static void*
|
|
search(register Dict_t* dict, char* name, void* value)
|
|
{
|
|
register int cmp;
|
|
register Dict_item_t* root;
|
|
register Dict_item_t* t;
|
|
register Dict_item_t* left;
|
|
register Dict_item_t* right;
|
|
register Dict_item_t* lroot;
|
|
register Dict_item_t* rroot;
|
|
|
|
root = dict->root;
|
|
left = right = lroot = rroot = 0;
|
|
while (root)
|
|
{
|
|
if (!(cmp = strcmp(name, root->name)))
|
|
break;
|
|
else if (cmp < 0)
|
|
{
|
|
if (root->left && (cmp = strcmp(name, root->left->name)) <= 0)
|
|
{
|
|
ROTATE(root, left, right, t);
|
|
if (!cmp)
|
|
break;
|
|
}
|
|
if (right)
|
|
right->left = root;
|
|
else
|
|
rroot = root;
|
|
right = root;
|
|
root = root->left;
|
|
right->left = 0;
|
|
}
|
|
else
|
|
{
|
|
if (root->right && (cmp = strcmp(name, root->right->name)) >= 0)
|
|
{
|
|
ROTATE(root, right, left, t);
|
|
if (!cmp)
|
|
break;
|
|
}
|
|
if (left)
|
|
left->right = root;
|
|
else
|
|
lroot = root;
|
|
left = root;
|
|
root = root->right;
|
|
left->right = 0;
|
|
}
|
|
}
|
|
if (root)
|
|
{
|
|
if (right)
|
|
right->left = root->right;
|
|
else
|
|
rroot = root->right;
|
|
if (left)
|
|
left->right = root->left;
|
|
else
|
|
lroot = root->left;
|
|
}
|
|
else if (value)
|
|
{
|
|
if (!(root = newof(0, Dict_item_t, 1, strlen(name))))
|
|
report(3, "out of memory [dictionary]", name, (unsigned long)0);
|
|
strcpy(root->name, name);
|
|
}
|
|
if (root)
|
|
{
|
|
if (value)
|
|
root->value = value;
|
|
root->left = lroot;
|
|
root->right = rroot;
|
|
dict->root = root;
|
|
return value ? (void*)root->name : root->value;
|
|
}
|
|
if (left)
|
|
{
|
|
left->right = rroot;
|
|
dict->root = lroot;
|
|
}
|
|
else if (right)
|
|
{
|
|
right->left = lroot;
|
|
dict->root = rroot;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* low level for walk()
|
|
*/
|
|
|
|
static int
|
|
apply(Dict_t* dict, Dict_item_t* item, int (*func)(Dict_item_t*, void*), void* handle)
|
|
{
|
|
register Dict_item_t* right;
|
|
|
|
do
|
|
{
|
|
right = item->right;
|
|
if (item->left && apply(dict, item->left, func, handle))
|
|
return -1;
|
|
if ((*func)(item, handle))
|
|
return -1;
|
|
} while (item = right);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* apply func to each dictionary item
|
|
*/
|
|
|
|
static int
|
|
walk(Dict_t* dict, int (*func)(Dict_item_t*, void*), void* handle)
|
|
{
|
|
return dict->root ? apply(dict, dict->root, func, handle) : 0;
|
|
}
|
|
|
|
/*
|
|
* return a rule pointer for name
|
|
*/
|
|
|
|
static Rule_t*
|
|
rule(char* name)
|
|
{
|
|
Rule_t* r;
|
|
|
|
if (!(r = (Rule_t*)search(state.rules, name, NiL)))
|
|
{
|
|
if (!(r = newof(0, Rule_t, 1, 0)))
|
|
report(3, "out of memory [rule]", name, (unsigned long)0);
|
|
r->name = (char*)search(state.rules, name, (void*)r);
|
|
}
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* prepend p onto rule r prereqs
|
|
*/
|
|
|
|
static void
|
|
cons(Rule_t* r, Rule_t* p)
|
|
{
|
|
register List_t* x;
|
|
|
|
for (x = r->prereqs; x && x->rule != p; x = x->next);
|
|
if (!x)
|
|
{
|
|
if (!(x = newof(0, List_t, 1, 0)))
|
|
report(3, "out of memory [list]", r->name, (unsigned long)0);
|
|
x->rule = p;
|
|
x->next = r->prereqs;
|
|
r->prereqs = x;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* initialize the viewpath
|
|
*/
|
|
|
|
static void
|
|
view(void)
|
|
{
|
|
register char* s;
|
|
register char* t;
|
|
register char* p;
|
|
register View_t* vp;
|
|
|
|
View_t* zp;
|
|
int c;
|
|
int n;
|
|
|
|
Stat_t st;
|
|
Stat_t ts;
|
|
|
|
char buf[CHUNK];
|
|
|
|
if (stat(".", &st))
|
|
report(3, "cannot stat", ".", (unsigned long)0);
|
|
if ((s = (char*)search(state.vars, "PWD", NiL)) && !stat(s, &ts) &&
|
|
ts.st_dev == st.st_dev && ts.st_ino == st.st_ino)
|
|
state.pwd = s;
|
|
if (!state.pwd)
|
|
{
|
|
if (!getcwd(buf, sizeof(buf) - 1))
|
|
report(3, "cannot determine PWD", NiL, (unsigned long)0);
|
|
state.pwd = duplicate(buf);
|
|
search(state.vars, "PWD", state.pwd);
|
|
}
|
|
if ((s = (char*)search(state.vars, "VPATH", NiL)) && *s)
|
|
{
|
|
zp = 0;
|
|
for (;;)
|
|
{
|
|
for (t = s; *t && *t != ':'; t++);
|
|
if (c = *t)
|
|
*t = 0;
|
|
if (!state.view)
|
|
{
|
|
/*
|
|
* determine the viewpath offset
|
|
*/
|
|
|
|
if (stat(s, &st))
|
|
report(3, "cannot stat top view", s, (unsigned long)0);
|
|
if (stat(state.pwd, &ts))
|
|
report(3, "cannot stat", state.pwd, (unsigned long)0);
|
|
if (ts.st_dev == st.st_dev && ts.st_ino == st.st_ino)
|
|
p = ".";
|
|
else
|
|
{
|
|
p = state.pwd + strlen(state.pwd);
|
|
while (p > state.pwd)
|
|
{
|
|
if (*--p == '/')
|
|
{
|
|
if (p == state.pwd)
|
|
report(3, ". not under VPATH", s, (unsigned long)0);
|
|
*p = 0;
|
|
if (stat(state.pwd, &ts))
|
|
report(3, "cannot stat", state.pwd, (unsigned long)0);
|
|
*p = '/';
|
|
if (ts.st_dev == st.st_dev && ts.st_ino == st.st_ino)
|
|
{
|
|
p++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (p <= state.pwd)
|
|
report(3, "cannot determine viewpath offset", s, (unsigned long)0);
|
|
}
|
|
}
|
|
n = strlen(s);
|
|
if (!(vp = newof(0, View_t, 1, strlen(p) + n + 1)))
|
|
report(3, "out of memory [view]", s, (unsigned long)0);
|
|
vp->node = n + 1;
|
|
strcpy(vp->dir, s);
|
|
*(vp->dir + n) = '/';
|
|
strcpy(vp->dir + n + 1, p);
|
|
report(-4, vp->dir, "view", (unsigned long)0);
|
|
if (!state.view)
|
|
state.view = zp = vp;
|
|
else
|
|
zp = zp->next = vp;
|
|
if (!c)
|
|
break;
|
|
*t++ = c;
|
|
s = t;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* return next '?' or '}' in nested '}'
|
|
*/
|
|
|
|
static char*
|
|
cond(register char* s)
|
|
{
|
|
register int n;
|
|
|
|
if (*s == '?')
|
|
s++;
|
|
n = 0;
|
|
for (;;)
|
|
{
|
|
switch (*s++)
|
|
{
|
|
case 0:
|
|
break;
|
|
case '{':
|
|
n++;
|
|
continue;
|
|
case '}':
|
|
if (!n--)
|
|
break;
|
|
continue;
|
|
case '?':
|
|
if (!n)
|
|
break;
|
|
continue;
|
|
default:
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
return s - 1;
|
|
}
|
|
|
|
/*
|
|
* expand var refs from s into buf
|
|
*/
|
|
|
|
static void
|
|
substitute(Buf_t* buf, register char* s)
|
|
{
|
|
register char* t;
|
|
register char* v;
|
|
register char* q;
|
|
register char* b;
|
|
register int c;
|
|
register int n;
|
|
int a = 0;
|
|
int i;
|
|
|
|
while (c = *s++)
|
|
{
|
|
if (c == '$' && *s == '{')
|
|
{
|
|
b = s - 1;
|
|
i = 1;
|
|
for (n = *(t = ++s) == '-' ? 0 : '-'; (c = *s) && c != '?' && c != '+' && c != n && c != ':' && c != '=' && c != '[' && c != '}'; s++)
|
|
if (!isalnum(c) && c != '_')
|
|
i = 0;
|
|
*s = 0;
|
|
if (c == '[')
|
|
{
|
|
append(buf, b);
|
|
*s = c;
|
|
continue;
|
|
}
|
|
v = (char*)search(state.vars, t, NiL);
|
|
if ((c == ':' || c == '=') && (!v || c == ':' && !*v))
|
|
{
|
|
append(buf, b);
|
|
*s = c;
|
|
continue;
|
|
}
|
|
if (t[0] == 'A' && t[1] == 'R' && t[2] == 0)
|
|
a = 1;
|
|
*s = c;
|
|
if (c && c != '}')
|
|
{
|
|
n = 1;
|
|
for (t = ++s; *s; s++)
|
|
if (*s == '{')
|
|
n++;
|
|
else if (*s == '}' && !--n)
|
|
break;
|
|
}
|
|
switch (c)
|
|
{
|
|
case '?':
|
|
q = cond(t - 1);
|
|
if (v)
|
|
{
|
|
if (((q - t) != 1 || *t != '*') && strncmp(v, t, q - t))
|
|
v = 0;
|
|
}
|
|
else if (q == t)
|
|
v = s;
|
|
t = cond(q);
|
|
if (v)
|
|
{
|
|
if (t > q)
|
|
{
|
|
c = *t;
|
|
*t = 0;
|
|
substitute(buf, q + 1);
|
|
*t = c;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
q = cond(t);
|
|
if (q > t)
|
|
{
|
|
c = *q;
|
|
*q = 0;
|
|
substitute(buf, t + 1);
|
|
*q = c;
|
|
}
|
|
}
|
|
break;
|
|
case '+':
|
|
case '-':
|
|
if ((v == 0 || *v == 0) == (c == '-'))
|
|
{
|
|
c = *s;
|
|
*s = 0;
|
|
substitute(buf, t);
|
|
*s = c;
|
|
break;
|
|
}
|
|
if (c != '-')
|
|
break;
|
|
/* FALLTHROUGH */
|
|
case 0:
|
|
case '=':
|
|
case '}':
|
|
if (v)
|
|
{
|
|
if (a && t[0] == 'm' && t[1] == 'a' && t[2] == 'm' && t[3] == '_' && t[4] == 'l' && t[5] == 'i' && t[6] == 'b')
|
|
{
|
|
for (t = v; isspace(*t); t++);
|
|
for (; *t && !isspace(*t); t++);
|
|
if (*t)
|
|
*t = 0;
|
|
else
|
|
t = 0;
|
|
substitute(buf, v);
|
|
if (t)
|
|
*t = ' ';
|
|
}
|
|
else
|
|
substitute(buf, v);
|
|
}
|
|
else if (i)
|
|
{
|
|
c = *s;
|
|
*s = 0;
|
|
append(buf, b);
|
|
*s = c;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
if (*s)
|
|
s++;
|
|
}
|
|
else
|
|
add(buf, c);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* expand var refs from s into buf and return buf base
|
|
*/
|
|
|
|
static char*
|
|
expand(Buf_t* buf, char* s)
|
|
{
|
|
substitute(buf, s);
|
|
return use(buf);
|
|
}
|
|
|
|
/*
|
|
* stat() with .exe check
|
|
*/
|
|
|
|
static char*
|
|
status(Buf_t* buf, int off, char* path, struct stat* st)
|
|
{
|
|
int r;
|
|
char* s;
|
|
Buf_t* tmp;
|
|
|
|
if (!stat(path, st))
|
|
return path;
|
|
if (!(tmp = buf))
|
|
{
|
|
tmp = buffer();
|
|
off = 0;
|
|
}
|
|
if (off)
|
|
set(tmp, off);
|
|
else
|
|
append(tmp, path);
|
|
append(tmp, ".exe");
|
|
s = use(tmp);
|
|
r = stat(s, st);
|
|
if (!buf)
|
|
{
|
|
drop(tmp);
|
|
s = path;
|
|
}
|
|
if (r)
|
|
{
|
|
if (off)
|
|
s[off] = 0;
|
|
s = 0;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
/*
|
|
* return path to file
|
|
*/
|
|
|
|
static char*
|
|
find(Buf_t* buf, char* file, struct stat* st)
|
|
{
|
|
char* s;
|
|
View_t* vp;
|
|
int node;
|
|
int c;
|
|
int o;
|
|
|
|
if (s = status(buf, 0, file, st))
|
|
{
|
|
report(-3, s, "find", (unsigned long)0);
|
|
return s;
|
|
}
|
|
if (vp = state.view)
|
|
{
|
|
node = 0;
|
|
if (*file == '/')
|
|
{
|
|
do
|
|
{
|
|
if (!strncmp(file, vp->dir, vp->node))
|
|
{
|
|
file += vp->node;
|
|
node = 2;
|
|
break;
|
|
}
|
|
} while (vp = vp->next);
|
|
}
|
|
else
|
|
vp = vp->next;
|
|
if (vp)
|
|
{
|
|
do
|
|
{
|
|
if (node)
|
|
{
|
|
c = vp->dir[vp->node];
|
|
vp->dir[vp->node] = 0;
|
|
append(buf, vp->dir);
|
|
vp->dir[vp->node] = c;
|
|
}
|
|
else
|
|
{
|
|
append(buf, vp->dir);
|
|
append(buf, "/");
|
|
}
|
|
append(buf, file);
|
|
o = get(buf);
|
|
s = use(buf);
|
|
if (s = status(buf, o, s, st))
|
|
{
|
|
report(-3, s, "find", (unsigned long)0);
|
|
return s;
|
|
}
|
|
} while (vp = vp->next);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* bind r to a file and return the modify time
|
|
*/
|
|
|
|
static unsigned long
|
|
bindfile(Rule_t* r)
|
|
{
|
|
char* s;
|
|
Buf_t* buf;
|
|
struct stat st;
|
|
|
|
buf = buffer();
|
|
if (s = find(buf, r->name, &st))
|
|
{
|
|
if (s != r->name)
|
|
r->path = duplicate(s);
|
|
r->time = st.st_mtime;
|
|
r->flags |= RULE_exists;
|
|
}
|
|
drop(buf);
|
|
return r->time;
|
|
}
|
|
|
|
/*
|
|
* pop the current input file
|
|
*/
|
|
|
|
static int
|
|
pop(void)
|
|
{
|
|
int r;
|
|
|
|
if (!state.sp)
|
|
report(3, "input stack underflow", NiL, (unsigned long)0);
|
|
if (!state.sp->fp || (state.sp->flags & STREAM_KEEP))
|
|
r = 0;
|
|
else if (state.sp->flags & STREAM_PIPE)
|
|
r = pclose(state.sp->fp);
|
|
else
|
|
r = fclose(state.sp->fp);
|
|
if (state.sp == state.streams)
|
|
state.sp = 0;
|
|
else
|
|
state.sp--;
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* push file onto the input stack
|
|
*/
|
|
|
|
static int
|
|
push(char* file, Stdio_t* fp, int flags)
|
|
{
|
|
char* path;
|
|
Buf_t* buf;
|
|
struct stat st;
|
|
|
|
if (!state.sp)
|
|
state.sp = state.streams;
|
|
else if (++state.sp >= &state.streams[elementsof(state.streams)])
|
|
report(3, "input stream stack overflow", NiL, (unsigned long)0);
|
|
if (state.sp->fp = fp)
|
|
{
|
|
if(state.sp->file)
|
|
free(state.sp->file);
|
|
state.sp->file = strdup("pipeline");
|
|
if(!state.sp->file)
|
|
report(3, "out of memory [push]", NiL, (unsigned long)0);
|
|
}
|
|
else if (flags & STREAM_PIPE)
|
|
report(3, "pipe error", file, (unsigned long)0);
|
|
else if (!file || !strcmp(file, "-") || !strcmp(file, "/dev/stdin"))
|
|
{
|
|
flags |= STREAM_KEEP;
|
|
if(state.sp->file)
|
|
free(state.sp->file);
|
|
state.sp->file = strdup("/dev/stdin");
|
|
if(!state.sp->file)
|
|
report(3, "out of memory [push]", NiL, (unsigned long)0);
|
|
state.sp->fp = stdin;
|
|
}
|
|
else
|
|
{
|
|
buf = buffer();
|
|
if (path = find(buf, file, &st))
|
|
{
|
|
if (!(state.sp->fp = fopen(path, "r")))
|
|
report(3, "cannot read", path, (unsigned long)0);
|
|
if(state.sp->file)
|
|
free(state.sp->file);
|
|
state.sp->file = duplicate(path);
|
|
drop(buf);
|
|
}
|
|
else
|
|
{
|
|
drop(buf);
|
|
pop();
|
|
if (flags & STREAM_MUST)
|
|
report(3, "not found", file, (unsigned long)0);
|
|
return 0;
|
|
}
|
|
}
|
|
state.sp->flags = flags;
|
|
state.sp->line = 0;
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* return the next input line
|
|
*/
|
|
|
|
static char*
|
|
input(void)
|
|
{
|
|
char* e;
|
|
|
|
if (!state.sp)
|
|
report(3, "no input file stream", NiL, (unsigned long)0);
|
|
if (state.peek)
|
|
state.peek = 0;
|
|
else if (!fgets(state.input, sizeof(state.input), state.sp->fp))
|
|
return 0;
|
|
else if (*state.input && *(e = state.input + strlen(state.input) - 1) == '\n')
|
|
*e = 0;
|
|
state.sp->line++;
|
|
return state.input;
|
|
}
|
|
|
|
/*
|
|
* pass shell action s to ${SHELL:-/bin/sh}
|
|
* the -c wrapper ensures that scripts are run in the selected shell
|
|
* even on systems that otherwise demand #! magic (can you say Cygwin)
|
|
*/
|
|
|
|
static int
|
|
execute(register char* s)
|
|
{
|
|
register int c;
|
|
Buf_t* buf;
|
|
|
|
if (!state.shell && (!(state.shell = (char*)search(state.vars, "SHELL", NiL)) || !strcmp(state.shell, sh)))
|
|
state.shell = sh;
|
|
buf = buffer();
|
|
append(buf, state.shell);
|
|
append(buf, " -c '");
|
|
while (c = *s++)
|
|
{
|
|
if (c == '\'')
|
|
{
|
|
add(buf, c);
|
|
for (s--; *s == c; s++)
|
|
{
|
|
add(buf, '\\');
|
|
add(buf, c);
|
|
}
|
|
}
|
|
add(buf, c);
|
|
}
|
|
add(buf, '\'');
|
|
s = use(buf);
|
|
report(-5, s, "exec", (unsigned long)0);
|
|
if ((c = system(s)) > 255)
|
|
c >>= 8;
|
|
drop(buf);
|
|
return c;
|
|
}
|
|
|
|
/*
|
|
* run action s to update r
|
|
*/
|
|
|
|
static unsigned long
|
|
run(Rule_t* r, register char* s)
|
|
{
|
|
register Rule_t* q;
|
|
register char* t;
|
|
register int c;
|
|
register View_t* v;
|
|
int i;
|
|
int j;
|
|
int x;
|
|
Stat_t st;
|
|
Buf_t* buf;
|
|
|
|
if (r->flags & RULE_error)
|
|
return r->time;
|
|
buf = buffer();
|
|
if (!strncmp(s, "mamake -r ", 10))
|
|
{
|
|
state.verified = 1;
|
|
x = !state.never;
|
|
}
|
|
else
|
|
x = state.exec;
|
|
if (x)
|
|
append(buf, "trap - 1 2 3 15\nPATH=.:$PATH\nset -x\n");
|
|
if (state.view)
|
|
{
|
|
do
|
|
{
|
|
for (; delimiter(*s); s++)
|
|
add(buf, *s);
|
|
for (t = s; *s && !delimiter(*s); s++);
|
|
c = *s;
|
|
*s = 0;
|
|
if (c == '=')
|
|
{
|
|
append(buf, t);
|
|
continue;
|
|
}
|
|
if ((q = (Rule_t*)search(state.rules, t, NiL)) && q->path && !(q->flags & RULE_generated))
|
|
append(buf, q->path);
|
|
else
|
|
{
|
|
append(buf, t);
|
|
if (*t == '-' && *(t + 1) == 'I' && (*(t + 2) || c))
|
|
{
|
|
if (*(t + 2))
|
|
i = 2;
|
|
else
|
|
{
|
|
for (i = 3; isspace(*(t + i)); i++);
|
|
*s = c;
|
|
for (s = t + i; *s && !isspace(*s); s++);
|
|
c = *s;
|
|
*s = 0;
|
|
append(buf, t + 2);
|
|
}
|
|
if (*(t + i) && *(t + i) != '/')
|
|
{
|
|
v = state.view;
|
|
while (v = v->next)
|
|
{
|
|
add(buf, ' ');
|
|
for (j = 0; j < i; j++)
|
|
add(buf, *(t + j));
|
|
append(buf, v->dir);
|
|
if (*(t + i) != '.' || *(t + i + 1))
|
|
{
|
|
add(buf, '/');
|
|
append(buf, t + i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} while (*s = c);
|
|
s = use(buf);
|
|
}
|
|
else if (x)
|
|
{
|
|
append(buf, s);
|
|
s = use(buf);
|
|
}
|
|
if (x)
|
|
{
|
|
if (c = execute(s))
|
|
dont(r, c, state.keepgoing);
|
|
if (status((Buf_t*)0, 0, r->name, &st))
|
|
{
|
|
r->time = st.st_mtime;
|
|
r->flags |= RULE_exists;
|
|
}
|
|
else
|
|
r->time = NOW;
|
|
}
|
|
else
|
|
{
|
|
fprintf(stdout, "%s\n", s);
|
|
if (state.debug)
|
|
fflush(stdout);
|
|
r->time = NOW;
|
|
r->flags |= RULE_exists;
|
|
}
|
|
drop(buf);
|
|
return r->time;
|
|
}
|
|
|
|
/*
|
|
* return the full path for s using buf workspace
|
|
*/
|
|
|
|
static char*
|
|
path(Buf_t* buf, char* s, int must)
|
|
{
|
|
register char* p;
|
|
register char* d;
|
|
register char* x;
|
|
char* e;
|
|
register int c;
|
|
int t;
|
|
int o;
|
|
Stat_t st;
|
|
|
|
for (e = s; *e && !isspace(*e); e++);
|
|
t = *e;
|
|
if ((x = status(buf, 0, s, &st)) && (st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))
|
|
return x;
|
|
if (!(p = (char*)search(state.vars, "PATH", NiL)))
|
|
report(3, "variable not defined", "PATH", (unsigned long)0);
|
|
do
|
|
{
|
|
for (d = p; *p && *p != ':'; p++);
|
|
c = *p;
|
|
*p = 0;
|
|
if (*d && (*d != '.' || *(d + 1)))
|
|
{
|
|
append(buf, d);
|
|
add(buf, '/');
|
|
}
|
|
*p = c;
|
|
if (t)
|
|
*e = 0;
|
|
append(buf, s);
|
|
if (t)
|
|
*e = t;
|
|
o = get(buf);
|
|
x = use(buf);
|
|
if ((x = status(buf, o, x, &st)) && (st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))
|
|
return x;
|
|
} while (*p++);
|
|
if (must)
|
|
report(3, "command not found", s, (unsigned long)0);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* generate (if necessary) and read the MAM probe information
|
|
* done on the first `setv CC ...'
|
|
*/
|
|
|
|
static void
|
|
probe(void)
|
|
{
|
|
register char* cc;
|
|
register char* s;
|
|
unsigned long h;
|
|
unsigned long q;
|
|
Buf_t* buf;
|
|
Buf_t* pro;
|
|
Buf_t* tmp;
|
|
struct stat st;
|
|
|
|
static char let[] = "ABCDEFGHIJKLMNOP";
|
|
static char cmd[] = "mamprobe";
|
|
|
|
if (!(cc = (char*)search(state.vars, "CC", NiL)))
|
|
cc = "cc";
|
|
buf = buffer();
|
|
s = path(buf, cmd, 1);
|
|
q = stat(s, &st) ? (unsigned long)0 : (unsigned long)st.st_mtime;
|
|
pro = buffer();
|
|
s = cc = path(pro, cc, 1);
|
|
for (h = 0; *s; s++)
|
|
h = h * 0x63c63cd9L + *s + 0x9c39c33dL;
|
|
if (!(s = (char*)search(state.vars, "INSTALLROOT", NiL)))
|
|
report(3, "variable must be defined", "INSTALLROOT", (unsigned long)0);
|
|
append(buf, s);
|
|
append(buf, "/lib/probe/C/mam/");
|
|
for (h &= 0xffffffffL; h; h >>= 4)
|
|
add(buf, let[h & 0xf]);
|
|
s = use(buf);
|
|
h = stat(s, &st) ? (unsigned long)0 : (unsigned long)st.st_mtime;
|
|
if (h < q || !push(s, (Stdio_t*)0, 0))
|
|
{
|
|
tmp = buffer();
|
|
append(tmp, cmd);
|
|
add(tmp, ' ');
|
|
append(tmp, s);
|
|
add(tmp, ' ');
|
|
append(tmp, cc);
|
|
if (execute(use(tmp)))
|
|
report(3, "cannot generate probe info", s, (unsigned long)0);
|
|
drop(tmp);
|
|
if (!push(s, (Stdio_t*)0, 0))
|
|
report(3, "cannot read probe info", s, (unsigned long)0);
|
|
}
|
|
drop(pro);
|
|
drop(buf);
|
|
make(rule(""));
|
|
pop();
|
|
}
|
|
|
|
/*
|
|
* add attributes in s to r
|
|
*/
|
|
|
|
static void
|
|
attributes(register Rule_t* r, register char* s)
|
|
{
|
|
register char* t;
|
|
register int n;
|
|
|
|
for (;;)
|
|
{
|
|
int flag = 0;
|
|
for (; isspace(*s); s++);
|
|
for (t = s; *s && !isspace(*s); s++);
|
|
if (!(n = s - t))
|
|
break;
|
|
switch (*t)
|
|
{
|
|
case 'd':
|
|
if (n == 8 && !strncmp(t, "dontcare", n))
|
|
flag = RULE_dontcare;
|
|
break;
|
|
case 'g':
|
|
if (n == 9 && !strncmp(t, "generated", n))
|
|
flag = RULE_generated;
|
|
break;
|
|
case 'i':
|
|
if (n == 6 && !strncmp(t, "ignore", n))
|
|
flag = RULE_ignore;
|
|
else if (n == 8 && !strncmp(t, "implicit", n))
|
|
flag = RULE_implicit;
|
|
break;
|
|
case 'v':
|
|
if (n == 7 && !strncmp(t, "virtual", n))
|
|
flag = RULE_virtual;
|
|
break;
|
|
case 'a':
|
|
if (n == 7 && !strncmp(t, "archive", n))
|
|
flag = -1; /* ignore (not implemented) */
|
|
break;
|
|
case 'j':
|
|
if (n == 5 && !strncmp(t, "joint", n))
|
|
flag = -1; /* ignore (not implemented) */
|
|
break;
|
|
}
|
|
if(flag > 0)
|
|
r->flags |= flag;
|
|
else if(flag == 0)
|
|
{
|
|
t[n] = '\0';
|
|
report(3, "unknown attribute", t, (unsigned long)0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* define ${mam_libX} for library reference lib
|
|
*/
|
|
|
|
static char*
|
|
require(char* lib, int dontcare)
|
|
{
|
|
register int c;
|
|
char* s;
|
|
char* r;
|
|
FILE* f;
|
|
Buf_t* buf;
|
|
Buf_t* tmp;
|
|
struct stat st;
|
|
|
|
int tofree = 0;
|
|
static int dynamic = -1;
|
|
|
|
if (dynamic < 0)
|
|
dynamic = (s = search(state.vars, "mam_cc_L", NiL)) ? atoi(s) : 0;
|
|
if (!(r = search(state.vars, lib, NiL)))
|
|
{
|
|
buf = buffer();
|
|
tmp = buffer();
|
|
s = 0;
|
|
for (;;)
|
|
{
|
|
if (s)
|
|
append(buf, s);
|
|
if (r = search(state.vars, "mam_cc_PREFIX_ARCHIVE", NiL))
|
|
append(buf, r);
|
|
append(buf, lib + 2);
|
|
if (r = search(state.vars, "mam_cc_SUFFIX_ARCHIVE", NiL))
|
|
append(buf, r);
|
|
r = expand(tmp, use(buf));
|
|
if (!stat(r, &st))
|
|
break;
|
|
if (s)
|
|
{
|
|
r = lib;
|
|
break;
|
|
}
|
|
s = "${INSTALLROOT}/lib/";
|
|
if (dynamic)
|
|
{
|
|
append(buf, s);
|
|
if (r = search(state.vars, "mam_cc_PREFIX_SHARED", NiL))
|
|
append(buf, r);
|
|
append(buf, lib + 2);
|
|
if (r = search(state.vars, "mam_cc_SUFFIX_SHARED", NiL))
|
|
append(buf, r);
|
|
r = expand(tmp, use(buf));
|
|
if (!stat(r, &st))
|
|
{
|
|
r = lib;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (r != lib)
|
|
{
|
|
tofree = 1;
|
|
r = duplicate(r);
|
|
}
|
|
search(state.vars, lib, r);
|
|
append(tmp, lib + 2);
|
|
append(tmp, ".req");
|
|
if (!(f = fopen(use(tmp), "r")))
|
|
{
|
|
append(tmp, "${INSTALLROOT}/lib/lib/");
|
|
append(tmp, lib + 2);
|
|
f = fopen(expand(buf, use(tmp)), "r");
|
|
}
|
|
if (f)
|
|
{
|
|
for (;;)
|
|
{
|
|
while (isspace(c = fgetc(f)));
|
|
if (c == EOF)
|
|
break;
|
|
do
|
|
{
|
|
add(tmp, c);
|
|
} while ((c = fgetc(f)) != EOF && !isspace(c));
|
|
s = use(tmp);
|
|
if (s[0] && (s[0] != '-' || s[1]))
|
|
{
|
|
add(buf, ' ');
|
|
append(buf, require(s, 0));
|
|
}
|
|
}
|
|
fclose(f);
|
|
if(tofree)
|
|
free(r);
|
|
r = use(buf);
|
|
}
|
|
else if (dontcare)
|
|
{
|
|
append(tmp, "set -\n");
|
|
append(tmp, "cd \"${TMPDIR:-/tmp}\"\n");
|
|
append(tmp, "echo 'int main(){return 0;}' > x.${!-$$}.c\n");
|
|
append(tmp, "${CC} ${CCFLAGS} -o x.${!-$$}.x x.${!-$$}.c ");
|
|
append(tmp, r);
|
|
append(tmp, " >/dev/null 2>&1\n");
|
|
append(tmp, "c=$?\n");
|
|
append(tmp, "rm -f x.${!-$$}.[cox]\n");
|
|
append(tmp, "exit $c\n");
|
|
if (execute(expand(buf, use(tmp))))
|
|
{
|
|
if(tofree)
|
|
free(r);
|
|
r = "";
|
|
}
|
|
}
|
|
r = duplicate(r);
|
|
search(state.vars, lib, r);
|
|
append(tmp, "mam_lib");
|
|
append(tmp, lib + 2);
|
|
search(state.vars, use(tmp), r);
|
|
drop(tmp);
|
|
drop(buf);
|
|
}
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* input() until `done r'
|
|
*/
|
|
|
|
static unsigned long
|
|
make(Rule_t* r)
|
|
{
|
|
register char* s;
|
|
register char* t;
|
|
register char* u;
|
|
register char* v;
|
|
register Rule_t* q;
|
|
unsigned long z;
|
|
unsigned long x;
|
|
Buf_t* buf;
|
|
Buf_t* cmd;
|
|
|
|
r->making++;
|
|
if (r->flags & RULE_active)
|
|
state.active++;
|
|
if (*r->name)
|
|
{
|
|
z = bindfile(r);
|
|
state.indent++;
|
|
report(-1, r->name, "make", r->time);
|
|
}
|
|
else
|
|
z = 0;
|
|
buf = buffer();
|
|
cmd = 0;
|
|
/*
|
|
* Parse lines
|
|
*/
|
|
while (s = input())
|
|
{
|
|
/* skip initial whitespace and empty line */
|
|
for (; isspace(*s); s++);
|
|
if (!*s)
|
|
continue;
|
|
/* isolate command name (u), argument word (t), and the operand string (v) */
|
|
for (u = s; *s && !isspace(*s); s++);
|
|
if (*s)
|
|
{
|
|
for (*s++ = 0; isspace(*s); s++);
|
|
for (t = s; *s && !isspace(*s); s++);
|
|
if (*s)
|
|
for (*s++ = 0; isspace(*s); s++);
|
|
v = s;
|
|
}
|
|
else
|
|
t = v = s;
|
|
/* enforce 4-letter lowercase command name */
|
|
if(u[0]<'a' || u[0]>'z' || u[1]<'a' || u[1]>'z' || u[2]<'a' || u[2]>'z' || u[3]<'a' || u[3]>'z' || u[4] && !isspace(u[4]))
|
|
report(3, "not a command name", u, (unsigned long)0);
|
|
switch (KEY(u[0], u[1], u[2], u[3]))
|
|
{
|
|
case KEY('b','i','n','d'):
|
|
if ((t[0] == '-' || t[0] == '+') && t[1] == 'l' && (s = require(t, !strcmp(v, "dontcare"))) && strncmp(r->name, "FEATURE/", 8) && strcmp(r->name, "configure.h"))
|
|
{
|
|
for (;;)
|
|
{
|
|
for (t = s; *s && !isspace(*s); s++);
|
|
if (*s)
|
|
*s = 0;
|
|
else
|
|
s = 0;
|
|
if (*t)
|
|
{
|
|
q = rule(expand(buf, t));
|
|
attributes(q, v);
|
|
x = bindfile(q);
|
|
if (z < x)
|
|
z = x;
|
|
if (q->flags & RULE_error)
|
|
r->flags |= RULE_error;
|
|
}
|
|
if (!s)
|
|
break;
|
|
for (*s++ = ' '; isspace(*s); s++);
|
|
}
|
|
}
|
|
continue;
|
|
case KEY('d','o','n','e'):
|
|
q = rule(expand(buf, t));
|
|
if (q != r && t[0] != '$')
|
|
report(2, "improper done statement", t, (unsigned long)0);
|
|
attributes(r, v);
|
|
if (cmd && state.active && (state.force || r->time < z || !r->time && !z))
|
|
{
|
|
if (state.explain && !state.force)
|
|
{
|
|
if (!r->time)
|
|
fprintf(stderr, "%s [not found]\n", r->name);
|
|
else
|
|
fprintf(stderr, "%s [%lu] older than prerequisites [%lu]\n", r->name, r->time, z);
|
|
}
|
|
substitute(buf, use(cmd));
|
|
x = run(r, use(buf));
|
|
if (z < x)
|
|
z = x;
|
|
}
|
|
r->flags |= RULE_made;
|
|
if (!(r->flags & (RULE_dontcare|RULE_error|RULE_exists|RULE_generated|RULE_implicit|RULE_virtual)))
|
|
dont(r, 0, state.keepgoing);
|
|
break;
|
|
case KEY('e','x','e','c'):
|
|
r->flags |= RULE_generated;
|
|
if (r->path)
|
|
{
|
|
free(r->path);
|
|
r->path = 0;
|
|
r->time = 0;
|
|
}
|
|
if (state.active)
|
|
{
|
|
if (cmd)
|
|
add(cmd, '\n');
|
|
else
|
|
cmd = buffer();
|
|
append(cmd, v);
|
|
}
|
|
continue;
|
|
case KEY('m','a','k','e'):
|
|
q = rule(expand(buf, t));
|
|
if (!q->making)
|
|
{
|
|
attributes(q, v);
|
|
x = make(q);
|
|
if (!(q->flags & RULE_ignore) && z < x)
|
|
z = x;
|
|
if (q->flags & RULE_error)
|
|
r->flags |= RULE_error;
|
|
}
|
|
continue;
|
|
case KEY('p','r','e','v'):
|
|
q = rule(expand(buf, t));
|
|
if (!q->making)
|
|
{
|
|
if (!(q->flags & RULE_ignore) && z < q->time)
|
|
z = q->time;
|
|
if (q->flags & RULE_error)
|
|
r->flags |= RULE_error;
|
|
state.indent++;
|
|
report(-2, q->name, "prev", q->time);
|
|
state.indent--;
|
|
}
|
|
continue;
|
|
case KEY('s','e','t','v'):
|
|
if (!search(state.vars, t, NiL))
|
|
{
|
|
if (*v == '"')
|
|
{
|
|
s = v + strlen(v) - 1;
|
|
if (*s == '"')
|
|
{
|
|
*s = 0;
|
|
v++;
|
|
}
|
|
}
|
|
search(state.vars, t, duplicate(expand(buf, v)));
|
|
}
|
|
if (!state.probed && t[0] == 'C' && t[1] == 'C' && !t[2])
|
|
{
|
|
state.probed = 1;
|
|
probe();
|
|
}
|
|
continue;
|
|
case KEY('i','n','f','o'):
|
|
case KEY('n','o','t','e'):
|
|
case KEY('m','e','t','a'):
|
|
/* comment command */
|
|
continue;
|
|
default:
|
|
report(3, "unknown command", u, (unsigned long)0);
|
|
}
|
|
break;
|
|
}
|
|
drop(buf);
|
|
if (cmd)
|
|
drop(cmd);
|
|
if (*r->name)
|
|
{
|
|
report(-1, r->name, "done", z);
|
|
state.indent--;
|
|
}
|
|
if (r->flags & RULE_active)
|
|
state.active--;
|
|
r->making--;
|
|
return r->time = z;
|
|
}
|
|
|
|
/*
|
|
* verify that active targets were made
|
|
*/
|
|
|
|
static int
|
|
verify(Dict_item_t* item, void* handle)
|
|
{
|
|
Rule_t* r = (Rule_t*)item->value;
|
|
|
|
if ((r->flags & (RULE_active|RULE_error|RULE_made)) == RULE_active)
|
|
dont(r, 0, 1);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* return 1 if name is an initializer
|
|
*/
|
|
|
|
static int
|
|
initializer(char* name)
|
|
{
|
|
register char* s;
|
|
|
|
if (s = last(name, '/'))
|
|
s++;
|
|
else
|
|
s = name;
|
|
return s[0] == 'I' && s[1] == 'N' && s[2] == 'I' && s[3] == 'T';
|
|
}
|
|
|
|
/*
|
|
* update recursion leaf r and its prerequisites
|
|
*/
|
|
|
|
static int
|
|
update(register Rule_t* r)
|
|
{
|
|
register List_t* x;
|
|
Buf_t* buf;
|
|
|
|
static char cmd[] = "${MAMAKE} -C ";
|
|
static char arg[] = " ${MAMAKEARGS}";
|
|
|
|
r->flags |= RULE_made;
|
|
if (r->leaf)
|
|
r->leaf->flags |= RULE_made;
|
|
for (x = r->prereqs; x; x = x->next)
|
|
if (x->rule->leaf && !(x->rule->flags & RULE_made))
|
|
update(x->rule);
|
|
buf = buffer();
|
|
substitute(buf, cmd);
|
|
append(buf, r->name);
|
|
substitute(buf, arg);
|
|
run(r, use(buf));
|
|
drop(buf);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* scan Mamfile prereqs
|
|
*/
|
|
|
|
static int
|
|
scan(Dict_item_t* item, void* handle)
|
|
{
|
|
register Rule_t* r = (Rule_t*)item->value;
|
|
register char* s;
|
|
register char* t;
|
|
register char* u;
|
|
register char* w;
|
|
Rule_t* q;
|
|
int i;
|
|
int j;
|
|
int k;
|
|
int p;
|
|
Buf_t* buf;
|
|
|
|
static char* files[] =
|
|
{
|
|
"Mamfile"
|
|
/* ksh 93u+m no longer uses these:
|
|
* "Nmakefile",
|
|
* "nmakefile",
|
|
* "Makefile",
|
|
* "makefile"
|
|
*/
|
|
};
|
|
|
|
/*
|
|
* drop non-leaf rules
|
|
*/
|
|
|
|
if (!r->leaf)
|
|
return 0;
|
|
|
|
/*
|
|
* always make initializers
|
|
*/
|
|
|
|
if (initializer(r->name))
|
|
{
|
|
if (!(r->flags & RULE_made))
|
|
update(r);
|
|
return 0;
|
|
}
|
|
buf = buffer();
|
|
for (i = 0; i < elementsof(files); i++)
|
|
{
|
|
append(buf, r->name);
|
|
add(buf, '/');
|
|
append(buf, files[i]);
|
|
if (push(use(buf), (Stdio_t*)0, 0))
|
|
{
|
|
while (s = input())
|
|
{
|
|
j = p = 0;
|
|
while (*s)
|
|
{
|
|
for (k = 1; isspace(i = *s) || i == '"' || i == '\''; s++);
|
|
for (t = s; (i = *s) && !isspace(i) && i != '"' && i != '\'' && i != '\\' && i != ':'; s++)
|
|
if (i == '/')
|
|
t = s + 1;
|
|
else if (i == '.' && *(s + 1) != 'c' && *(s + 1) != 'C' && *(s + 1) != 'h' && *(s + 1) != 'H' && t[0] == 'l' && t[1] == 'i' && t[2] == 'b')
|
|
*s = 0;
|
|
if (*s)
|
|
*s++ = 0;
|
|
if (!t[0])
|
|
k = 0;
|
|
else if ((t[0] == '-' || t[0] == '+') && t[1] == 'l' && t[2])
|
|
{
|
|
append(buf, "lib");
|
|
append(buf, t + 2);
|
|
t = use(buf);
|
|
}
|
|
else if (p)
|
|
{
|
|
if (t[0] == '+' && !t[1])
|
|
p = 2;
|
|
else if (p == 1)
|
|
{
|
|
if (i != ':' || strncmp(s, "command", 7))
|
|
{
|
|
append(buf, "lib");
|
|
append(buf, t);
|
|
t = use(buf);
|
|
}
|
|
if (i == ':')
|
|
while (*s && isspace(*s))
|
|
s++;
|
|
}
|
|
}
|
|
else if (i == ':')
|
|
{
|
|
if (j != ':' || !isupper(*t))
|
|
k = 0;
|
|
else if (!strcmp(t, "PACKAGE"))
|
|
{
|
|
p = 1;
|
|
k = 0;
|
|
}
|
|
else
|
|
{
|
|
for (u = t; *u; u++)
|
|
{
|
|
if (isupper(*u))
|
|
*u = tolower(*u);
|
|
else if (!isalnum(*u))
|
|
{
|
|
k = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (t[0] != 'l' || t[1] != 'i' || t[2] != 'b')
|
|
k = 0;
|
|
else
|
|
{
|
|
for (u = t + 3; *u; u++)
|
|
{
|
|
if (!isalnum(*u))
|
|
{
|
|
k = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (k && ((q = (Rule_t*)search(state.leaf, t, NiL)) && q != r || *t++ == 'l' && *t++ == 'i' && *t++ == 'b' && *t && (q = (Rule_t*)search(state.leaf, t, NiL)) && q != r))
|
|
{
|
|
for (t = w = r->name; *w; w++)
|
|
if (*w == '/')
|
|
t = w + 1;
|
|
if (t[0] == 'l' && t[1] == 'i' && t[2] == 'b')
|
|
t += 3;
|
|
for (u = w = q->name; *w; w++)
|
|
if (*w == '/')
|
|
u = w + 1;
|
|
if (strcmp(t, u))
|
|
cons(r, q);
|
|
}
|
|
j = i;
|
|
}
|
|
}
|
|
pop();
|
|
for (s = 0, w = r->name; *w; w++)
|
|
if (*w == '/')
|
|
s = w;
|
|
if (s)
|
|
{
|
|
if ((s - r->name) > 3 && *(s - 1) == 'b' && *(s - 2) == 'i' && *(s - 3) == 'l' && *(s - 4) != '/')
|
|
{
|
|
/*
|
|
* foolib : foo : libfoo
|
|
*/
|
|
|
|
*(s - 3) = 0;
|
|
q = (Rule_t*)search(state.leaf, r->name, NiL);
|
|
if (q && q != r)
|
|
cons(r, q);
|
|
for (t = w = r->name; *w; w++)
|
|
if (*w == '/')
|
|
t = w + 1;
|
|
append(buf, "lib");
|
|
append(buf, t);
|
|
q = (Rule_t*)search(state.leaf, use(buf), NiL);
|
|
if (q && q != r)
|
|
cons(r, q);
|
|
*(s - 3) = 'l';
|
|
}
|
|
else if (((s - r->name) != 3 || *(s - 1) != 'b' || *(s - 2) != 'i' || *(s - 3) != 'l') && (*(s + 1) != 'l' || *(s + 2) != 'i' || *(s + 3) != 'b'))
|
|
{
|
|
/*
|
|
* huh/foobar : lib/libfoo
|
|
*/
|
|
|
|
s++;
|
|
t = s + strlen(s);
|
|
while (--t > s)
|
|
{
|
|
append(buf, "lib/lib");
|
|
appendn(buf, s, t - s);
|
|
q = (Rule_t*)search(state.leaf, use(buf), NiL);
|
|
if (q && q != r)
|
|
cons(r, q);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
drop(buf);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* descend into op and its prereqs
|
|
*/
|
|
|
|
static int
|
|
descend(Dict_item_t* item, void* handle)
|
|
{
|
|
Rule_t* r = (Rule_t*)item->value;
|
|
|
|
if (!state.active && (!(r->flags & RULE_active) || !(r = (Rule_t*)search(state.leaf, r->name, NiL))))
|
|
return 0;
|
|
return r->leaf && !(r->flags & RULE_made) ? update(r) : 0;
|
|
}
|
|
|
|
/*
|
|
* append the non-leaf active targets to state.opt
|
|
*/
|
|
|
|
static int
|
|
active(Dict_item_t* item, void* handle)
|
|
{
|
|
Rule_t* r = (Rule_t*)item->value;
|
|
|
|
if (r->flags & RULE_active)
|
|
{
|
|
if (r->leaf || search(state.leaf, r->name, NiL))
|
|
state.active = 0;
|
|
else
|
|
{
|
|
add(state.opt, ' ');
|
|
append(state.opt, r->name);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* recurse on mamfiles in subdirs matching pattern
|
|
*/
|
|
|
|
static int
|
|
recurse(char* pattern)
|
|
{
|
|
register char* s;
|
|
register char* t;
|
|
Rule_t* r;
|
|
Buf_t* buf;
|
|
Buf_t* tmp;
|
|
struct stat st;
|
|
|
|
/*
|
|
* first determine the MAM subdirs
|
|
*/
|
|
|
|
tmp = buffer();
|
|
buf = buffer();
|
|
state.exec = !state.never;
|
|
state.leaf = dictionary();
|
|
append(buf, "ls -d ");
|
|
append(buf, pattern);
|
|
s = use(buf);
|
|
push("recurse", popen(s, "r"), STREAM_PIPE);
|
|
while (s = input())
|
|
{
|
|
append(buf, s);
|
|
add(buf, '/');
|
|
append(buf, mamfile);
|
|
if (find(tmp, use(buf), &st))
|
|
{
|
|
r = rule(s);
|
|
if (t = last(r->name, '/'))
|
|
t++;
|
|
else
|
|
t = r->name;
|
|
r->leaf = rule(t);
|
|
search(state.leaf, t, r);
|
|
}
|
|
}
|
|
pop();
|
|
drop(buf);
|
|
drop(tmp);
|
|
|
|
/*
|
|
* grab the non-leaf active targets
|
|
*/
|
|
|
|
if (!state.active)
|
|
{
|
|
state.active = 1;
|
|
walk(state.rules, active, NiL);
|
|
}
|
|
search(state.vars, "MAMAKEARGS", duplicate(use(state.opt) + 1));
|
|
|
|
/*
|
|
* scan the Mamfile and descend
|
|
*/
|
|
|
|
walk(state.rules, scan, NiL);
|
|
while (state.view)
|
|
{
|
|
View_t *prev = state.view;
|
|
state.view = state.view->next;
|
|
free(prev);
|
|
}
|
|
walk(state.rules, descend, NiL);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
main(int argc, char** argv)
|
|
{
|
|
register char** e;
|
|
register char* s;
|
|
register char* t;
|
|
register char* v;
|
|
Buf_t* tmp;
|
|
int c;
|
|
|
|
/*
|
|
* initialize the state
|
|
*/
|
|
|
|
state.id = "mamake";
|
|
state.active = 1;
|
|
state.exec = 1;
|
|
state.file = mamfile;
|
|
state.opt = buffer();
|
|
state.rules = dictionary();
|
|
state.vars = dictionary();
|
|
search(state.vars, "MAMAKE", *argv);
|
|
|
|
/*
|
|
* parse the options
|
|
*/
|
|
|
|
#if _PACKAGE_ast
|
|
error_info.id = state.id;
|
|
for (;;)
|
|
{
|
|
switch (optget(argv, usage))
|
|
{
|
|
case 'e':
|
|
append(state.opt, " -e");
|
|
state.explain = 1;
|
|
continue;
|
|
case 'i':
|
|
append(state.opt, " -i");
|
|
state.ignore = 1;
|
|
continue;
|
|
case 'k':
|
|
append(state.opt, " -k");
|
|
state.keepgoing = 1;
|
|
continue;
|
|
case 'N':
|
|
state.never = 1;
|
|
/* FALLTHROUGH */
|
|
case 'n':
|
|
append(state.opt, " -n");
|
|
state.exec = 0;
|
|
continue;
|
|
case 'F':
|
|
append(state.opt, " -F");
|
|
state.force = 1;
|
|
continue;
|
|
case 'K':
|
|
continue;
|
|
case 'V':
|
|
fprintf(stdout, "%s\n", id + 10);
|
|
exit(0);
|
|
case 'f':
|
|
append(state.opt, " -f ");
|
|
append(state.opt, opt_info.arg);
|
|
state.file = opt_info.arg;
|
|
continue;
|
|
case 'r':
|
|
state.recurse = opt_info.arg;
|
|
continue;
|
|
case 'C':
|
|
state.directory = opt_info.arg;
|
|
continue;
|
|
case 'D':
|
|
append(state.opt, " -D");
|
|
append(state.opt, opt_info.arg);
|
|
state.debug = -opt_info.num;
|
|
continue;
|
|
case 'G':
|
|
append(state.opt, " -G");
|
|
search(state.vars, "-debug-symbols", "1");
|
|
continue;
|
|
case 'S':
|
|
append(state.opt, " -S");
|
|
search(state.vars, "-strip-symbols", "1");
|
|
continue;
|
|
case '?':
|
|
error(ERROR_usage(2), "%s", opt_info.arg);
|
|
UNREACHABLE();
|
|
case ':':
|
|
error(2, "%s", opt_info.arg);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
if (error_info.errors)
|
|
{
|
|
error(ERROR_usage(2), "%s", optusage(NiL));
|
|
UNREACHABLE();
|
|
}
|
|
argv += opt_info.index;
|
|
#else
|
|
while ((s = *++argv) && *s == '-')
|
|
{
|
|
if (*(s + 1) == '-')
|
|
{
|
|
if (!*(s + 2))
|
|
{
|
|
append(state.opt, " --");
|
|
argv++;
|
|
break;
|
|
}
|
|
for (t = s += 2; *t && *t != '='; t++);
|
|
if (!strncmp(s, "debug-symbols", t - s) && append(state.opt, " -G") || !strncmp(s, "strip-symbols", t - s) && append(state.opt, " -S"))
|
|
{
|
|
if (*t)
|
|
{
|
|
v = t + 1;
|
|
if (t > s && *(t - 1) == '+')
|
|
t--;
|
|
c = *t;
|
|
*t = 0;
|
|
}
|
|
else
|
|
{
|
|
c = 0;
|
|
v = "1";
|
|
}
|
|
search(state.vars, s - 1, v);
|
|
if (c)
|
|
*t = c;
|
|
continue;
|
|
}
|
|
usage();
|
|
break;
|
|
}
|
|
for (;;)
|
|
{
|
|
switch (*++s)
|
|
{
|
|
case 0:
|
|
break;
|
|
case 'e':
|
|
append(state.opt, " -e");
|
|
state.explain = 1;
|
|
continue;
|
|
case 'i':
|
|
append(state.opt, " -i");
|
|
state.ignore = 1;
|
|
continue;
|
|
case 'k':
|
|
append(state.opt, " -k");
|
|
state.keepgoing = 1;
|
|
continue;
|
|
case 'N':
|
|
state.never = 1;
|
|
/* FALLTHROUGH */
|
|
case 'n':
|
|
append(state.opt, " -n");
|
|
state.exec = 0;
|
|
continue;
|
|
case 'F':
|
|
append(state.opt, " -F");
|
|
state.force = 1;
|
|
continue;
|
|
case 'G':
|
|
append(state.opt, " -G");
|
|
search(state.vars, "-debug-symbols", "1");
|
|
continue;
|
|
case 'K':
|
|
continue;
|
|
case 'S':
|
|
append(state.opt, " -S");
|
|
search(state.vars, "-strip-symbols", "1");
|
|
continue;
|
|
case 'V':
|
|
fprintf(stdout, "%s\n", id + 10);
|
|
exit(0);
|
|
case 'f':
|
|
case 'r':
|
|
case 'C':
|
|
case 'D':
|
|
t = s;
|
|
if (!*++s && !(s = *++argv))
|
|
{
|
|
report(2, "option value expected", t, (unsigned long)0);
|
|
usage();
|
|
}
|
|
else
|
|
switch (*t)
|
|
{
|
|
case 'f':
|
|
append(state.opt, " -f ");
|
|
append(state.opt, s);
|
|
state.file = s;
|
|
break;
|
|
case 'r':
|
|
state.recurse = s;
|
|
break;
|
|
case 'C':
|
|
state.directory = s;
|
|
break;
|
|
case 'D':
|
|
append(state.opt, " -D");
|
|
append(state.opt, s);
|
|
state.debug = -atoi(s);
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
report(2, "unknown option", s, (unsigned long)0);
|
|
/* FALLTHROUGH */
|
|
case '?':
|
|
usage();
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* load the environment
|
|
*/
|
|
|
|
for (e = environ; s = *e; e++)
|
|
{
|
|
for (t = s; *t; t++)
|
|
{
|
|
if (*t == '=')
|
|
{
|
|
*t = 0;
|
|
search(state.vars, s, t + 1);
|
|
*t = '=';
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* grab the command line targets and variable definitions
|
|
*/
|
|
|
|
while (s = *argv++)
|
|
{
|
|
for (t = s; *t; t++)
|
|
{
|
|
if (*t == '=')
|
|
{
|
|
v = t + 1;
|
|
if (t > s && *(t - 1) == '+')
|
|
t--;
|
|
c = *t;
|
|
*t = 0;
|
|
search(state.vars, s, v);
|
|
tmp = buffer();
|
|
append(tmp, s);
|
|
append(tmp, ".FORCE");
|
|
search(state.vars, use(tmp), v);
|
|
drop(tmp);
|
|
*t = c;
|
|
break;
|
|
}
|
|
}
|
|
if (!*t)
|
|
{
|
|
/*
|
|
* handle a few targets for nmake compatibility
|
|
*/
|
|
|
|
if (*s == 'e' && !strncmp(s, "error 0 $(MAKEVERSION:", 22))
|
|
exit(1);
|
|
if (*s == 'r' && !strcmp(s, "recurse") || *s == 'c' && !strncmp(s, "cc-", 3))
|
|
continue;
|
|
rule(s)->flags |= RULE_active;
|
|
state.active = 0;
|
|
if (state.recurse)
|
|
continue;
|
|
}
|
|
add(state.opt, ' ');
|
|
add(state.opt, '\'');
|
|
append(state.opt, s);
|
|
add(state.opt, '\'');
|
|
}
|
|
|
|
/*
|
|
* initialize the views
|
|
*/
|
|
|
|
if (state.directory && chdir(state.directory))
|
|
report(3, "cannot change working directory", NiL, (unsigned long)0);
|
|
view();
|
|
|
|
/*
|
|
* recursion drops out here
|
|
*/
|
|
|
|
if (state.recurse)
|
|
return recurse(state.recurse);
|
|
|
|
/*
|
|
* read the mamfile(s) and bring the targets up to date
|
|
*/
|
|
|
|
search(state.vars, "MAMAKEARGS", duplicate(use(state.opt) + 1));
|
|
push(state.file, (Stdio_t*)0, STREAM_MUST);
|
|
make(rule(""));
|
|
pop();
|
|
|
|
/*
|
|
* verify that active targets were made
|
|
*/
|
|
|
|
if (!state.active && !state.verified)
|
|
walk(state.rules, verify, NiL);
|
|
|
|
/*
|
|
* done
|
|
*/
|
|
|
|
return state.errors != 0;
|
|
}
|