# Part of the A-A-P recipe executive 

# Copyright (c) 2002-2004 Lars Ivar Igesund and stichting NLnet Labs
# Permission to copy and use this file is specified in the file COPYING.
# If this file is missing you can find it here: http://www.a-a-p.org/COPYING

#
# This module sets up variables and actions for using the DMD compiler tools.
#

from RecPython import *
import Global
from Action import action_add
from Dictlist import str2dictlist
from RecPos import RecPos


def exists():
    """
    Return TRUE when the DMD toolchain can be found.
    """
    if program_path("dmd"):
        if os.name == "nt":
            if program_path("link"):
                return program_path("lib")
            else:
                return 0
        else:
            return program_path("gcc")
    else:
        return 0


def define_actions():
    """
    Define the actions that DMD can accomplish.
    """
    rd = Global.globals
    # Compile one sourcefile at the time
    define_action("compile_dmd", 0, """
        opt =
        @if _no.OPTIMIZE and int(_no.OPTIMIZE) > 0:
          opt = -O
        dbg =
        DEBUG ?=
        @if _no.DEBUG == 'yes':
          dbg = -g
        :sys $DMD $?DFLAGS $?DVERSION $opt $dbg $?DDEBUG $?DIMPORT -of$target 
                -c $source
        """,
        outtypes = ["object", "libobject", "dllobject"],
        intypes = ["d"])

    define_action("compile_dmd", 0, """
        DLINKFLAGS += source
        """,
        intypes = ["def"])

    # Build a program from object files
    define_action("build_dmd", 0, """
        DLINKFLAGS += /DELEXECUTABLE
        dbg =
        DEBUG ?=
        @if _no.DEBUG == 'yes':
            dbg = -g
        opt =
        @if _no.OPTIMIZE and int(_no.OPTIMIZE) > 0:
            opt = -O
        :sys $DMD $?DFLAGS $?DVERSION $opt $dbg $?DDEBUG $?DIMPORT
                -L$*?DLINKFLAGS -L+$*?LIBS -of$target $source
        """,
        outtypes = ["default"],
        intypes = ["object"])

    # Build a static lib from object files
    define_action("buildlib_dmd", 0, """
        @if os.name == "nt":
            :progsearch LIB lib
            :sys $LIB $?DLINKFLAGS -p512 -c $target $source
        @else:
            :sys $DMD $?DLINKFLAGS -of$target $source
        """,
        outtypes = ["default"],
        intypes = ["libobject"])

    # Build a dll from object files  
    define_action("builddll_dmd", 0, """
        DLINKFLAGS += /DELEXECUTABLE
        @if os.name == "nt":
            DIMPLIB ?= 
            @exec "import tools.dmd"
            @if _no.DIMPLIB and _no.DIMPLIB == "yes":
                DLINKFLAGS += /implib
            @if not tools.dmd.find_dll_main_object(source):
                @f = file("aap_dllmain.d", 'w')
                @f.write(tools.dmd.dll_main_source())
                @f.close()
                :do compile {target = aap_dllmain.obj} aap_dllmain.d
                source += aap_dllmain.obj
            @if not tools.dmd.find_def_file(source):
                @f = file("aap_dll.def", 'w')
                @f.write(tools.dmd.dll_def_file(target))
                @f.close()
                source += aap_dll.def
        :sys $DMD -L$*?DLINKFLAGS -L+$*?LIBS -of$target $source
        @if os.name == "nt":
            :del {f} {q} aap_dllmain.*
            :del {f} {q} aap_dll.def
        """,
        outtypes = ["default"],
        intypes = ["dllobject", "def"])

    # Build a program directly from source
    define_action("buildonestep_dmd", 0, """ 
        DLINKFLAGS += /DELEXECUTABLE
        opt =
        @if _no.OPTIMIZE and int(_no.OPTIMIZE) > 0:
            opt = -O
        dbg =
        DEBUG ?=
        @if _no.DEBUG == 'yes':
            dbg = -g
        :sys $DMD $?DFLAGS $?DVERSION $opt $dbg $?DDEBUG $?DIMPORT
                -L$*?DLINKFLAGS -L+$*?LIBS -of$target $source
        :del {f} {q} *.obj
        """,
        outtypes = ["default"],
        intypes = ["d"])

    # Build a static lib directly from source
    define_action("buildlibonestep_dmd", 0, """
        opt =
        @if _no.OPTIMIZE and int(_no.OPTIMIZE) > 0:
            opt = -O
        dbg =
        DEBUG ?=
        @if _no.DEBUG == 'yes':
            dbg = -g
        :sys $DMD -c $?DFLAGS $?DVERSION $opt $dbg $?DDEBUG $?DIMPORT
                -op $source
        :progsearch LIB lib
        BDIR = 
        objects = `src2obj(source)`
        :sys $LIB $?DLINKFLAGS -p512 -c $target $objects
        :del {f} {q} *.obj
        """,
        outtypes = ["default"],
        intypes = ["d"])

    # Build a dll directly from source
    define_action("builddllonestep_dmd", 0, """
        DLINKFLAGS += /DELEXECUTABLE
        opt =
        @if _no.OPTIMIZE and int(_no.OPTIMIZE) > 0:
            opt = -O
        dbg =
        DEBUG ?=
        @if _no.DEBUG == 'yes':
            dbg = -g
        @if os.name == "nt":
            DIMPLIB ?= 
            @if DIMPLIB and DIMPLIB == "yes":
                DLINKFLAGS += -L/implib
            @exec "import tools.dmd"
            @if not tools.dmd.find_dll_main_source(source):
                @f = file("aap_dllmain.d", 'w')
                @f.write(tools.dmd.dll_main_source())
                @f.close()
                source += aap_dllmain.d
            @if not tools.dmd.find_def_file(source):
                @f = file("aap_dll.def", 'w')
                @f.write(tools.dmd.dll_def_file(target))
                @f.close()
                source += aap_dll.def
        :sys $DMD $?DFLAGS $?DVERSION $opt $dbg $?DDEBUG $?DIMPORT
                -L$*?DLINKFLAGS -L+$*?LIBS -of$target $source
        @if os.name == "nt":
            :del {f} {q} aap_dllmain.d
            :del {f} {q} aap_dll.def
        :del {q} *.obj
        """,
        outtypes = ["default"],
        intypes = ["d", "def"])
        
    if not rd["_top"].get("DMD"):
        rd["_top"]["DMD"] = "dmd"


def use_actions(scope):
    """
    Setup variables so that the default actions use the DMD actions.
    """
    scope["D_COMPILE_ACTION"] = "compile_dmd"
    scope["D_BUILD_ACTION"] = "build_dmd"
    scope["D_BUILDLIB_ACTION"] = "buildlib_dmd"
    scope["D_BUILDDLL_ACTION"] = "builddll_dmd"
    scope["D_BUILDONESTEP_ACTION"] = "buildonestep_dmd"
    scope["D_BUILDDLLONESTEP_ACTION"] = "builddllonestep_dmd"
    scope["D_BUILDLIBONESTEP_ACTION"] = "buildlibonestep_dmd"


def find_phobos():
    dmd_path = program_path("dmd")
    import re
    phobos_path = re.sub('bin', 'lib', dmd_path)
    return re.sub('dmd.EXE', '', phobos_path)


def find_dll_main_source(sourcestr):
    import re
    m = re.compile(r"BOOL\s+DllMain\s*\(\s*HINSTANCE")
    for si in var2list(sourcestr):
        f = file2string(si)
        if m.search(f):
            return 1

    return None 

def find_dll_main_object(sourcestr):
    for si in var2list(sourcestr):
        f = file2string(si)
        from string import find
        if find(f, "_DllMain@12") > -1:
            return 1

    return None 

def find_def_file(sourcestr):
    print sourcestr
    for si in var2list(sourcestr):
        from string import find
        if (find(si, ".def") > -1):
            return 1

    return None

def dll_main_source():
    source = """
        import std.c.windows.windows;

        HINSTANCE g_hInst;

        extern (C)
        {
            void gc_init();
            void gc_term();
            void _minit();
            void _moduleCtor();
        }

        export:
        extern (Windows)
        BOOL DllMain(HINSTANCE hInstance,
                     ULONG ulReason,
                     LPVOID pvReserved)
        {   
            switch (ulReason)
            {
                case DLL_PROCESS_ATTACH:
                gc_init();
                _minit();
                _moduleCtor();
                break;

                case DLL_PROCESS_DETACH:
                gc_term();
                break;

                case DLL_THREAD_ATTACH:
                case DLL_THREAD_DETACH:
                return false;
            }
            g_hInst = hInstance;
            return true;
        }
        """
    return source

def dll_def_file(target):
    from string import find
    idx = find(target, ".dll")
    source = "LIBRARY \"" + target[0:idx+4] + "\""
    source = source + """
EXETYPE NT
SUBSYSTEM WINDOWS
CODE SHARED EXECUTE
DATA WRITE
        """
    return source

# vim: set sw=4 et sts=4 tw=79 fo+=l:
