#!/usr/bin/python

import sys
import os
import copy
import xml.etree.ElementTree as etree
import uuid
import getopt
import zipfile
import shutil

EXT_MAPPING = { ".c": "c", ".cc": "cpp", ".cpp": "cpp", ".h": "h", ".s": "s"}

def parseError(str):
    print("*** error: \""+str+"\"\n");

def platformCompare(a, b):
    return cmp(a.count('*'), b.count('*'))


def platformSubseteqTest(a, b):
    a4=a.split("-")
    b4=b.split("-")
    ret=len(a4)==len(b4)
    i=0
    while i<len(a4) and i<len(b4):        
        ret=ret and (a4[i]==b4[i] or b4[i]=="*")
        i=i+1
#    sys.stderr.write("platformSubseteqTest("+a+", "+b+")="+str(ret))
    return ret

def parseFile(node, ctx, list):
    path=node.attrib["path"]
    abs_path=os.path.join(ctx["base"], ctx["root"], path)
#    sys.stderr.write "path="+abs_path
    filterMatch=True
    if "platform" in node.attrib:
        filter=node.attrib["platform"]
        filterMatch=platformSubseteqTest(ctx["platform"], filter)
        
    if filterMatch:
        file_ext=os.path.splitext(path)[1]
        if file_ext in EXT_MAPPING:
            ext=EXT_MAPPING[file_ext]
            if os.path.exists(abs_path):
                install=None
                if "install" in node.attrib and node.attrib["install"]=="yes":
                    install=os.path.dirname(os.path.relpath(abs_path, ctx["root"]))
##                    sys.stderr.write install
                file_item={ "path": abs_path, "ext": ext, "install": install }
                if "x" in node.attrib:
                    file_item["x"]=node.attrib["x"]
                list.append(file_item)
            else:
                parseError("file "+abs_path+" does not exist!")
        else:
            parseError("extension "+file_ext+" is unknown")

def parseFiles(target, node, ctx):
    for child in list(node):
        if child.tag=="file":
            parseFile(child, ctx, target)
        else:
            parseError("unhandled element: "+child.tag)

def setAttr(dict, node, tag):
    if tag in node.attrib:
        dict[tag]=node.attrib[tag]        

def fallbackCompare(a, b):
    return -cmp(a.count('-'), b.count('-'))


def generateFallbackList(platform):
    ret=[]
    p4=platform.split("-")
    for i in range(1<<len(p4)):
        p=""
        for j in range(len(p4)):
            m=(i&(1<<j))==(1<<j)
#            print " "+str(i)+" "+str(j)+" "+str(m)+" "+p
            if m:
                if len(p)>0:
                    p=p+"-"
                p=p+p4[j]
        if len(p)>0:
            ret.append(p)
    ret.sort(fallbackCompare)
    return ret

def updateCtx(conf, ctx, node):
    if "root" in node.attrib:
        root=ctx["root"]
        ctx=copy.copy(ctx)    
        rel_prj_path=os.path.split(ctx["root"])[1]
        prjconfigdir=os.path.join(ctx["config"], rel_prj_path, "config")
#        print "rel_prj_path="+rel_prj_path+" prjconfigdir="+prjconfigdir
        v=node.attrib["root"].format(platform=ctx["platform"], config=os.path.join(ctx["config"], "config"), prjconfig=prjconfigdir)
        f=node.attrib["root"].format(platform="{platform}", config=os.path.join(ctx["config"], "config"), prjconfig=prjconfigdir)
        if v!=f:
            conf["format"][os.path.join(root, v)]=os.path.join(root, f)

        ctx_root=os.path.join(root, v)
        if not os.path.exists(ctx_root):
            list=generateFallbackList(ctx["platform"])
            for p in list:
                v=node.attrib["root"].format(platform=p, config=os.path.join(ctx["config"], "config"), prjconfig=prjconfigdir)
                if os.path.exists(os.path.join(root, v)):
                    map=os.path.join(root, v)
                    conf["mapping"][map]=ctx_root
                    ctx_root=map
                    list=[]
        if not os.path.exists(ctx_root) and "platforms" in conf and ctx["platform"] in conf["platforms"] and "family" in conf["platforms"][ctx["platform"]]:
            family=conf["platforms"][ctx["platform"]]["family"]
            list=generateFallbackList(family)
#            print "list="+str(list)
            for p in list:
                v=node.attrib["root"].format(platform=p, config=os.path.join(ctx["config"], "config"), prjconfig= prjconfigdir)
                if os.path.exists(os.path.join(root, v)):
                    map=os.path.join(root, v)
                    conf["mapping"][map]=ctx_root
                    ctx_root=map
                    list=[]
        ctx["root"]=ctx_root
    return ctx

def parseDefines(ctx, node, dict):
    value=None
    if "value" in node.attrib:
        value=node.attrib["value"]
    if "name" in node.attrib:
        name=node.attrib["name"]
        dict[name]=value    
    else:
               parseError("define syntax invalid ("+nv+")");

def parseSettings(conf, node, ctx, lib):
    if node.tag=="settings":
        tag="defines"
    elif node.tag=="export":
        tag="exports"
    else:
               parseError("unknown setting: "+node.tag);
    for child in list(node):
        if child.tag=="define":
            parseDefines(ctx, child, lib[tag])    
                else:
            parseError("unhanded element: "+child.tag)

def parseDeps(node):    
    if "dep" in node.attrib:
        dep=node.attrib["dep"].split()
        return dep
    else:
        return []



def parseLibrary(conf, node, ctx, seq_type):
    lib_name=node.attrib["name"]
    lib_uuid=uuid.uuid5(uuid.NAMESPACE_URL, "urn:lib:"+lib_name)
    lib={"name": lib_name, "uuid": lib_uuid, "source": { "files": []}, "header": { "files": [], "target": "", "includes": [] }, "defines": {}, "exports": {}, "deps": parseDeps(node), "mode": "c90", "align": "", "external": False }    
    ctx=updateCtx(conf, ctx, node)
    setAttr(lib, node, "mode")
    setAttr(lib, node, "align")
    if not(lib_name in ctx["externals"]) or not("external" in ctx["externals"][lib_name]) or not(ctx["externals"][lib_name]["external"]):
        for child in list(node):
            if child.tag=="source":
                parseFiles(lib["source"]["files"], list(child), updateCtx(conf, ctx, child))
            elif child.tag=="header":
                setAttr(lib["header"], child, "target")
                child_ctx=updateCtx(conf, ctx, child)
                lib["header"]["includes"].append(child_ctx["root"])
                parseFiles(lib["header"]["files"], list(child), child_ctx)
            elif child.tag=="settings":
                parseSettings(conf, child, ctx, lib)
            elif child.tag=="export":
                parseSettings(conf, child, ctx, lib)
            else:
                parseError("unhanded element: "+child.tag)
    else:
        sys.stderr.write("using external "+lib_name)
        lib["external"]=True
    conf[seq_type].append(lib)

def isExcluded(conf, ctx, lib):
#    sys.stderr.write conf["platforms"]
    if ctx["platform"] in conf["platforms"]:
        platform=conf["platforms"][ctx["platform"]]
        return lib in platform["exclude"]
    else:
        return False

def parsePlatform(conf, node, ctx):
    if "name" in node.attrib:
        name=node.attrib["name"]
        platform={ "exclude": {}, "libs": [], "ldflags": "" }
        setAttr(platform, node, "family")
        setAttr(platform, node, "cc")
        setAttr(platform, node, "ar")
        setAttr(platform, node, "ld")
        setAttr(platform, node, "cflags")
        setAttr(platform, node, "cflags_c")
        setAttr(platform, node, "cflags_cpp")
        setAttr(platform, node, "cppflags")
        setAttr(platform, node, "ldflags")
        setAttr(platform, node, "host")
        if "exclude" in node.attrib:
            for prj in node.attrib["exclude"].split(" "):
                platform["exclude"][prj]=None
#            sys.stderr.write platform["exclude"]
        if "libs" in node.attrib:
            platform["libs"].extend(node.attrib["libs"].split(" "))
        conf["platforms"][name]=platform
    else:
        parseError("no name given for platform")
        

def parseTargets(conf, node, ctx):
    for child in list(node):
        if child.tag=="include":
            path=os.path.abspath(os.path.join(ctx["root"], child.attrib["path"]))
            new_ctx=copy.copy(ctx)        
            new_ctx["root"]=os.path.dirname(path)
            parseConfiguration(conf, path, new_ctx)
        elif child.tag=="library":
            parseLibrary(conf, child, ctx, "libraries")
        elif child.tag=="tool":
            parseLibrary(conf, child, ctx, "tools")
        elif child.tag=="platform":
            parsePlatform(conf, child, ctx)
        else:
            parseError("unhanded element: "+child.tag)

def parseConfiguration(conf, filename, ctx):
    sys.stderr.write("parsing "+filename+"\n")
    tree=etree.parse(filename)
    if tree is not None:
        parseTargets(conf, tree.getroot(), ctx)
    else:
        parseError("failed to open file \""+filename+"\"")



def depClosure2(conf, deps, ignoreExternal):
    ret=[]    
    gray=set(deps)
    black=set()
    while len(gray)>0:
        for lib in conf["libraries"]:
            if lib["name"] in gray:
                gray.remove(lib["name"])
                black.add(lib["name"])
                if not(ignoreExternal) or not(lib["external"]):
                    ret.append((lib, []))
                    lib_deps=lib["deps"]
                    for lib_dep in lib_deps:
                        if not lib_dep in black:
                            gray.add(lib_dep)
    return ret

def depCompare(a, b):
    if a[0] in b[1]:
        return 1
    elif b[0] in a[1]:
        return -1
    else:
        return 0

def depClosureStr(req_deps, deps):
    ret="depClosure("+str(req_deps)+") ="
    for lib in deps:
        ret=ret+" "+lib[0]["name"]+"<"
        for x in lib[1]:
            ret=ret+" "+x[0]["name"]
        ret=ret+" >"
    return ret

def depClosure(conf, req_deps, ignoreExternal):
    deps=depClosure2(conf, req_deps, ignoreExternal)
    for lib in deps:
        lib[1].extend(depClosure2(conf, lib[0]["deps"], ignoreExternal))
#    print depClosureStr(req_deps, deps)
#    deps.sort(depCompare)
#    print depClosureStr(req_deps, deps)
    ret=[]    
    for lib in deps:
        ret.append(lib[0]["name"])
#    print "\n"
    return ret

def gatherIncludeDirs(conf, ctx, deps):
    ret=[]
    aux={}
    for lib in conf["libraries"]:
        if lib["name"] in deps:
            for file in lib["header"]["files"]:
                if file["install"] is not None:
                    dir=os.path.dirname(file["path"])
                    dir=os.path.abspath(dir.rstrip(file["install"]))
                    dir=os.path.relpath(dir, ctx["base"])
                    if dir not in aux:
                        aux[dir]=None
                        ret.append(dir)
#    sys.stderr.write ret
    return ret

def gatherSources(conf, ctx, lib):
    sources=[]
    for file in lib["source"]["files"]:
        rel_source=os.path.relpath(file["path"], ctx["base"])
        sources.append((rel_source, file))
    return sources

#def generateOBJS(conf, ctx, lib, out, obj_dir, cppflags):
#    objs=[]
#    for file in lib["source"]["files"]:
#        rel_source=os.path.relpath(file["path"], ctx["base"])
#        rel_obj=os.path.join(obj_dir, os.path.splitext(rel_source)[0]+".o")
#        out_dir=os.path.dirname(rel_obj)
#        out.write(rel_obj+": "+rel_source+"\n")
#        out.write("\t@mkdir -p "+out_dir+"\n")
#        specific=""
#        if file["ext"]=="c":
#            specific ="$(CFLAGS_C) "
#        elif file["ext"]=="cpp":
#            specific ="$(CFLAGS_CPP) "
#        out.write("\t$(CC) -c $(CFLAGS) "+ specific +"$(CPPFLAGS)"+cppflags+" "+ rel_source+" -o "+rel_obj+"\n")
#        objs.append(rel_obj)
#    return objs

def generateDEPS(conf, ctx, lib, out, obj_dir, cppflags, filename):
    for file in lib["source"]["files"]:
        rel_source=os.path.relpath(file["path"], ctx["base"])
        specific=""
        if file["ext"]=="c":
            specific ="$(CFLAGS_C) "
        elif file["ext"]=="cpp":
            specific ="$(CFLAGS_CPP) "
        out.write("\tmakedepend -a -f"+filename+" $(CPPFLAGS)"+cppflags+" "+ rel_source+"\n")

def generateCPPFLAGS(conf, ctx, lib, build_dir):
    cppflags=""
    for define in lib["defines"]:
        if lib["defines"][define] is None:
            cppflags=cppflags+" -D"+define
        else:
            cppflags=cppflags+" -D"+define+"="+lib["defines"][define]
    for dir in lib["header"]["includes"]:
        abs_dir=os.path.abspath(dir)
        rel_dir=os.path.relpath(abs_dir, os.path.join(ctx["base"], build_dir))
#        sys.stderr.write("abs_dir="+str(abs_dir)+" build_dir="+os.path.join(ctx["base"], build_dir)+" rel_dir="+str(rel_dir))
        cppflags=cppflags+" -I"+rel_dir
    dep_closure=depClosure(conf, lib["deps"], False)
    dep_dirs=gatherIncludeDirs(conf, ctx, dep_closure)
    for dir in dep_dirs:
        abs_dir=os.path.abspath(dir)
        rel_dir=os.path.relpath(abs_dir, os.path.join(ctx["base"], build_dir))
#        sys.stderr.write("abs_dir="+str(abs_dir)+" build_dir="+os.path.join(ctx["base"], build_dir)+" rel_dir="+str(rel_dir))
        cppflags=cppflags+" -I"+rel_dir
    for dep_lib in conf["libraries"]:
        if dep_lib["name"] in ctx["externals"]:
            ext_lib=ctx["externals"][dep_lib["name"]]
            if "external" in ext_lib and ext_lib["external"]:                        
                if "cppflags" in ext_lib:
                    cppflags=cppflags+" "+ext_lib["cppflags"]

    return cppflags


def generateVCXPROJ(conf, ctx, lib, type):
    filename=os.path.abspath("win32\\"+lib["name"]+"\\"+lib["name"]+".vcxproj")
    filepath=os.path.dirname(filename)
    prj_uuid=lib["uuid"]
    sys.stderr.write("generating "+filename+"\n")
    try:
        os.makedirs(os.path.dirname(filename))
    except OSError:
        pass

    out=open(filename, "w")
    out.write("<Project DefaultTargets=\"Build\" ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n");
        out.write("<ItemGroup Label=\"ProjectConfigurations\">\n");
        out.write(" <ProjectConfiguration Include=\"Debug|Win32\">\n");
        out.write("   <Configuration>Debug</Configuration>\n");
        out.write("   <Platform>Win32</Platform>\n");
        out.write(" </ProjectConfiguration>\n");
        out.write("<ProjectConfiguration Include=\"Release|Win32\">\n");
        out.write("   <Configuration>Release</Configuration>\n");
        out.write("   <Platform>Win32</Platform>\n");
        out.write("</ProjectConfiguration>\n");
    out.write("</ItemGroup>\n");
        out.write("<PropertyGroup Label=\"Globals\">\n");
        out.write("    <ProjectGuid>{"+str(prj_uuid)+"}</ProjectGuid>\n");
        out.write("    <Keyword>Win32Proj</Keyword>\n");
        out.write("    <RootNamespace>"+lib["name"]+"</RootNamespace>\n");
        out.write("</PropertyGroup>\n");
        out.write("<Import Project=\"$(VCTargetsPath)\Microsoft.Cpp.default.props\" />\n");
        out.write("  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\n");
        out.write("    <ConfigurationType>"+type+"</ConfigurationType>\n");
        out.write("    <UseDebugLibraries>true</UseDebugLibraries>\n");
        out.write("    <CharacterSet>Unicode</CharacterSet>\n");
        out.write("  </PropertyGroup>\n");
        out.write("  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\n");
        out.write("    <ConfigurationType>"+type+"</ConfigurationType>\n");
        out.write("    <UseDebugLibraries>false</UseDebugLibraries>\n");
        out.write("    <WholeProgramOptimization>true</WholeProgramOptimization>\n");
        out.write("    <CharacterSet>Unicode</CharacterSet>\n");
        out.write("  </PropertyGroup>\n");
        out.write("<Import Project=\"$(VCTargetsPath)\Microsoft.Cpp.props\" />\n");
        out.write("<ImportGroup Label=\"ExtensionSettings\" />\n");
        out.write("<ImportGroup Label=\"PropertySheets\" />\n");
        out.write("<PropertyGroup Label=\"UserMacros\" />\n");
        out.write("<PropertyGroup />\n");
        out.write("<ItemDefinitionGroup>\n");
    out.write("<ClCompile>\n");
    dep_closure=depClosure(conf, lib["deps"], False)
    cpp_defs="WIN32;"
    for define in lib["defines"]:
        if lib["defines"][define] is None:
            cpp_defs=cpp_defs+define+";"
        else:
            cpp_defs=cpp_defs+define+"="+lib["defines"][define]+";"
    for dep in dep_closure:
        for dep_lib in conf["libraries"]:
            if dep_lib["name"]==dep and not isExcluded(conf, ctx, dep):
                for define in dep_lib["exports"]:
                    if dep_lib["exports"][define] is None:
                        cpp_defs=cpp_defs+define+";"
                    else:
                        cpp_defs=cpp_defs+define+"="+dep_lib["defines"][define]+";"
    cpp_defs=cpp_defs+"_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)"
    out.write("<PreprocessorDefinitions>"+cpp_defs+"</PreprocessorDefinitions>\n" )
    AdditionalIncludeDirectories=""
    for dir in lib["header"]["includes"]:
        AdditionalIncludeDirectories=AdditionalIncludeDirectories+os.path.relpath(os.path.abspath(dir), filepath)+";"    
    dep_dirs=gatherIncludeDirs(conf, ctx, dep_closure)
    for dir in dep_dirs:
        abs_path=os.path.relpath(os.path.abspath(dir), filepath)
        AdditionalIncludeDirectories=AdditionalIncludeDirectories+abs_path+";"
    AdditionalIncludeDirectories=AdditionalIncludeDirectories+"%(AdditionalIncludeDirectories)"
    out.write("<AdditionalIncludeDirectories>"+AdditionalIncludeDirectories+"</AdditionalIncludeDirectories>\n")
    if "c99"==lib["mode"]:
        out.write("<CompileAs>CompileAsCpp</CompileAs>\n");
    if ""!=lib["align"]:
        out.write("<StructMemberAlignment>"+lib["align"]+"Byte</StructMemberAlignment>\n");
    out.write("</ClCompile>\n");
    if "Application"==type:
        out.write("<Link>\n");
        out.write("<AdditionalDependencies>")
        if ctx["platform"] in conf["platforms"]:
            ext_libs=conf["platforms"][ctx["platform"]]["libs"]
        else:
            ext_libs={}
#        sys.stderr.write ext_libs
        for ext_lib in ext_libs:
#            sys.stderr.write ext_lib
            out.write(ext_lib+";")
        out.write("%(AdditionalDependencies)</AdditionalDependencies> \n")
        out.write("</Link>\n");
        out.write("</ItemDefinitionGroup>\n");


    out.write("<ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n")
    out.write("<ClCompile>\n");
    out.write("<WarningLevel>Level3</WarningLevel>\n")
    out.write("<Optimization>Disabled</Optimization>\n")
    out.write("<DisableSpecificWarnings>4996</DisableSpecificWarnings>\n")
    out.write("</ClCompile>\n");
    if "Application"==type:
        out.write("<Link>\n");
        out.write("<GenerateDebugInformation>true</GenerateDebugInformation>\n")
        out.write("<SubSystem>Console</SubSystem>\n")
        out.write("</Link>\n");    
        out.write("</ItemDefinitionGroup>\n");

    out.write("<ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n")
    out.write("<ClCompile>\n")
    out.write("<Optimization>MaxSpeed</Optimization>\n")
    out.write("<DisableSpecificWarnings>4996</DisableSpecificWarnings>\n")
    out.write("<FunctionLevelLinking>true</FunctionLevelLinking> \n")
    out.write("<IntrinsicFunctions>true</IntrinsicFunctions> \n")
    out.write("</ClCompile>\n");
    if "Application"==type:
        out.write("<Link>\n");
        out.write("<GenerateDebugInformation>false</GenerateDebugInformation>\n")
        out.write("<SubSystem>Console</SubSystem>\n")
        out.write("<EnableCOMDATFolding>true</EnableCOMDATFolding>\n")
        out.write("<OptimizeReferences>true</OptimizeReferences>\n")
        out.write("</Link>\n");    
        out.write("</ItemDefinitionGroup>\n");

    out.write("<ItemGroup>\n");
        for file in lib["header"]["files"]:
        if "h"==file["ext"]:
            out.write("  <ClInclude Include=\""+os.path.relpath(file["path"], filepath)+"\"/>\n");
        out.write("</ItemGroup>\n");
        out.write("<ItemGroup>\n");
        for file in lib["source"]["files"]:
        if "cpp"==file["ext"] or "c"==file["ext"] or "s"==file["ext"]:
            out.write("  <ClCompile Include=\""+os.path.relpath(file["path"], filepath)+"\">\n");
            out.write("  </ClCompile>\n");
        out.write("</ItemGroup>\n");
        out.write("<ItemGroup>\n");
    for dep in dep_closure:
        for dep_lib in conf["libraries"]:
            if dep_lib["name"]==dep and not isExcluded(conf, ctx, dep_lib["name"]):
                out.write("<ProjectReference Include=\"../"+dep_lib["name"]+"/"+dep_lib["name"]+".vcxproj\">\n")
                out.write("<Project>{"+str(dep_lib["uuid"])+"}</Project>\n")
                out.write("</ProjectReference>\n")
        out.write("</ItemGroup>\n")
        out.write("<Import Project=\"$(VCTargetsPath)\Microsoft.Cpp.targets\" />\n");
        out.write("<ImportGroup Label=\"ExtensionTargets\" />\n");
        out.write("</Project>\n");
    out.close()

def writeProject(out, solution_uuid, lib):
    out.write("Project(\"{"+str(solution_uuid)+"}\") = \""+lib["name"]+"\", \""+lib["name"]+"\\"+lib["name"]+".vcxproj\", \"{"+str(lib["uuid"])+"}\"\n")
    out.write("EndProject\n")

def generatePlatformList(conf, ctx):
    add=[]
    for platform in conf["platforms"]:
        if platformSubseteqTest(ctx["platform"], platform):
            add.append(platform)
    add.sort(platformCompare)
    return add

def generateConfiguration(ctx, includes, platform):
    conf={ "libraries": [], "tools": [], "platforms": {}, "mapping": {}, "format": {} }
    ctx["platform"]=platform
    for include in includes:
        parseConfiguration(conf, include, ctx)
    if ctx["platform"] not in conf["platforms"]:    
        add=generatePlatformList(conf, ctx);
        if len(add)>0:
            conf["platforms"][ctx["platform"]]=conf["platforms"][add[0]]
    return conf

def generateWin32(ctx, source):
    platform=ctx["platforms"][0]
    conf=generateConfiguration(ctx, [source], platform)
#    sys.stderr.write conf
        sys.stderr.write("generate win32 project")
    for lib in conf["libraries"]:
        if not isExcluded(conf, ctx, lib["name"]):
            generateVCXPROJ(conf, ctx, lib, "StaticLibrary")
    for tool in conf["tools"]:
        if not isExcluded(conf, ctx, tool["name"]):
            generateVCXPROJ(conf, ctx, tool, "Application")
    solution_file=os.path.abspath("win32\\solution.sln")
    solution_uuid=uuid.uuid5(uuid.NAMESPACE_URL, solution_file)
    out=open(solution_file, "w")
    out.write("\n")
    out.write("Microsoft Visual Studio Solution File, Format Version 11.00\n")
    for lib in conf["libraries"]:
        if not isExcluded(conf, ctx, lib["name"]):
            writeProject(out, solution_uuid, lib)
    for tool in conf["tools"]:
        if not isExcluded(conf, ctx, tool["name"]):
            writeProject(out, solution_uuid, tool)
    out.write("\n")
    out.close()


def writeSourceRules(conf, ctx, lib_type, out, lib, build_dir):
    dirs={}
    sources=gatherSources(conf, ctx, lib)
    out.write("objs_"+lib["name"]+"=\\\n")
    for source in sources:
        out.write("     $(BUILD_DIR)/"+os.path.splitext(source[0])[0]+".o\\\n")
        dirs[os.path.dirname(source[0])]=None
    out.write("     \n")
    out.write(lib["name"]+"_includes="+generateCPPFLAGS(conf, ctx, lib, build_dir)+"\n")
    out.write("\n")
    includes="$("+lib["name"]+"_includes)"
    lib_type_flags=""
    if lib_type=="shared":
        if platformSubseteqTest(ctx["platform"], "linux-*-gcc-*"):
            lib_type_flags=" -fPIC -DPIC"
        elif platformSubseteqTest(ctx["platform"], "darwin-*-gcc-*"):
            lib_type_flags=" -fno-common"
    # write special rules
    for source in sources:
        file=source[1]
        if "x" in file:
            special="-x "+file["x"]+" "
            out.write("$(BUILD_DIR)/"+os.path.splitext(source[0])[0]+".o: $(SRC_DIR)/"+source[0]+"\n")
            out.write("\t@mkdir -p $(dir $@)\n")
            out.write("\t$(CC) $(CFLAGS) $(CFLAGS_C) $(CPPFLAGS) "+special+includes+lib_type_flags+" -c \"$<\" -o \"$@\"\n")
    # write generic rules
    for dir in dirs:
        for ext in EXT_MAPPING:
            if EXT_MAPPING[ext]=='c' or EXT_MAPPING[ext]=='s':
                out.write("$(BUILD_DIR)/"+dir+"/%.o: $(SRC_DIR)/"+dir+"/%"+ext+"\n")
                out.write("\t@mkdir -p $(dir $@)\n")
                out.write("\t$(CC) $(CFLAGS) $(CFLAGS_C) $(CPPFLAGS) "+includes+lib_type_flags+" -c \"$<\" -o \"$@\"\n")
                out.write("\n")
            elif EXT_MAPPING[ext]=='cpp':
                out.write("$(BUILD_DIR)/"+dir+"/%.o: $(SRC_DIR)/"+dir+"/%"+ext+"\n")
                out.write("\t@mkdir -p $(dir $@)\n")
                out.write("\t$(CC) $(CFLAGS) $(CFLAGS_CPP) $(CPPFLAGS) "+includes+lib_type_flags+" -c \"$<\" -o \"$@\"\n")
                out.write("\n")

def writeMakefileFlags(conf, ctx, out):
    platform=ctx["platform"]
    out.write("CC="+conf["platforms"][platform]["cc"]+"\n");
    out.write("AR="+conf["platforms"][platform]["ar"]+"\n");
    out.write("CFLAGS="+conf["platforms"][platform]["cflags"]+"\n")
    out.write("CFLAGS_C="+conf["platforms"][platform]["cflags_c"]+"\n")
    out.write("CFLAGS_CPP="+conf["platforms"][platform]["cflags_cpp"]+"\n")
    out.write("CPPFLAGS="+conf["platforms"][platform]["cppflags"]+"\n");
    out.write("LDFLAGS="+conf["platforms"][platform]["ldflags"]+"\n");

def getSharedExt(conf, ctx):
    if platformSubseteqTest(ctx["platform"], "darwin-*-gcc-*"):
        return ".dylib"
    else:
        return ".so"

def generateExtLDFlags(conf, ctx):
    ext_ldflags=""
    for dep_lib in conf["libraries"]:
        if dep_lib["name"] in ctx["externals"]:
            ext_lib=ctx["externals"][dep_lib["name"]]
            if "external" in ext_lib and ext_lib["external"]:                        
                if "ldflags" in ext_lib:
                    ext_ldflags=ext_ldflags+" "+ext_lib["ldflags"]
    return ext_ldflags

def generateLDFlags(conf, ctx, lib_type, initial_deps, exclude_deps, lib_form):
    ret=""
    deps=depClosure(conf, initial_deps, True)
    for dep in deps:
        if not(dep in exclude_deps):
            if lib_type=="static":
                ret=ret+" $(BUILD_DIR)/lib"+dep+".a"
            if lib_type=="shared":
                if lib_form:
                    ret=ret+" -L$(BUILD_DIR) -l"+dep
                else:
                    ret=ret+" $(BUILD_DIR)/lib"+dep+getSharedExt(conf, ctx)

    return ret


def generateLibraryMakefile(conf, ctx, lib_type, lib, filename, build_dir, src_dir):
    sys.stderr.write("generating "+filename+"\n")
    if not os.path.exists(os.path.dirname(filename)):
            os.makedirs(os.path.dirname(filename))
    out=open(filename, "w")
    out.write("# Generated.\n")
    # compiler flags
    writeMakefileFlags(conf, ctx, out)
    out.write("BUILD_DIR=.\n")
    out.write("SRC_DIR="+src_dir+"\n")
    out.write("\n")
    out.write(".PHONY: all clean\n")
    out.write("\n")
    out.write("# library "+lib["name"]+"\n")
    out.write("all:")
    if lib_type=="static":
        out.write(" $(BUILD_DIR)/lib"+lib["name"]+".a")
    if lib_type=="shared":
        out.write(" $(BUILD_DIR)/lib"+lib["name"]+getSharedExt(conf, ctx))
    out.write("\n\n")
    writeSourceRules(conf, ctx, lib_type, out, lib, build_dir)
    if lib_type=="static":
        out.write("$(BUILD_DIR)/lib"+lib["name"]+".a: $(objs_"+lib["name"]+")\n")
        out.write("\t$(AR) -rcs $(BUILD_DIR)/lib"+lib["name"]+".a $(objs_"+lib["name"]+")\n")
        out.write("\n")
    if lib_type=="shared":
        ld_deps=generateLDFlags(conf, ctx, lib_type, lib["deps"], [lib["name"]], True)+generateExtLDFlags(conf, ctx)
        out.write("$(BUILD_DIR)/lib"+lib["name"]+getSharedExt(conf, ctx)+": $(objs_"+lib["name"]+")\n")
        if platformSubseteqTest(ctx["platform"], "darwin-*-gcc-*"):
            out.write("\t$(CC) -dynamiclib -install_name lib"+lib["name"]+getSharedExt(conf, ctx)+" -o $(BUILD_DIR)/lib"+lib["name"]+getSharedExt(conf, ctx)+" $(objs_"+lib["name"]+")"+ld_deps+"\n")
        else:
            out.write("\t$(CC) -shared -Wl,-soname,lib"+lib["name"]+getSharedExt(conf, ctx)+" -Wl,-z,defs  -fPIC -o $(BUILD_DIR)/lib"+lib["name"]+getSharedExt(conf, ctx)+" $(objs_"+lib["name"]+")"+ld_deps+" -lc\n")
            out.write("\tstrip --remove-section=.comment --remove-section=.note --strip-unneeded $(BUILD_DIR)/lib"+lib["name"]+getSharedExt(conf, ctx))
        out.write("\n")
    out.write("clean:\n")
    out.write("\trm -rf $(BUILD_DIR)/lib"+lib["name"]+".a $(BUILD_DIR)/lib"+lib["name"]+getSharedExt(conf, ctx)+" $(objs_"+lib["name"]+")\n")
    out.close()
    
def generateLibraryInclude(conf, ctx, lib, filename, build_dir, src_dir):
    sys.stderr.write("generating "+filename+"\n")
    if not os.path.exists(os.path.dirname(filename)):
            os.makedirs(os.path.dirname(filename))
    ret=[]
    lib_deps=lib["deps"]
    lib_deps.append(lib["name"])
#    out.write("deps="+str(lib["deps"])+"\n");
    dep_closure=depClosure(conf, lib_deps, False)
    dep_dirs=gatherIncludeDirs(conf, ctx, dep_closure)
    for dep in dep_dirs:
        dep_dir=os.path.join(ctx["base"], str(dep))
        if dep_dir in conf["mapping"]:
            dep_dir=conf["mapping"][dep_dir]
        if dep_dir in conf["format"]:
            ln_src=os.path.join(build_dir, "include", os.path.relpath(conf["format"][dep_dir], ctx["base"]).format(platform="platform").replace(os.sep, "_"))
            ln_dst=os.path.join(src_dir, dep)
            if os.path.exists(ln_src):
                os.remove(ln_src)
            print "symlink("+ln_dst+", "+ln_src+")"+str(os.path.exists(ln_src))
            os.symlink(ln_dst, ln_src)
            ret.append(ln_src)
        else:
            ret.append(dep)

    out=open(filename, "w")
    for file in ret:
        out.write(file+"\n")
    out.close()


def generateToolMakefile(conf, ctx, lib_type, tool, filename, build_dir, src_dir):
    sys.stderr.write("generating "+filename+"\n")
    if not os.path.exists(os.path.dirname(filename)):
            os.makedirs(os.path.dirname(filename))
    out=open(filename, "w")
    out.write("# Generated.\n")
    # compiler flags
    writeMakefileFlags(conf, ctx, out)
    out.write("BUILD_DIR=.\n")
    out.write("SRC_DIR="+src_dir+"\n")
    out.write("\n")
    out.write(".PHONY: all clean\n")
    out.write("\n")
    out.write("# tool "+tool["name"]+"\n")
    out.write("all: $(BUILD_DIR)/"+ tool["name"]+"\n\n")
    writeSourceRules(conf, ctx, "static", out, tool, build_dir)
    out.write(tool["name"]+"_ld="+generateLDFlags(conf, ctx, lib_type, tool["deps"], [], False)+"\n")
#    deps=depClosure(conf, tool["deps"], True)
#    for dep in deps:
#        if lib_type=="static":
#            out.write(" $(BUILD_DIR)/lib"+ dep +".a")    
#        if lib_type=="shared":
#            out.write(" $(BUILD_DIR)/lib"+ dep +getSharedExt(conf, ctx))    
#    out.write("\n")
    ext_ldflags=generateExtLDFlags(conf, ctx)
#    ext_ldflags=""
#    for dep_lib in conf["libraries"]:
#        if dep_lib["name"] in ctx["externals"]:
#            ext_lib=ctx["externals"][dep_lib["name"]]
#            if "external" in ext_lib and ext_lib["external"]:                        
#                if "ldflags" in ext_lib:
#                    ext_ldflags=ext_ldflags+" "+ext_lib["ldflags"]

    out.write("$(BUILD_DIR)/"+tool["name"]+": $(objs_"+tool["name"]+") $("+tool["name"]+"_ld)\n")            
    static_flag=""
    if lib_type=="static" and platformSubseteqTest(ctx["platform"], "linux-*-gcc-*"):
        static_flag=" --static"
    out.write("\t$(CC) $(CFLAGS) $(CPPFLAGS) -o $(BUILD_DIR)/"+tool["name"]+" $(objs_"+tool["name"]+") "+generateLDFlags(conf, ctx, lib_type, tool["deps"], [], True)+ ext_ldflags+" $(LDFLAGS)\n")
    out.write("\n")
    out.write("clean:\n")
    out.write("\trm -f $(BUILD_DIR)/"+tool["name"]+" $(objs_"+tool["name"]+")\n")


def generateTypedMakefile(conf, ctx, lib_type):
    obj_dir=os.path.join("build", ctx["platform"], lib_type)
    filename=os.path.join(obj_dir, "Makefile")
    sys.stderr.write("generating "+filename+"\n")
    if not os.path.exists(os.path.dirname(filename)):
            os.makedirs(os.path.dirname(filename))
    out=open(filename, "w")
    out.write("# Generated.\n")

    # generate phony targets
    out.write(".PHONY: all clean tools dep")
    for lib in conf["libraries"]:
        if not lib["external"]:
            out.write(" "+lib["name"])
    for tool in conf["tools"]:
        out.write(" "+tool["name"])
    out.write("\n")
    # all
    out.write("all:")
    for lib in conf["libraries"]:
        if not lib["external"]:
            out.write(" "+lib["name"])
    out.write(" tools\n")
    # tools
    out.write("tools:")
    for tool in conf["tools"]:
        out.write(" "+tool["name"])
    out.write("\n")

    # generate libs
    for lib in conf["libraries"]:
        if not lib["external"]:
            makefile=os.path.join(obj_dir, "Makefile."+lib["name"])
            generateLibraryMakefile(conf, ctx, lib_type, lib, makefile, obj_dir, os.path.join("..", "..", ".."))
            libincludefile=os.path.join(obj_dir, "include", lib["name"]+".inc")
            generateLibraryInclude(conf, ctx, lib, libincludefile, obj_dir, os.path.join("..", "..", "..", ".."))
            out.write(lib["name"]+":")
            deps=depClosure(conf, lib["deps"], True)
            for dep in deps:
                if lib["name"]!=dep:
                    out.write(" "+dep)
            out.write("\n")
            out.write("\t$(MAKE) -f "+"Makefile."+lib["name"]+"\n\n")
    
    # generate tools
    for tool in conf["tools"]:
        makefile=os.path.join(obj_dir, "Makefile."+tool["name"])
        generateToolMakefile(conf, ctx, lib_type, tool, makefile, obj_dir, os.path.join("..", "..", ".."))
        deps=depClosure(conf, tool["deps"], True)
        out.write(tool["name"]+":")
        for dep in deps:
            if tool["name"]!=dep:
                out.write(" "+dep)
        out.write("\n")
        out.write("\t$(MAKE) -f "+"Makefile."+ tool["name"]+"\n\n")

    out.write("clean:\n")
    for lib in conf["libraries"]:
        if not lib["external"]:
            out.write("\t$(MAKE) -f "+"Makefile."+lib["name"]+" clean\n")
    for tool in conf["tools"]:
        out.write("\t$(MAKE) -f "+"Makefile."+tool["name"]+" clean\n")


#    out.write("dep:\n")
#    for lib in conf["libraries"]:
#        generateDEPS(conf, ctx, lib, out, obj_dir, generateCPPFLAGS(conf, ctx, lib), filename)
#    for tool in conf["tools"]:
#        generateDEPS(conf, ctx, tool, out, obj_dir, generateCPPFLAGS(conf, ctx, tool), filename)

    out.write("package: all\n")
    out.write("\tcd ../../.. && ./generate.py --include Makefile.xml");
    for ext_lib in ctx["externals"]:
        if ctx["externals"][ext_lib]["external"]:
            name=ext_lib
            if "xml"==ext_lib:
                name="libxml"
            out.write(" --with-"+name+"=yes")
            if "cppflags" in ctx["externals"][ext_lib]:
                out.write(" --with-"+name+"-cppflags=\""+ctx["externals"][ext_lib]["cppflags"]+"\"")
            if "ldflags" in ctx["externals"][ext_lib]:
                out.write(" --with-"+name+"-ldflags=\""+ctx["externals"][ext_lib]["ldflags"]+"\"")
    out.write(" --package package_"+lib_type+".zip --type "+lib_type)
    out.write(" --config-dir "+ctx["config"]+" "+ctx["platform"])
    out.write("\n")

    obj_dir=os.path.join(ctx["base"], "build", ctx["platform"], lib_type)
    tool_ext=""
    if lib_type=="static":
        lib_ext=".a"
    else:
        lib_ext=getSharedExt(conf, ctx)
    lib_prefix="lib"
    base="$(DESTDIR)/usr/"
    out.write("install: all\n")
    for lib in conf["libraries"]:
        if not lib["external"] and not isExcluded(conf, ctx, lib["name"]):
            for include in lib["header"]["files"]:
                if None!=include["install"]:
                    target=base+"include/"
                    if len(include["install"])>0:
                        target=target+include["install"]+"/"
                    target=target+os.path.split(include["path"])[1]
                    out.write("\t@mkdir -p "+os.path.dirname(target)+"\n")
                    out.write("\t@cp "+include["path"]+" "+target+"\n")
            target=base+"lib/"+lib_prefix+lib["name"]+lib_ext
            out.write("\t@mkdir -p "+os.path.dirname(target)+"\n")
            if lib_type=="shared":
                out.write("\t@cp "+os.path.join(obj_dir, lib_prefix+lib["name"]+lib_ext)+" "+target+".1\n")
                out.write("\tldconfig -v -n "+os.path.dirname(target)+"\n")
            else:
                out.write("\t@cp "+os.path.join(obj_dir, lib_prefix+lib["name"]+lib_ext)+" "+target+"\n")
    for tool in conf["tools"]:
        if not isExcluded(conf, ctx, tool["name"]):
            target=base+"bin/"+tool["name"]+tool_ext
            out.write("\t@mkdir -p "+os.path.dirname(target)+"\n")
            out.write("\t@cp "+os.path.join(obj_dir, tool["name"]+tool_ext)+" "+target+"\n")
    target=base+"lib/pkgconfig/libopc.pc"
    out.write("\t@mkdir -p "+os.path.dirname(target)+"\n")    
    out.write("\t@cp "+os.path.join(ctx["base"], "config", "libopc.pc")+" "+target+"\n")
    out.close()


def generateTypedMakefiles(ctx, source, lib_types):
    for platform in ctx["platforms"]:
        conf=generateConfiguration(ctx, [source], platform)
        if ctx["platform"] in conf["platforms"]:    
            for lib_type in lib_types:        
                generateTypedMakefile(conf, ctx, lib_type)
        else:
            parseError("platform "+ctx["platform"]+" is unknown.")
            sys.stderr.write("available platforms:");
            for platform in conf["platforms"]:
                sys.stderr.write(platform+"\n")
    out=open("Makefile", "w")
    out.write(".PHONY: all clean");
    for platform in ctx["platforms"]:
        for lib_type in lib_types:        
            out.write(" "+platform+"."+lib_type);
    for lib_type in lib_types:        
        out.write(" "+lib_type);
    out.write("\n");
    out.write("all:");
    for lib_type in lib_types:        
        out.write(" "+lib_type);
    out.write("\n");

    for lib_type in lib_types:        
        out.write(lib_type+":");
        for platform in ctx["platforms"]:
            out.write(" "+platform+"."+lib_type);
        out.write("\n");

    for lib_type in lib_types:        
        for platform in ctx["platforms"]:
            out.write(platform+"."+lib_type+": build"+os.sep+platform+os.sep+lib_type+os.sep+"Makefile\n");
            out.write("\t@$(MAKE) -C build"+os.sep+platform+os.sep+lib_type+"\n");
    out.write("clean:\n")
    for lib_type in lib_types:        
        for platform in ctx["platforms"]:
            out.write("\t@$(MAKE) -C build"+os.sep+platform+os.sep+lib_type+" clean\n");    
    out.write("package:\n")
    for lib_type in lib_types:        
        for platform in ctx["platforms"]:
            out.write("\t@$(MAKE) -C build"+os.sep+platform+os.sep+lib_type+" package\n")
    out.write("install:\n")
    for lib_type in lib_types:        
        for platform in ctx["platforms"]:
            out.write("\t@$(MAKE) -C build"+os.sep+platform+os.sep+lib_type+" install\n")
    out.close()

def dumpEnvironment(ctx, includes, platform):
    conf=generateConfiguration(ctx, includes, platform)
    if platform in conf["platforms"]:            
        p=conf["platforms"][platform]
#        print(str(p))
        host=""
        if "host" in p:
            host="HOST=\""+p["host"]+"\" "
        sys.stdout.write("export CC=\""+p["cc"]+"\" "+
             "export AR=\""+p["ar"]+"\" "+
             "export LD=\""+p["ld"]+"\" "
             "export CPPFLAGS=\""+p["cppflags"]+"\" "
             "export CFLAGS=\""+p["cflags"]+"\" "+
             host
            )
    else:
        parseError("platform "+platform+" is unknown.")
        sys.stderr.write("available platforms:");
        for platform in conf["platforms"]:
            sys.stderr.write(platform)    

def set_external_flag(ctx, lib, flag, value):
#    sys.stderr.write("SETTING "+lib+" "+flag+" "+str(value)+"\n")
    if lib in ctx["externals"]:
        ctx["externals"][lib][flag]=value
    else:
        ctx["externals"][lib]={flag: value}

def is_external(ctx, lib):
    if lib in ctx["externals"]:
        if "external" in ctx["externals"][lib]:
            return ctx["externals"][lib]["external"]
        else:
            return False
    else:
        return False


def generateZipPackage(ctx, source, lib_type, install_zip):
    zip=zipfile.ZipFile(install_zip, 'w', zipfile.ZIP_DEFLATED)
    base=os.path.splitext(install_zip)[0]+"/"
    platform=ctx["platforms"][0]
    conf=generateConfiguration(ctx, [source], platform)
    if platformSubseteqTest(ctx["platforms"][0], "win32-*-*"):
        obj_dir=os.path.join("win32", ctx["platform"].split('-')[1])
        tool_ext=".exe"
        lib_exts=[".lib"]
        if (lib_type=="shared"):
            shared_exts.append(".dll")        
        lib_prefix=""
    else:
        obj_dir=os.path.join("build", ctx["platform"], lib_type)
        tool_ext=""
        if lib_type=="shared":
            lib_exts=[".dylib"]
        else:
            lib_exts=[".a"]
        lib_prefix="lib"
    for lib in conf["libraries"]:
        if not lib["external"] and not isExcluded(conf, ctx, lib["name"]):
            for include in lib["header"]["files"]:
                if None!=include["install"]:
                    target=base+"include/"
                    if len(include["install"])>0:
                        target=target+include["install"]+"/"
                    target=target+os.path.split(include["path"])[1]
                    zip.write(include["path"], target)
            for lib_ext in lib_exts:
                target=base+"lib/"+lib_prefix+lib["name"]+lib_ext
#                print "ADD "+os.path.join(obj_dir, lib_prefix+lib["name"]+lib_ext)
                zip.write(os.path.join(obj_dir, lib_prefix+lib["name"]+lib_ext), target)
    for tool in conf["tools"]:
        if not isExcluded(conf, ctx, tool["name"]):
            target=base+"bin/"+tool["name"]+tool_ext
            zip.write(os.path.join(obj_dir, tool["name"]+tool_ext), target)
    zip.write(os.path.join("third_party", "LICENSE"), base+"/LICENSE.THIRD_PARTY")
    zip.write("LICENSE", base+"/LICENSE")
    zip.close()



def installFiles(ctx, source, lib_type, base):
    copy_ctx=None
    platform=ctx["platforms"][0]
    conf=generateConfiguration(ctx, [source], platform)
    if platformSubseteqTest(ctx["platforms"][0], "win32-*-*"):
        obj_dir=os.path.join("win32", ctx["platform"].split('-')[1])
        tool_ext=".exe"
        lib_exts=[".lib"]
        lib_prefix=""
    else:
        obj_dir=os.path.join("build", ctx["platform"], lib_type)
        tool_ext=""
        lib_exts=[".a", getSharedExt(conf, ctx)]
        lib_prefix="lib"
    for lib in conf["libraries"]:
        if not lib["external"] and not isExcluded(conf, ctx, lib["name"]):
            for include in lib["header"]["files"]:
                if None!=include["install"]:
                    target=base+"include/"
                    if len(include["install"])>0:
                        target=target+include["install"]+"/"
                    target=target+os.path.split(include["path"])[1]
                    copyFile(copy_ctx, include["path"], target)
            for lib_ext in lib_exts:
                target=base+"lib/"+lib_prefix+lib["name"]+lib_ext
                copyFile(copy_ctx, os.path.join(obj_dir, lib_prefix+lib["name"]+lib_ext), target)
    for tool in conf["tools"]:
        if not isExcluded(conf, ctx, tool["name"]):
            target=base+"bin/"+tool["name"]+tool_ext
            copyFile(copy_ctx, os.path.join(obj_dir, tool["name"]+tool_ext), target)
    copyFile(copy_ctx, os.path.join("third_party", "LICENSE"), base+"/LICENSE.THIRD_PARTY")
    copyFile(copy_ctx, "LICENSE", base+"/LICENSE")

def usage():
    print("usage:")
    print(" generate [--include \"Makefile.xml\"]")
    print("          [--config-dir \"config/\"]")
    print("          [--print-env \"linux-release-gcc\"]")
    print("          [--package \"xyz.zip/\"]")

if __name__ == "__main__":    
    try:
        opts, args = getopt.getopt(sys.argv[1:], "h", ["help", "include=", "config-dir=", "print-env=", 
            "package=", "install=", 
            "with-zlib-cppflags=", "with-zlib-ldflags=", "with-zlib=",
            "with-libxml-cppflags=", "with-libxml-ldflags=", "with-libxml=",
            "type="])
    except getopt.GetoptError, err:
        # print help information and exit:
        print str(err) # will print something like "option -a not recognized"
        usage()
        sys.exit(2)
    ctx={ "base": os.path.abspath(os.curdir), "root": os.path.abspath(os.curdir), "platform": "?-?-?", "platforms": [], "externals": {} }
    ctx["config"]=ctx["base"]
    includes=[]
    dump_env=[]
    install_zip=None
    target_type="static"
    for o, a in opts:
        if o in ("-h", "--help"):
            usage()
            sys.exit(0)
        elif o in ("--include"):
            includes.append(a)
        elif o in ("--config-dir"):
            ctx["config"]=os.path.join(ctx["base"], a)
        elif o in ("--print-env"):
            args=[]
            dump_env=[a]
        elif o in ("--package"):
            install_zip=a
        elif o in ("--install"):
            install_files=a
        elif o in ("--with-zlib"):
            set_external_flag(ctx, "zlib", "external", "yes"==a)
        elif o in ("--with-zlib-cppflags"):
            set_external_flag(ctx, "zlib", "cppflags", a)
        elif o in ("--with-zlib-ldflags"):
            set_external_flag(ctx, "zlib", "ldflags", a)
        elif o in ("--with-libxml"):
            set_external_flag(ctx, "xml", "external", "yes"==a)
        elif o in ("--with-libxml-cppflags"):
            set_external_flag(ctx, "xml", "cppflags", a)
        elif o in ("--with-libxml-ldflags"):
            set_external_flag(ctx, "xml", "ldflags", a)
        elif o in ("--type"):
            target_type=a

    for platform in args:
        ctx["platforms"].append(platform)

    if 1==len(ctx["platforms"]) and None!=install_zip and 1==len(includes):
        ctx["root"]=os.path.abspath(os.path.split(includes[0])[0])
        generateZipPackage(ctx, includes[0], target_type, install_zip)
    elif 1==len(ctx["platforms"]) and platformSubseteqTest(ctx["platforms"][0], "win32-*-msvc-*") and 1==len(includes):
        ctx["root"]=os.path.abspath(os.path.split(includes[0])[0])
        generateWin32(ctx, includes[0])
    else:
        if is_external(ctx, "zlib") and is_external(ctx, "xml"):
            lib_types=["static", "shared"]
        else:
            lib_types=["static"]
        if not os.path.exists("build"):
            os.makedirs("build")
        f=open(os.path.join("build", "configure.ctx"), "w")
        f.write(str(ctx))
        f.close()        
        for include in includes:
            ctx["root"]=os.path.abspath(os.path.split(include)[0])
            generateTypedMakefiles(ctx, include, lib_types)

    for platform in dump_env:
        dumpEnvironment(ctx, includes, platform)