
#define ECHO_REQUEST       1

#define LIBS               "bobcat"
#define LIBPATH            ""


string                  // contain options for
    g_copt,             // compiler options
    g_cwd,              // current WD
    libs,               // extra libs, e.g., "-lrss -licce"
    libpath,            // extra lib-paths, eg, "-L../rss"
    g_sources,          // sources to be used
    g_binary;           // the name of the program to create
int
    g_nClasses;         // number of classes/subdirectories
list
    g_classes;          // list of classes/directories

string setOpt(string install_im, string envvar)
{
    list optvar;
    string ret;

    optvar = getenv(envvar);    

    if (optvar[0] == "1")
        ret = optvar[1];
    else 
        ret = install_im;

    return ret;
}

void setClasses()
{
    list candidate;
    list class;

    while (sizeof(class = fgets("CLASSES", (int)element(1, class))))
    {
        candidate = strtok(element(0, class), " \t\n");

        // if the line contains info not starting with #, add the class
        if (sizeof(candidate) && element(0, element(0, candidate)) != "#")
            g_classes += (list)element(0, strtok(element(0, class), " \t\n"));
    }

    g_nClasses = sizeof(g_classes);
}

void static_lib(string ofiles, string library)
{
    if (sizeof(makelist(ofiles)))
    {
        run("ar cru " + library + " " + ofiles);
        run("ranlib " + library);
        run("rm " + ofiles);
    }
}

void static_library(string library)
{
    static_lib("*/o/*.o", library);
    static_lib("o/*.o", library);
}

void scanner()
{
    int idx;
    list names;
    string file;

    chdir("scanner");
    names = makelist("inc/*");
    names += (list)"lexer";

    for (idx = sizeof(names); idx--; )
    {
        file = names[idx];

        if 
        (                                          // new lexer needed
            exists(file)
            &&
            file younger "yylex.cc"
        )
            break;
    }
    if (idx >= 0)
    {
        exec("./buildlexer");
        exec("flex", "lexer");
    }

    chdir("..");
}

/*
                                I N I T I A L . I M
*/
void initialize()
{
    echo(ECHO_REQUEST);
    g_sources = "*.cc";

    g_binary = "tmp/bin/bisonc++" EXTENSION;

    g_cwd = chdir(".");

    if (exists("scanner"))                  // subdir scanner exists
        scanner(); 
    
    setClasses();                           // remaining classes
}

/*
                        L I N K . I M
*/

list inspect(int prefix, list srcList, string library)
{
    int idx;
    string ofile;
    string oprefix;
    string file;

    oprefix = "./o/" + (string)prefix;

    for (idx = sizeof(srcList); idx--; )
    {
        file  = element(idx, srcList);   
        ofile   = oprefix + change_ext(file, "o");    // make o-filename

        // A file s must be recompiled if it's newer than its object
        // file o or newer than its target library l, or if neither o nor l
        // exist. 
        // Since `a newer b' is true if a is newer than b, or if a exists and
        // b doesn't exist s must be compiled if s newer o and s newer l.
        // So, it doesn't have to be compiled if s older o or s older l.
                                            // redo if file has changed
        if (file older ofile || file older library)
            srcList -= (list)file;
    }
    return srcList;
}


void c_compile(int prefix, string srcDir, list cfiles)
{
    int idx;
    string compiler;
    string file;

    compiler = COMPILER + " -Wall --std=c++0x -c -o " + 
                                srcDir + "/o/" + (string)prefix;

    md(srcDir + "/o");

    for (idx = sizeof(cfiles); idx--; )
    {
        file = element(idx, cfiles);
        
        run(compiler + change_ext(file, "o") + " " + 
                g_copt + " " + srcDir + "/" + file);
    }
}

void std_cpp(int prefix, string srcDir, string library)
{
    list files;

    chdir(srcDir);
                                                      // make list of all files
    files = inspect(prefix, makelist(g_sources), library);  
    chdir(g_cwd);

    if (sizeof(files))
        c_compile(prefix, srcDir, files);             // compile files
}

void cpp_make(string mainfile, string library, int lib_only)
{
    int idx;
    string class;
    string fullLibname;

    fullLibname = "lib" + library + ".a";

    for (idx = g_nClasses; idx--; )
        std_cpp(idx, element(idx, g_classes), "../" + fullLibname);

    if (!lib_only)                          // compile all files in current dir
        std_cpp(g_nClasses, ".", fullLibname);  

    static_library(fullLibname);            // make the library
}

void setlibs()
{
        int
            n,
            index;
        list
            cut;
            
        cut = strtok(LIBS, " ");        // cut op libraries
        n = sizeof(cut);
        for (index = 0; index < n; index++)
            libs += " -l" + element(index, cut);

        cut = strtok(LIBPATH, " ");     // cut up the paths
        n = sizeof(cut);
        for (index = 0; index < n; index++)
            libpath += " -L" + element(index, cut);
}

void library(int lib_only)
{
    initialize();
    setlibs();
    
    md("tmp/bin tmp/o");

    special();

    g_copt = setOpt(CXXFLAGS, "CXXFLAGS");

    cpp_make
    (
        "bisonc++.cc",      // program source
        "bisonc++",         // static program library base name
        lib_only            // don't compile maindir for lib construction
    );

    if (lib_only)
        exit(0);
}
