#!/usr/local/bin/icmake -qt /tmp/lib

// CLASSES: the list of directories containing the sources of classes. E.g.,
//      #define CLASSES "class1 class2"

string
    CLASSES;

void setclasses()
{
    CLASSES =   "error mem log config dbase parser startup ttyline "
                "dgroup dest dgroup user dname process "
            ;
}


/*                              build

    This icmake script will create the librss.a library, containing
    rss-functions for the callback programs

Configurable defines for the build script:

    BIFLEX:         only to be defined if the biflex-script will be used.
    CLASSES:        string of directory-names under which sources of classes
                    are found
    COMPILER:       "g++" for C++ sources. Do not change this, unless you're
                    sure you want something else.
    COPT:           C-options used by COMPILER
    ECHO_REQUEST:   ON (default) if command echoing is wanted, otherwise: set 
                    to OFF
    GDB:            define if gdb-symbolic debug information is wanted
                    (not defined by default)
    LIBS:           Extra libraries used for linking
    LIBPATH:        Extra library-searchpaths used for linking
    INTERACTIVE:    define only if, in the context of BIFLEX, an interactive
                    scanner is wanted
    NO_LIBRARY:     define this if you don't want a library for the object
                    modules, but want to keep them in separate ./o directories
		    by default: not defined, so a library is used.
    PROGRAM:        define if a program is to be built. If not defined,
                    library maintenance is assumed.
                    (default: defined)
                    
    Current values:
*/

#define BIFLEX

//Do not change the next define unless you know what you're doing:
#define COMPILER "gcc"

// COPT: the set of C-options
#define COPT "-m486 -Wall -funsigned-char"

#define ECHO_REQUEST ON

// #define GDB
// #define INTERACTIVE

// Extra libraries required. Remove lib and .a from the library names.
// E.g., #define LIBS "m Xt" to link libm.a and libXt.a explicitly
// Specify libs from the most specific to the most general one.
#define LIBS ""

// Extra library-paths required. 
// E.g., #define LIBPATH "/some/path /some/other/path" to search these paths
// apart from the default paths
#define LIBPATH ""

// #define NO_LIBRARY
// #define PROGRAM


//      DO NOT ALTER THINGS BELOW THIS LINE
string                                      // contain options for
    libs,	                            // extra libs, e.g., "-lrss -licce"
    libpath,			   	    // extra lib-paths, eg, "-L../rss"
    copt,
    lopt,
    libxxxa;				    // expanded lib-name
int
    relink;                                 // internally used: != 0 to relink

string
    ofiles,                             // wildcards for o-files
    sources,				// sources to be used
    wild,                               // wildcard of extension
    current;                            // contains name of current dir.
/*
                                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
#ifdef NO_LIBRARY
        objfile = "./o/" + change_ext(file, "o");    // make obj-filename
#else
        objfile = change_ext(file, "o");    // make obj-filename
#endif
        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);
}
/*
                                 biflex.im
*/             

void biflex()
{
    chdir("biflex");
    
    if ("parser" younger "parser.tab.c")        // new parser needed
    {
        exec("bison", "-d", "parser");
        printf("Note: the compilation of parser.c may produce "
	       "several compiler warnings.\n");
    }
        
    if 
    (                                           // new lexer needed
        "lexer" younger "lex.yy.c"
        ||
        "parser.tab.h" younger "lex.yy.c" 
    )
    {
        exec("flex",
#ifdef INTERACTIVE
                    "-I", 
#endif
                    "lexer");
                    
        printf("Note: the compilation of lexer.c may produce "
	       "several compiler warnings.\n");
    }
                    
    chdir("..");
}

/*
                                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
#ifndef NO_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, string exe)
{
    if
    (
        relink                           // new library, new main.obj
        ||
        !exists(exe)                     // final program doesn't exist
    )
    {
        printf("\n");
        exec(COMPILER, "-o", exe, 
#ifdef NO_LIBRARY
            ofiles,
#else
	    "-l" + library, 
#endif
	    libs, "-L.", libpath, lopt);
#ifndef GDB
        exec("strip", exe);
#endif
        printf("ok: ", exe, "\n");
    }
}
/*
                            C C O M P I L E . I M
*/

void c_compile(list cfiles)
{
	string
		nextfile;
	int
		i;
                
#ifdef NO_LIBRARY
    if (!exists("o"))
        system("mkdir o");
    
    chdir ("o");    
#endif
                                                      
    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 "
		COPT + " " +
#ifdef NO_LIBRARY
		copt + " ../" + nextfile);
#else
		copt + " " + nextfile);
#endif

       	relink = 1;
	printf("\n");
    }

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

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

    objlist = makelist("*.o");

    if (!sizeof(objlist))
        return;

    printf("\n");
    relink = 1;

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

    printf("\n");
}

void prefix_class(string class_id)
{
    list
	o_files;
    string
	o_file;
    int
	i;

    o_files = makelist("*.o");

    for (i = 0; o_file = element(i, o_files); i++)
	exec("mv", o_file, class_id + o_file);
}
/*
                                S T D C P P . I M
*/

void std_cpp(string library)
{
    list
        cfiles;

#ifdef BIFLEX
    if (exists("biflex"))                  // subdir biflex exists
        biflex();                          // make recent files
#endif                       

    cfiles = file_list(wild, 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, .a suffix
				 if main is given here, libmain.a is created)
        string exe,         : (path) name of the exe file to create
        )

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

void cpp_make(string mainfile, string library, string exe)
{
    int
        n,
        index;
    list
        classes;
        
    ofiles = "o/*.o";                       // std set of o-files

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

    if (n = sizeof(classes))
        ofiles += " */o/*.o";               // set ofiles for NO_LIBRARY

    wild = sources;
					    // make library name
    libxxxa = chdir(".") + "lib" + library + ".a";

                                            // first process all classes
    for (index = 0; index < n; index++)
    {                   
        current = element(index, classes);  // next class to process
        chdir(current);                     // change to directory

        current = "subdir " + current;
        std_cpp (libxxxa);                // compile all files
        chdir( "..");                     // go back to parent dir
    }


    current = "auxiliary " + wild + " files";
    std_cpp (libxxxa);                    // compile all files in current dir
    
    for (index = 0; index < n; index++)
    {
        current = element(index, classes);  // determine class name
        chdir( current);                  // chdir to a class directory.
#ifndef NO_LIBRARY
	prefix_class((string)index);	  // prefix class-number for .o files
        updatelib(libxxxa);
#endif
        chdir( "..");                     // go back to parent dir
    }

    current = "";                           // no class anymore

#ifndef NO_LIBRARY
    updatelib(libxxxa);			    // update lib in current dir
#endif 

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


int main()
{
    setclasses();

    echo(ECHO_REQUEST);

    sources = "*.c";

    setlibs();

#ifdef GDB
    copt = "-g";
#else
    copt = "-O2";
#endif

#ifdef PROGRAM
    cpp_make
    (
        "lib.cc",          // program source
        "lib",                    // program library
        "lib"                     // binary program
    );
#else
    cpp_make
    (
        "",
        "rss",                   // program library
        ""
    );
#endif
    return (0);
}
