#!/usr/bin/icmake -qt/tmp/driver


string
    CLASSES;

void setClasses()
{
//  ADD ADDITIONAL DIRECTORIES CONTAINING SOURCES OF CLASSES HERE
//  Use the construction `CLASSES += "classname1 classname2";' etc.

    CLASSES += " ";
}

//#define BUILD_LIBRARY
#define BUILD_PROGRAM      "driver"

#define COMPILER           "g++"
#define COPT               "-Wall -I../../inc"
#define LOPT               ""

#define ECHO_REQUEST       1
//#define GDB              "-g"

#define LIBS               "bobcat"
#define LIBPATH            "../../lib"

// local namespace is: FBB
// using-declarations generated for: std:FBB

// qt-mt can be used to select the threaded QT library
//#define QT               "qt"

//                      NO CONFIGURABLE PARTS BELOW THIS LINE

/*
                            V A R S . I M
*/

string                  // contain options for
    cwd,                // current WD
    libs,               // extra libs, e.g., "-lrss -licce"
    libpath,            // extra lib-paths, eg, "-L../rss"
    copt,               // Compiler options
    lopt,               // Linker options
    libxxx,             // full library-path
    ofiles,             // wildcards for o-files
    sources,            // sources to be used
    current,            // contains name of current dir.
    programname;        // the name of the program to create
int
    nClasses,           // number of classes/subdirectories
    program;            // 1: program is built
list
    classes;            // list of classes/directories
/*
                                 parser.im
*/

void parser()
{
    #ifdef GRAMBUILD
        chdir("parser/gramspec");
        #ifdef GRAMMAR_LINES
            system("./grambuild lines");
        #else
            system("./grambuild");
        #endif
        chdir("..");

        if
        (
            exists("grammar")
            &&
            "grammar" younger "parse.cc"
        )                                   // new parser needed
        #ifdef BISON_FLAGS
            exec("bisonc++", BISON_FLAGS, "grammar");
        #else
            exec("bisonc++", "grammar");
        #endif

        chdir("..");
    #endif
}
/*
                                 S C A N N E R . I M
*/
void scanner()
{
    string
        interactive;

    #ifdef INTERACTIVE
        interactive = "-I";
    #endif

    #ifdef GRAMBUILD
        chdir("scanner");
        if
        (                                           // new lexer needed
            exists("lexer")
            &&
            (
                "lexer" younger "yylex.cc"
                ||
                (
                    exists("../parser/parser.h")
                    &&
                    "../parser/parser.h" younger "yylex.cc"
                )
            )
        )
            exec("flex", interactive, "lexer");

        chdir("..");
    #endif
}

/*
                                I N I T I A L . I M
*/
void initialize()
{
    echo(ECHO_REQUEST);
    sources = "*.cc";
    ofiles = "o/*.o";                       // std set of o-files
    copt = COPT;

    #ifdef GDB
        copt += " " + GDB;
    #endif

    #ifdef BUILD_PROGRAM
        program = 1;
        programname = BUILD_PROGRAM;
    #else
        program = 0;
    #endif;

    cwd = chdir(".");

    #ifdef GRAMBUILD
        if (exists("parser"))                  // subdir parser exists
        {
            CLASSES += "parser ";
            parser();
        }
        if (exists("scanner"))                  // subdir scanner exists
        {
            CLASSES += "scanner ";
            scanner();
        }
    #endif

    setClasses();                           // remaining classes

    classes = strtok(CLASSES, " ");         // list of classes

    nClasses = sizeof(classes);
}


/*
                                O B J F I L E S . I M
*/

list objfiles(list files)
{
    string
        file,
        objfile;
    int
        i;

    for (i = 0; i < sizeof(files); i++)
    {
        file = element(i, files);           // determine element of the list
        objfile = "./o/" + change_ext(file, "o");    // make obj-filename
        if (objfile younger file)           // objfile is younger
        {
            files -= (list)file;            // remove the file from the list
            i--;                            // reduce i to test the next
        }
    }
    return (files);
}
/*
                                A L T E R E D . I M
*/

list altered(list files, string target)
{
    int
        i;
    string
        file;

    for (i = 0; i < sizeof(files); i++)     // try all elements of the list
    {
        file = element(i, files);           // use element i of the list

        if (file older target)              // a file is older than the target
        {
            files -= (list)file;            // remove the file from the list
            i--;                            // reduce i to inspect the next
        }                                   // file of the list
    }
    return (files);                         // return the new list
}
/*
                            F I L E L I S T . I M
*/

list file_list(string type, string library)
{
    list
        files;

    files = makelist(type);                 // make all files of certain type
    #ifdef BUILD_LIBRARY
        files = altered(files, library);    // keep all files newer than lib.
    #endif
    files = objfiles(files);                // remove if younger .obj exist

    return (files);
}
/*
                        L I N K . I M
*/

void link(string library)
{
    printf("\n");
    system("mv ../../lib/libbobcat.so ../../lib/libbobcat.OFF");
    exec(COMPILER, "-o", programname,
             ofiles,
         libs,
         "-L.", libpath, lopt
         #ifndef GDB
            , "-s"
        #endif
    );
    system("mv ../../lib/libbobcat.OFF ../../lib/libbobcat.so");
    printf("ok: ", programname, "\n");
}
/*
                          P R E F I X C L . I M
*/
void prefix_class(string class_id)
{
    list
        o_files;
    string
        o_file;
    int
        i;

    chdir("o");
    o_files = makelist("*.o");
    for (i = 0; o_file = element(i, o_files); i++)
        exec("mv", o_file, class_id + o_file);
    chdir("..");
}
/*
                          R M C L A S S P . I M
*/

#ifdef BUILD_LIBRARY
    string rm_class_id(string class_id, string ofile)
    {
        string
            ret;
        int
            index,
            n;

        n = strlen(ofile);
        for (index = strlen(class_id); index < n; index++)
            ret += element(index, ofile);

        return ret;
    }
#endif

void rm_class_prefix(string class_id)
{
    #ifdef BUILD_LIBRARY
        list
            o_files;
        string
            o_file;
        int
            i;

        chdir("o");
        o_files = makelist("*.o");
        for (i = 0; o_file = element(i, o_files); i++)
            exec("mv", o_file, rm_class_id(class_id, o_file));
        chdir("..");
    #endif
}
/*
                            C C O M P I L E . I M
*/

void c_compile(list cfiles)
{
        string
                nextfile;
        int
                i;

    if (!exists("o"))
        system("mkdir o");

    if (sizeof(cfiles))                 // files to compile ?
    {
        printf("\ncompiling: ", current, "\n\n");

                                        // compile all files separately
        for (i = 0; nextfile = element(i, cfiles); i++)
            exec(COMPILER,
                "-c -o o/" + change_ext(nextfile, "o"),
                copt, nextfile);

        printf("\n");
    }
    printf("ok: ", current, "\n");
}
/*
                            U P D A T E L I . I M
*/

void updatelib(string library)
{
    #ifdef BUILD_LIBRARY
        list
            arlist,
            objlist;
        string
            to,
            from;

        objlist = makelist("o/*.o");

        if (!sizeof(objlist))
            return;

        printf("\n");

        exec("ar", "rvs", library, "o/*.o");
        exec("rm", "o/*.o");

        printf("\n");
    #endif
}
/*
                                S T D C P P . I M
*/

void std_cpp(string library)
{
    list
        cfiles;

    cfiles = file_list(sources, library);     // make list of all cpp-files

    c_compile(cfiles);                      // compile cpp-files
}
/*
                                C P P M A K E . C

    CPP files are processed by stdmake.

    Arguments of CPPMAKE:

    cpp_make(
        string mainfile,    : name of the main .cpp file, or "" for library
                              maintenance
        string library,     : name of the local library to use/create
                                (without lib prefix or .a/.so suffix
                                (E.g., use `main' for `libmain.a')
        )

    Both mainfile and library MUST be in the current directory
*/

void cpp_make(string mainfile, string library)
{
    int
        index;
    string class;

    if (nClasses)
        ofiles += " */o/*.o";               // set ofiles for no LIBRARY use

                                            // make library name
    #ifdef BUILD_LIBRARY
        libxxx = chdir(".") + "lib" + library + ".a";
    #endif
                                            // first process all classes
    for (index = 0; index < nClasses; index++)
    {
        class = element(index, classes);  // next class to process
        chdir(class);                     // change to directory

        current = "subdir " + class;
        #ifdef QT
            moc(class);                     // see if we should call moc
        #endif
        std_cpp(libxxx);                    // compile all files
        chdir(cwd);                         // go back to parent dir
    }

    current = "auxiliary " + sources + " files";
    std_cpp(libxxx);                        // compile all files in current dir


    #ifdef BUILD_LIBRARY
                                        // prefix class-number for .o files
        for (index = 0; index < nClasses; index++)
        {
            current = element(index, classes);  // determine class name
            chdir( current);              // chdir to a class directory.
            prefix_class((string)index);
            updatelib(libxxx);
            chdir(cwd);                // go back to parent dir
        }
        current = "";                  // no class anymore
        updatelib(libxxx);             // update lib in current dir
    #endif

    if (mainfile != "")                // mainfile -> do link
    {
        link(library);
        printf
        (
            "\nProgram construction completed.\n"
            "\n"
        );
    }
}
/*
                        S E T L I B S . I M
*/
void setlibs()
{
    #ifdef LIBS
        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);
    #endif
}

void main()
{
    initialize();
    setlibs();

    #ifdef BUILD_PROGRAM
        cpp_make
        (
            "driver.cc", // program source
            "driver"  // static program library
        );
    #else
        cpp_make
        (
            "",
            "driver"  // static- or so-library
        );
    #endif
}
