mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-08 12:18:31 +00:00
Improve build system
- Reduce full build latency from ~20s to ~18s - Bring back silent mode if `make V=0` is passed - Demodernize utimes() polyfill so it works RHEL5 - Delete some old shell scripts that are no longer needed - Truncate long lines when outputting builds to Emacs buffers
This commit is contained in:
parent
c797f139bb
commit
b740cca642
39 changed files with 440 additions and 916 deletions
|
@ -18,30 +18,54 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/safemacros.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/copyfile.h"
|
||||
#include "libc/calls/sigbits.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/log/color.internal.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/x/x.h"
|
||||
#include "third_party/getopt/getopt.h"
|
||||
|
||||
#define MANUAL \
|
||||
"\
|
||||
SYNOPSIS\n\
|
||||
\n\
|
||||
compile.com [FLAGS] COMMAND [ARGS...]\n\
|
||||
\n\
|
||||
OVERVIEW\n\
|
||||
\n\
|
||||
GNU/LLVM Compiler Collection Frontend Frontend\n\
|
||||
Compiler Collection Frontend Frontend\n\
|
||||
\n\
|
||||
DESCRIPTION\n\
|
||||
\n\
|
||||
This launches gcc or clang after scrubbing flags.\n\
|
||||
This is a generic command wrapper, e.g.\n\
|
||||
\n\
|
||||
EXAMPLE\n\
|
||||
compile.com gcc -o program program.c\n\
|
||||
\n\
|
||||
compile.com gcc -o program program.c\n\
|
||||
This wrapper provides the following services:\n\
|
||||
\n\
|
||||
- Ensures the output directory exists\n\
|
||||
- Echo the launched subcommand (silent mode supported if V=0)\n\
|
||||
- Magic filtering of GCC vs. Clang flag incompatibilities\n\
|
||||
- Unzips the vendored GCC toolchain if it hasn't happened yet\n\
|
||||
- Making temporary copies of APE executables w/o side-effects\n\
|
||||
- Truncating long lines in \"TERM=dumb\" terminals like emacs\n\
|
||||
\n\
|
||||
This wrapper is extremely fast.\n\
|
||||
\n\
|
||||
FLAGS\n\
|
||||
\n\
|
||||
-A ACTION specifies short command name for V=0 logging\n\
|
||||
-T TARGET specifies target name for V=0 logging\n\
|
||||
-t touch target on success\n\
|
||||
-n do nothing (used to prime the executable)\n\
|
||||
-? print help\n\
|
||||
\n"
|
||||
|
||||
struct Flags {
|
||||
|
@ -65,13 +89,22 @@ bool wantnopg;
|
|||
bool wantpg;
|
||||
bool wantrecord;
|
||||
bool wantubsan;
|
||||
bool touchtarget;
|
||||
|
||||
char *cc;
|
||||
char *cmd;
|
||||
char *cachedcmd;
|
||||
char *originalcmd;
|
||||
char *colorflag;
|
||||
char *outdir;
|
||||
char *outpath;
|
||||
char *action;
|
||||
char *target;
|
||||
char ccpath[PATH_MAX];
|
||||
int ccversion;
|
||||
int columns;
|
||||
|
||||
sigset_t mask;
|
||||
sigset_t savemask;
|
||||
|
||||
struct Flags flags;
|
||||
struct Command command;
|
||||
|
@ -129,6 +162,17 @@ const char *const kGccOnlyFlags[] = {
|
|||
"-mno-fentry",
|
||||
};
|
||||
|
||||
char *DescribeCommand(void) {
|
||||
if (iscc) {
|
||||
if (isgcc) {
|
||||
return xasprintf("gcc %d", ccversion);
|
||||
} else if (isclang) {
|
||||
return xasprintf("clang %d", ccversion);
|
||||
}
|
||||
}
|
||||
return basename(cmd);
|
||||
}
|
||||
|
||||
bool IsGccOnlyFlag(const char *s) {
|
||||
int m, l, r, x;
|
||||
l = 0;
|
||||
|
@ -174,40 +218,75 @@ void AddFlag(char *s) {
|
|||
command.n += n;
|
||||
}
|
||||
} else {
|
||||
command.p = realloc(command.p, command.n + 1);
|
||||
command.p[command.n] = '\n';
|
||||
command.n += 1;
|
||||
command.p = realloc(command.p, command.n + 2);
|
||||
command.p[command.n++] = '\r';
|
||||
command.p[command.n++] = '\n';
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int i, ws, pid;
|
||||
sigset_t mask, savemask;
|
||||
int Launch(void) {
|
||||
int ws, pid;
|
||||
if ((pid = vfork()) == -1) exit(errno);
|
||||
if (!pid) {
|
||||
sigprocmask(SIG_SETMASK, &savemask, NULL);
|
||||
execv(cmd, flags.p);
|
||||
_exit(127);
|
||||
}
|
||||
while (waitpid(pid, &ws, 0) == -1) {
|
||||
if (errno != EINTR) exit(errno);
|
||||
}
|
||||
return ws;
|
||||
}
|
||||
|
||||
if (argc == 1) {
|
||||
int main(int argc, char *argv[]) {
|
||||
char *p;
|
||||
size_t n;
|
||||
int i, ws, rc, opt;
|
||||
|
||||
/*
|
||||
* parse prefix arguments
|
||||
*/
|
||||
while ((opt = getopt(argc, argv, "?hntA:T:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'n':
|
||||
exit(0);
|
||||
case 't':
|
||||
touchtarget = true;
|
||||
break;
|
||||
case 'A':
|
||||
action = optarg;
|
||||
break;
|
||||
case 'T':
|
||||
target = optarg;
|
||||
break;
|
||||
case '?':
|
||||
case 'h':
|
||||
write(1, MANUAL, sizeof(MANUAL) - 1);
|
||||
exit(0);
|
||||
default:
|
||||
write(2, MANUAL, sizeof(MANUAL) - 1);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (optind == argc) {
|
||||
write(2, MANUAL, sizeof(MANUAL) - 1);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (argc == 2 && !strcmp(argv[1], "--do-nothing")) {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (!isdirectory("o/third_party/gcc")) {
|
||||
system("third_party/gcc/unbundle.sh");
|
||||
}
|
||||
|
||||
cc = argv[1];
|
||||
if (!strchr(cc, '/')) {
|
||||
if (!(cc = commandv(argv[1], ccpath))) exit(127);
|
||||
cmd = argv[optind];
|
||||
if (!strchr(cmd, '/')) {
|
||||
if (!(cmd = commandv(cmd, ccpath))) exit(127);
|
||||
}
|
||||
|
||||
ccversion = atoi(firstnonnull(emptytonull(getenv("CCVERSION")), "4"));
|
||||
isgcc = !!strstr(basename(cc), "gcc");
|
||||
isclang = !!strstr(basename(cc), "clang");
|
||||
isgcc = !!strstr(basename(cmd), "gcc");
|
||||
isclang = !!strstr(basename(cmd), "clang");
|
||||
iscc = isgcc | isclang;
|
||||
|
||||
for (i = 1; i < argc; ++i) {
|
||||
/*
|
||||
* ingest flag arguments
|
||||
*/
|
||||
for (i = optind; i < argc; ++i) {
|
||||
if (argv[i][0] != '-') {
|
||||
AddFlag(argv[i]);
|
||||
continue;
|
||||
|
@ -297,7 +376,13 @@ int main(int argc, char *argv[]) {
|
|||
AddFlag(argv[i]);
|
||||
}
|
||||
}
|
||||
if (!outpath) {
|
||||
outpath = target;
|
||||
}
|
||||
|
||||
/*
|
||||
* append special flags
|
||||
*/
|
||||
if (iscc) {
|
||||
if (isclang) {
|
||||
/* AddFlag("-fno-integrated-as"); */
|
||||
|
@ -337,8 +422,14 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* terminate argument list passed to subprocess
|
||||
*/
|
||||
AddFlag(NULL);
|
||||
|
||||
/*
|
||||
* ensure output directory exists
|
||||
*/
|
||||
if (outpath) {
|
||||
outdir = xdirname(outpath);
|
||||
if (!isdirectory(outdir)) {
|
||||
|
@ -346,23 +437,100 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
}
|
||||
|
||||
write(2, command.p, command.n);
|
||||
/*
|
||||
* log command being run
|
||||
*/
|
||||
if (!strcmp(nulltoempty(getenv("V")), "0") && !IsTerminalInarticulate()) {
|
||||
p = xasprintf("\e[F\e[K%-15s%s\r\n", firstnonnull(action, "BUILD"),
|
||||
firstnonnull(target, nulltoempty(outpath)));
|
||||
n = strlen(p);
|
||||
} else {
|
||||
if (IsTerminalInarticulate() &&
|
||||
(columns = atoi(nulltoempty(getenv("COLUMNS")))) > 25 &&
|
||||
command.n > columns + 2) {
|
||||
/* emacs command window is very slow so truncate lines */
|
||||
command.n = columns + 2;
|
||||
command.p[command.n - 5] = '.';
|
||||
command.p[command.n - 4] = '.';
|
||||
command.p[command.n - 3] = '.';
|
||||
command.p[command.n - 2] = '\r';
|
||||
command.p[command.n - 1] = '\n';
|
||||
}
|
||||
p = command.p;
|
||||
n = command.n;
|
||||
}
|
||||
write(2, p, n);
|
||||
|
||||
/*
|
||||
* create temporary copy when launching APE binaries
|
||||
*/
|
||||
if (!IsWindows() && endswith(cmd, ".com")) {
|
||||
cachedcmd = xasprintf("o/%s", cmd);
|
||||
if (fileexists(cachedcmd)) {
|
||||
cmd = cachedcmd;
|
||||
} else {
|
||||
if (startswith(cmd, "o/")) {
|
||||
cachedcmd = NULL;
|
||||
}
|
||||
originalcmd = cmd;
|
||||
cmd = xasprintf("%s.tmp.%d", originalcmd, getpid());
|
||||
copyfile(originalcmd, cmd, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* launch command
|
||||
*/
|
||||
sigfillset(&mask);
|
||||
sigprocmask(SIG_BLOCK, &mask, &savemask);
|
||||
if ((pid = vfork()) == -1) exit(errno);
|
||||
if (!pid) {
|
||||
sigprocmask(SIG_SETMASK, &savemask, NULL);
|
||||
execv(cc, flags.p);
|
||||
_exit(127);
|
||||
}
|
||||
while (waitpid(pid, &ws, 0) == -1) {
|
||||
if (errno != EINTR) exit(errno);
|
||||
ws = Launch();
|
||||
|
||||
/*
|
||||
* if execve() failed unzip gcc and try again
|
||||
*/
|
||||
if (WIFEXITED(ws) && WEXITSTATUS(ws) == 127 &&
|
||||
startswith(cmd, "o/third_party/gcc") &&
|
||||
fileexists("third_party/gcc/unbundle.sh")) {
|
||||
system("third_party/gcc/unbundle.sh");
|
||||
ws = Launch();
|
||||
}
|
||||
|
||||
if (WIFEXITED(ws)) {
|
||||
return WEXITSTATUS(ws);
|
||||
} else {
|
||||
return 128 + WTERMSIG(ws);
|
||||
/*
|
||||
* cleanup temporary copy of ape executable
|
||||
*/
|
||||
if (originalcmd) {
|
||||
if (cachedcmd && WIFEXITED(ws) && !WEXITSTATUS(ws)) {
|
||||
makedirs(xdirname(cachedcmd), 0755);
|
||||
rename(cmd, cachedcmd);
|
||||
} else {
|
||||
unlink(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* propagate exit
|
||||
*/
|
||||
if (WIFEXITED(ws)) {
|
||||
if (!WEXITSTATUS(ws)) {
|
||||
if (touchtarget && target) {
|
||||
makedirs(xdirname(target), 0755);
|
||||
touch(target, 0644);
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
p = xasprintf("%s%s EXITED WITH %d%s: %.*s\r\n", RED2, DescribeCommand(),
|
||||
WEXITSTATUS(ws), RESET, command.n, command.p);
|
||||
rc = WEXITSTATUS(ws);
|
||||
}
|
||||
} else {
|
||||
p = xasprintf("%s%s TERMINATED BY %s%s: %.*s\r\n", RED2, DescribeCommand(),
|
||||
strsignal(WTERMSIG(ws)), RESET, command.n, command.p);
|
||||
rc = 128 + WTERMSIG(ws);
|
||||
}
|
||||
|
||||
/*
|
||||
* print full command in the event of error
|
||||
*/
|
||||
write(2, p, strlen(p));
|
||||
return rc;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue