shithub: mc

ref: 220525edb769047fcba3faed66b041c97a752b70
dir: /myrbuild/myrbuild.c/

View raw version
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <inttypes.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/utsname.h>
#include <ctype.h>
#include <fcntl.h>
#include <unistd.h>

#include "parse.h"

#include "../config.h"

/* make libparse happy */
Node *file;
char *filename;

/* options to pass along to the compiler */
int genasm = 0;

/* binaries we call out to */
char *mc = "6m";
char *muse = "muse";
char *as[] = Asmcmd;
char *ar[] = Arcmd;
char *ld[] = Ldcmd;
char *runtime = Instroot "/lib/myr/_myrrt" Objsuffix;
/* the name of the output file */
char *libname;
char *binname;
/* additional paths to search for packages */
char **incpaths;
size_t nincpaths;
/* libraries to link against, and their deps */
Htab *libgraph;  /* string -> null terminated string list */
/* the linker script to use */
char *ldscript;

char *sysname;

Htab *compiled; /* used as string set */
Htab *loopdetect; /* used as string set */

static void fail(int status, char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    vfprintf(stderr, fmt, ap);
    va_end(ap);
    exit(status);
}

static void usage(char *prog)
{
    printf("%s [-h] [-I path] [-l lib] [-b bin] inputs...\n", prog);
    printf("\t-h\tprint this help\n");
    printf("\t-b bin\tBuild a binary called 'bin'\n");
    printf("\t-l lib\tBuild a library called 'name'\n");
    printf("\t-s script\tUse the linker script 'script' when linking\n");
    printf("\t-I path\tAdd 'path' to use search path\n");
    printf("\t-S\tGenerate assembly files for all compiled code\n");
}

int hassuffix(char *path, char *suffix)
{
    int pathlen;
    int sufflen;

    pathlen = strlen(path);
    sufflen = strlen(suffix);

    if (sufflen > pathlen)
        return 0;
    return !strcmp(&path[pathlen-sufflen], suffix);
}

int isquoted(char *path)
{
    return path[0] == '"' && path[strlen(path) - 1] == '"';
}

char *usetomyr(char *path)
{
    char buf[1024];
    /* skip initial quote */
    path++;
    if (!hassuffix(path, ".use\"")) {
        fprintf(stderr, "\"%s, should end with \".use\"\n", path);
        exit(1);
    }
    swapsuffix(buf, 1024, path, ".use\"", ".myr");
    return strdup(buf);
}

void printl(char **lst)
{
    printf("\t");
    printf("%s\t", *lst++);
    while (*lst)
        printf("%s ", *lst++);
    printf("\n");
}

void gencmd(char ***cmd, size_t *ncmd, char *bin, char *file, char **extra, size_t nextra)
{
    size_t i;

    *cmd = NULL;
    *ncmd = 0;
    lappend(cmd, ncmd, bin);
    for (i = 0; i < nincpaths; i++) {
        lappend(cmd, ncmd, "-I");
        lappend(cmd, ncmd, incpaths[i]);
    }
    for (i = 0; i < nextra; i++)
        lappend(cmd, ncmd, extra[i]);
    lappend(cmd, ncmd, file);
    lappend(cmd, ncmd, NULL); 
}

void run(char **cmd)
{
    pid_t pid;
    int status;

    printl(cmd);
    pid = fork();
    status = 0;
    if (pid == -1) {
        fail(1, "Could not fork\n");
    } else if (pid == 0) {
        if (execvp(cmd[0], cmd) == -1)
            fail(1, "Failed to exec %s\n", cmd[0]);
    } else {
        waitpid(pid, &status, 0);
    }
    if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
        exit(WEXITSTATUS(status));
    else if (WIFSIGNALED(status))
        die("%s: exited with signal %d\n", cmd[0], WTERMSIG(status));
}

int isfresh(char *from, char *to)
{
    struct stat from_sb, to_sb;

    if (stat(from, &from_sb))
        fail(1, "Could not find %s\n", from);
    if (stat(to, &to_sb) == -1)
        return 0;

    return from_sb.st_mtime <= to_sb.st_mtime;
}

int inlist(char **list, size_t sz, char *str)
{
    size_t i;

    for (i = 0; i < sz; i++)
        if (!strcmp(list[i], str))
            return 1;
    return 0;
}

int finddep(char *buf, char **dep)
{
    char *end, *w, *p;
    p = buf;


    end = buf + strlen(buf);
    while (isspace(*p) && p != end)
        p++;
    if (strncmp(p, "use", 3) != 0)
        return 0;
    p += 3;
    if (!isspace(*p))
        return 0;
    while (isspace(*p) && *p != ';' && *p != '"' && p != end)
        p++;

    w = p;
    while (!isspace(*p) && p != end)
        p++;
    if (p == w)
        return 0;
    *dep = strdupn(w, p - w);
    return 1;
}

void getdeps(char *file, char **deps, size_t depsz, size_t *ndeps)
{
    char buf[2048];

    size_t i;
    FILE *f;
    char *dep;

    f = fopen(file, "r");
    if (!f)
        fail(1, "Could not open file \"%s\"\n", file);

    i = 0;
    while (fgets(buf, sizeof buf, f)) {
        if (!finddep(buf, &dep))
            continue;
        if (!inlist(deps, i, dep))
            deps[i++] = dep;
        else
            free(dep);
    }
    fclose(f);
    *ndeps = i;
}

FILE *openlib(char *lib)
{
    FILE *f;
    char buf[1024];
    size_t i;

    for (i = 0; i < nincpaths; i++) {
        snprintf(buf, sizeof buf, "%s/%s", incpaths[i], lib);
        f = fopen(buf, "r");
        if (f)
            return f;
    }
    snprintf(buf, sizeof buf, "%s/%s", Instroot "/lib/myr", lib);
    f = fopen(buf, "r");
    if (f)
        return f;
    fail(1, "could not open library file %s\n", lib);
    return NULL;
}

void scrapelib(Htab *g, char *lib)
{
    char **deps;
    size_t ndeps;
    FILE *use;
    char *l;

    if (hthas(libgraph, lib))
        return;
    deps = NULL;
    ndeps = 0;
    use = openlib(lib);
    if (fgetc(use) != 'U')
        fail(1, "library \"%s\" is not a usefile.\n", lib);
    /* we don't care about the usefile's name */
    free(rdstr(use));
    while (fgetc(use) == 'L') {
        l = rdstr(use);
        lappend(&deps, &ndeps, l);
        scrapelib(g, l);
    }
    lappend(&deps, &ndeps, NULL);
    htput(g, lib, deps);
}

void compile(char *file, char ***stack, size_t *nstack)
{
    size_t i, ndeps;
    char **cmd;
    size_t ncmd;
    char *s;
    char *localdep;
    char *deps[512];
    char use[1024];
    char obj[1024];
    char *extra[32];
    size_t nextra = 0;

    if (hthas(compiled, file))
        return;
    if (hthas(loopdetect, file)) {
        fprintf(stderr, "Cycle in dependency graph, involving %s. dependency stack:\n", file);
        for (i = 0; i < *nstack; i++)
            fprintf(stderr, "\t%s\n", (*stack)[i]);
        exit(1);
    }
    htput(loopdetect, file, file);
    if (hassuffix(file, ".myr")) {
        swapsuffix(use, sizeof use, file, ".myr", ".use");
        swapsuffix(obj, sizeof obj, file, ".myr", Objsuffix);
        getdeps(file, deps, 512, &ndeps);
        for (i = 0; i < ndeps; i++) {
            if (isquoted(deps[i])) {
                localdep = usetomyr(deps[i]);
                lappend(stack, nstack, localdep);
                compile(localdep, stack, nstack);
                lpop(stack, nstack);
                free(localdep);
            } else {
                scrapelib(libgraph, deps[i]);
            }
        }
        if (isfresh(file, use))
            goto done;
        if (isfresh(file, obj))
            goto done;
        if (genasm)
            extra[nextra++] = "-S";
        gencmd(&cmd, &ncmd, mc, file, extra, nextra);
        run(cmd);
    } else if (hassuffix(file, ".s")) {
        swapsuffix(obj, sizeof obj, file, ".s", Objsuffix);
        if (isfresh(file, obj))
            goto done;
        for (i = 1; as[i]; i++)
            extra[nextra++] = as[i];
        extra[nextra++] = obj;
        gencmd(&cmd, &ncmd, as[0], file, extra, nextra);
        run(cmd);
    }
done:
    s = strdup(file);
    htput(compiled, s, s);
    htdel(loopdetect, file);
}


void mergeuse(char **files, size_t nfiles)
{
    char **args;
    size_t i, nargs;
    char buf[1024];

    args = NULL;
    nargs = 0;
    lappend(&args, &nargs, strdup(muse));
    lappend(&args, &nargs, strdup("-o"));
    lappend(&args, &nargs, strdup(libname));
    for (i = 0; i < nfiles; i++) {
        if (hassuffix(files[i], ".myr")) {
            swapsuffix(buf, sizeof buf, files[i], ".myr", ".use");
            lappend(&args, &nargs, strdup(buf));
        } else if (!hassuffix(files[i], ".s")) {
            die("Unknown file type %s", files[i]);
        }
    }
    lappend(&args, &nargs, NULL);

    run(args);

    for (i = 0; i < nargs; i++)
        free(args[i]);
    lfree(&args, &nargs);
}

void archive(char **files, size_t nfiles)
{
    char **args;
    size_t i, nargs;
    char buf[1024];

    args = NULL;
    nargs = 0;
    snprintf(buf, sizeof buf, "lib%s.a", libname);
    for (i = 0; ar[i]; i++)
         lappend(&args, &nargs, strdup(ar[i]));
    lappend(&args, &nargs, strdup(buf));
    for (i = 0; i < nfiles; i++) {
        if (hassuffix(files[i], ".myr"))
            swapsuffix(buf, sizeof buf, files[i], ".myr", Objsuffix);
        else if (hassuffix(files[i], ".s"))
            swapsuffix(buf, sizeof buf, files[i], ".s", Objsuffix);
        else
            die("Unknown file type %s", files[i]);
        lappend(&args, &nargs, strdup(buf));
    }
    lappend(&args, &nargs, NULL);

    run(args);

    for (i = 0; i < nargs; i++)
        free(args[i]);
    lfree(&args, &nargs);
}

void findlib(char *buf, size_t bufsz, char *lib)
{
    size_t i;

    for (i = 0; i < nincpaths; i++) {
        snprintf(buf, bufsz, "%s/lib%s.a", incpaths[i], lib);
        if (access(buf, F_OK) == 0)
            return;
    }
    snprintf(buf, bufsz, "%s/lib/myr/lib%s.a", Instroot, lib);
    if (access(buf, F_OK) == 0)
        return;
    fail(1, "unable to find library lib%s.a\n", lib);
}

void visit(char ***args, size_t *nargs, size_t head, Htab *g, char *n, Htab *looped, Htab *marked)
{
    char **deps;
    char buf[1024];

    if (hthas(looped, n))
        fail(1, "cycle in library dependency graph involving %s\n", n);
    if (hthas(marked, n))
        return;
    htput(looped, n, n);
    for (deps = htget(g, n); *deps; deps++)
        visit(args, nargs, head, g, *deps, looped, marked);
    htdel(looped, n);
    htput(marked, n, n);
    if (!strcmp(sysname, "Plan9"))
        findlib(buf, sizeof buf, n);
    else
        snprintf(buf, sizeof buf, "-l%s", n);
    linsert(args, nargs, head, strdup(buf));
}

/* topologically sorts the dependency graph of the libraries. */
void addlibs(char ***args, size_t *nargs, Htab *g)
{
    void **libs;
    size_t nlibs;
    size_t i;
    size_t head;
    Htab *looped;
    Htab *marked;

    libs = htkeys(g, &nlibs);
    looped = mkht(strhash, streq);
    marked = mkht(strhash, streq);
    head = *nargs;
    for (i = 0; i < nlibs; i++)
        visit(args, nargs, head, g, libs[i], looped, marked);
}

void linkobj(char **files, size_t nfiles)
{
    char **args;
    size_t i, nargs;
    char buf[1024];

    if (!binname)
        binname = "a.out";

    args = NULL;
    nargs = 0;

    /* ld -T ldscript -o outfile */
    for (i = 0; ld[i]; i++)
        lappend(&args, &nargs, strdup(ld[i]));
    lappend(&args, &nargs, strdup(binname));

    /* ld -T ldscript */
    if (ldscript) {
        snprintf(buf, sizeof buf, "-T%s", ldscript);
        lappend(&args, &nargs, strdup(buf));
    }

    if (runtime) {
        lappend(&args, &nargs, strdup(runtime));
    }

    /* ld -T ldscript -o outfile foo.o bar.o baz.o */
    for (i = 0; i < nfiles; i++) {
        if (hassuffix(files[i], ".myr"))
            swapsuffix(buf, sizeof buf, files[i], ".myr", Objsuffix);
        else if (hassuffix(files[i], ".s"))
            swapsuffix(buf, sizeof buf, files[i], ".s", Objsuffix);
        else
            die("Unknown file type %s", files[i]);
        lappend(&args, &nargs, strdup(buf));
    }

    /* ld -T ldscript -o outfile foo.o bar.o baz.o -L/path1 -L/path2 */
    if (strcmp(sysname, "Plan9") != 0) {
        for (i = 0; i < nincpaths; i++) {
            snprintf(buf, sizeof buf, "-L%s", incpaths[i]);
            lappend(&args, &nargs, strdup(buf));
        }
        snprintf(buf, sizeof buf, "-L%s%s", Instroot, "/lib/myr");
        lappend(&args, &nargs, strdup(buf));
    }

    /* ld -T ldscript -o outfile foo.o bar.o baz.o -L/path1 -L/path2 -llib1 -llib2*/
    addlibs(&args, &nargs, libgraph);

    /* OSX wants a minimum version specified to prevent warnings*/
    if (!strcmp(sysname, "Darwin")) {
        lappend(&args, &nargs, strdup("-macosx_version_min"));
        lappend(&args, &nargs, strdup("10.6"));
    }

    /* the null terminator for exec() */
    lappend(&args, &nargs, NULL);

    run(args);

    for (i = 0; i < nargs; i++)
        free(args[i]);
    lfree(&args, &nargs);
}

int main(int argc, char **argv)
{
    struct utsname name;
    char **stack;
    size_t nstack;
    Optctx ctx;
    size_t i;

    if (uname(&name) == 0)
        sysname = strdup(name.sysname);
    
    optinit(&ctx, "hb:l:s:r:SI:C:A:M:L:R:", argv, argc);
    while (!optdone(&ctx)) {
        switch (optnext(&ctx)) {
            case 'b': binname = ctx.optarg; break;
            case 'l': libname = ctx.optarg; break;
            case 's': ldscript = ctx.optarg; break;
            case 'S': genasm = 1; break;
            case 'C': mc = ctx.optarg; break;
            case 'M': muse = ctx.optarg; break;
            case 'A': as[0] = ctx.optarg; break;
            case 'L': ld[0] = ctx.optarg; break;
            case 'R': ar[0] = ctx.optarg; break;
            case 'r':
                      if (!strcmp(ctx.optarg, "none"))
                          runtime = NULL;
                      else
                          runtime = strdup(ctx.optarg);
                      break;
            case 'I':
                lappend(&incpaths, &nincpaths, strdup(ctx.optarg));
                break;
            case 'h':
                usage(argv[0]);
                exit(0);
                break;
            default:
                usage(argv[0]);
                exit(0);
                break;
        }
    }

    if (libname && binname)
        die("Can't specify both library and binary names");

    stack = NULL;
    nstack = 0;
    libgraph = mkht(strhash, streq);
    compiled = mkht(strhash, streq);
    loopdetect = mkht(strhash, streq);
    for (i = 0; i < ctx.nargs; i++) {
        lappend(&stack, &nstack, ctx.args[i]);
        compile(ctx.args[i], &stack, &nstack);
        lpop(&stack, &nstack);
    }
    if (libname) {
        mergeuse(ctx.args, ctx.nargs);
        archive(ctx.args, ctx.nargs);
    } else {
        linkobj(ctx.args, ctx.nargs);
    }

    return 0;
}