17db96d56Sopenharmony_ci"""Extension management for Windows.
27db96d56Sopenharmony_ci
37db96d56Sopenharmony_ciUnder Windows it is unlikely the .obj files are of use, as special compiler options
47db96d56Sopenharmony_ciare needed (primarily to toggle the behavior of "public" symbols.
57db96d56Sopenharmony_ci
67db96d56Sopenharmony_ciI don't consider it worth parsing the MSVC makefiles for compiler options.  Even if
77db96d56Sopenharmony_ciwe get it just right, a specific freeze application may have specific compiler
87db96d56Sopenharmony_cioptions anyway (eg, to enable or disable specific functionality)
97db96d56Sopenharmony_ci
107db96d56Sopenharmony_ciSo my basic strategy is:
117db96d56Sopenharmony_ci
127db96d56Sopenharmony_ci* Have some Windows INI files which "describe" one or more extension modules.
137db96d56Sopenharmony_ci  (Freeze comes with a default one for all known modules - but you can specify
147db96d56Sopenharmony_ci  your own).
157db96d56Sopenharmony_ci* This description can include:
167db96d56Sopenharmony_ci  - The MSVC .dsp file for the extension.  The .c source file names
177db96d56Sopenharmony_ci    are extracted from there.
187db96d56Sopenharmony_ci  - Specific compiler/linker options
197db96d56Sopenharmony_ci  - Flag to indicate if Unicode compilation is expected.
207db96d56Sopenharmony_ci
217db96d56Sopenharmony_ciAt the moment the name and location of this INI file is hardcoded,
227db96d56Sopenharmony_cibut an obvious enhancement would be to provide command line options.
237db96d56Sopenharmony_ci"""
247db96d56Sopenharmony_ci
257db96d56Sopenharmony_ciimport os, sys
267db96d56Sopenharmony_citry:
277db96d56Sopenharmony_ci    import win32api
287db96d56Sopenharmony_ciexcept ImportError:
297db96d56Sopenharmony_ci    win32api = None # User has already been warned
307db96d56Sopenharmony_ci
317db96d56Sopenharmony_ciclass CExtension:
327db96d56Sopenharmony_ci    """An abstraction of an extension implemented in C/C++
337db96d56Sopenharmony_ci    """
347db96d56Sopenharmony_ci    def __init__(self, name, sourceFiles):
357db96d56Sopenharmony_ci        self.name = name
367db96d56Sopenharmony_ci        # A list of strings defining additional compiler options.
377db96d56Sopenharmony_ci        self.sourceFiles = sourceFiles
387db96d56Sopenharmony_ci        # A list of special compiler options to be applied to
397db96d56Sopenharmony_ci        # all source modules in this extension.
407db96d56Sopenharmony_ci        self.compilerOptions = []
417db96d56Sopenharmony_ci        # A list of .lib files the final .EXE will need.
427db96d56Sopenharmony_ci        self.linkerLibs = []
437db96d56Sopenharmony_ci
447db96d56Sopenharmony_ci    def GetSourceFiles(self):
457db96d56Sopenharmony_ci        return self.sourceFiles
467db96d56Sopenharmony_ci
477db96d56Sopenharmony_ci    def AddCompilerOption(self, option):
487db96d56Sopenharmony_ci        self.compilerOptions.append(option)
497db96d56Sopenharmony_ci    def GetCompilerOptions(self):
507db96d56Sopenharmony_ci        return self.compilerOptions
517db96d56Sopenharmony_ci
527db96d56Sopenharmony_ci    def AddLinkerLib(self, lib):
537db96d56Sopenharmony_ci        self.linkerLibs.append(lib)
547db96d56Sopenharmony_ci    def GetLinkerLibs(self):
557db96d56Sopenharmony_ci        return self.linkerLibs
567db96d56Sopenharmony_ci
577db96d56Sopenharmony_cidef checkextensions(unknown, extra_inis, prefix):
587db96d56Sopenharmony_ci    # Create a table of frozen extensions
597db96d56Sopenharmony_ci
607db96d56Sopenharmony_ci    defaultMapName = os.path.join( os.path.split(sys.argv[0])[0], "extensions_win32.ini")
617db96d56Sopenharmony_ci    if not os.path.isfile(defaultMapName):
627db96d56Sopenharmony_ci        sys.stderr.write("WARNING: %s can not be found - standard extensions may not be found\n" % defaultMapName)
637db96d56Sopenharmony_ci    else:
647db96d56Sopenharmony_ci        # must go on end, so other inis can override.
657db96d56Sopenharmony_ci        extra_inis.append(defaultMapName)
667db96d56Sopenharmony_ci
677db96d56Sopenharmony_ci    ret = []
687db96d56Sopenharmony_ci    for mod in unknown:
697db96d56Sopenharmony_ci        for ini in extra_inis:
707db96d56Sopenharmony_ci#                       print "Looking for", mod, "in", win32api.GetFullPathName(ini),"...",
717db96d56Sopenharmony_ci            defn = get_extension_defn( mod, ini, prefix )
727db96d56Sopenharmony_ci            if defn is not None:
737db96d56Sopenharmony_ci#                               print "Yay - found it!"
747db96d56Sopenharmony_ci                ret.append( defn )
757db96d56Sopenharmony_ci                break
767db96d56Sopenharmony_ci#                       print "Nope!"
777db96d56Sopenharmony_ci        else: # For not broken!
787db96d56Sopenharmony_ci            sys.stderr.write("No definition of module %s in any specified map file.\n" % (mod))
797db96d56Sopenharmony_ci
807db96d56Sopenharmony_ci    return ret
817db96d56Sopenharmony_ci
827db96d56Sopenharmony_cidef get_extension_defn(moduleName, mapFileName, prefix):
837db96d56Sopenharmony_ci    if win32api is None: return None
847db96d56Sopenharmony_ci    os.environ['PYTHONPREFIX'] = prefix
857db96d56Sopenharmony_ci    dsp = win32api.GetProfileVal(moduleName, "dsp", "", mapFileName)
867db96d56Sopenharmony_ci    if dsp=="":
877db96d56Sopenharmony_ci        return None
887db96d56Sopenharmony_ci
897db96d56Sopenharmony_ci    # We allow environment variables in the file name
907db96d56Sopenharmony_ci    dsp = win32api.ExpandEnvironmentStrings(dsp)
917db96d56Sopenharmony_ci    # If the path to the .DSP file is not absolute, assume it is relative
927db96d56Sopenharmony_ci    # to the description file.
937db96d56Sopenharmony_ci    if not os.path.isabs(dsp):
947db96d56Sopenharmony_ci        dsp = os.path.join( os.path.split(mapFileName)[0], dsp)
957db96d56Sopenharmony_ci    # Parse it to extract the source files.
967db96d56Sopenharmony_ci    sourceFiles = parse_dsp(dsp)
977db96d56Sopenharmony_ci    if sourceFiles is None:
987db96d56Sopenharmony_ci        return None
997db96d56Sopenharmony_ci
1007db96d56Sopenharmony_ci    module = CExtension(moduleName, sourceFiles)
1017db96d56Sopenharmony_ci    # Put the path to the DSP into the environment so entries can reference it.
1027db96d56Sopenharmony_ci    os.environ['dsp_path'] = os.path.split(dsp)[0]
1037db96d56Sopenharmony_ci    os.environ['ini_path'] = os.path.split(mapFileName)[0]
1047db96d56Sopenharmony_ci
1057db96d56Sopenharmony_ci    cl_options = win32api.GetProfileVal(moduleName, "cl", "", mapFileName)
1067db96d56Sopenharmony_ci    if cl_options:
1077db96d56Sopenharmony_ci        module.AddCompilerOption(win32api.ExpandEnvironmentStrings(cl_options))
1087db96d56Sopenharmony_ci
1097db96d56Sopenharmony_ci    exclude = win32api.GetProfileVal(moduleName, "exclude", "", mapFileName)
1107db96d56Sopenharmony_ci    exclude = exclude.split()
1117db96d56Sopenharmony_ci
1127db96d56Sopenharmony_ci    if win32api.GetProfileVal(moduleName, "Unicode", 0, mapFileName):
1137db96d56Sopenharmony_ci        module.AddCompilerOption('/D UNICODE /D _UNICODE')
1147db96d56Sopenharmony_ci
1157db96d56Sopenharmony_ci    libs = win32api.GetProfileVal(moduleName, "libs", "", mapFileName).split()
1167db96d56Sopenharmony_ci    for lib in libs:
1177db96d56Sopenharmony_ci        module.AddLinkerLib(win32api.ExpandEnvironmentStrings(lib))
1187db96d56Sopenharmony_ci
1197db96d56Sopenharmony_ci    for exc in exclude:
1207db96d56Sopenharmony_ci        if exc in module.sourceFiles:
1217db96d56Sopenharmony_ci            module.sourceFiles.remove(exc)
1227db96d56Sopenharmony_ci
1237db96d56Sopenharmony_ci    return module
1247db96d56Sopenharmony_ci
1257db96d56Sopenharmony_ci# Given an MSVC DSP file, locate C source files it uses
1267db96d56Sopenharmony_ci# returns a list of source files.
1277db96d56Sopenharmony_cidef parse_dsp(dsp):
1287db96d56Sopenharmony_ci#       print "Processing", dsp
1297db96d56Sopenharmony_ci    # For now, only support
1307db96d56Sopenharmony_ci    ret = []
1317db96d56Sopenharmony_ci    dsp_path, dsp_name = os.path.split(dsp)
1327db96d56Sopenharmony_ci    try:
1337db96d56Sopenharmony_ci        with open(dsp, "r") as fp:
1347db96d56Sopenharmony_ci            lines = fp.readlines()
1357db96d56Sopenharmony_ci    except IOError as msg:
1367db96d56Sopenharmony_ci        sys.stderr.write("%s: %s\n" % (dsp, msg))
1377db96d56Sopenharmony_ci        return None
1387db96d56Sopenharmony_ci    for line in lines:
1397db96d56Sopenharmony_ci        fields = line.strip().split("=", 2)
1407db96d56Sopenharmony_ci        if fields[0]=="SOURCE":
1417db96d56Sopenharmony_ci            if os.path.splitext(fields[1])[1].lower() in ['.cpp', '.c']:
1427db96d56Sopenharmony_ci                ret.append( win32api.GetFullPathName(os.path.join(dsp_path, fields[1] ) ) )
1437db96d56Sopenharmony_ci    return ret
1447db96d56Sopenharmony_ci
1457db96d56Sopenharmony_cidef write_extension_table(fname, modules):
1467db96d56Sopenharmony_ci    fp = open(fname, "w")
1477db96d56Sopenharmony_ci    try:
1487db96d56Sopenharmony_ci        fp.write (ext_src_header)
1497db96d56Sopenharmony_ci        # Write fn protos
1507db96d56Sopenharmony_ci        for module in modules:
1517db96d56Sopenharmony_ci            # bit of a hack for .pyd's as part of packages.
1527db96d56Sopenharmony_ci            name = module.name.split('.')[-1]
1537db96d56Sopenharmony_ci            fp.write('extern void init%s(void);\n' % (name) )
1547db96d56Sopenharmony_ci        # Write the table
1557db96d56Sopenharmony_ci        fp.write (ext_tab_header)
1567db96d56Sopenharmony_ci        for module in modules:
1577db96d56Sopenharmony_ci            name = module.name.split('.')[-1]
1587db96d56Sopenharmony_ci            fp.write('\t{"%s", init%s},\n' % (name, name) )
1597db96d56Sopenharmony_ci
1607db96d56Sopenharmony_ci        fp.write (ext_tab_footer)
1617db96d56Sopenharmony_ci        fp.write(ext_src_footer)
1627db96d56Sopenharmony_ci    finally:
1637db96d56Sopenharmony_ci        fp.close()
1647db96d56Sopenharmony_ci
1657db96d56Sopenharmony_ci
1667db96d56Sopenharmony_ciext_src_header = """\
1677db96d56Sopenharmony_ci#include "Python.h"
1687db96d56Sopenharmony_ci"""
1697db96d56Sopenharmony_ci
1707db96d56Sopenharmony_ciext_tab_header = """\
1717db96d56Sopenharmony_ci
1727db96d56Sopenharmony_cistatic struct _inittab extensions[] = {
1737db96d56Sopenharmony_ci"""
1747db96d56Sopenharmony_ci
1757db96d56Sopenharmony_ciext_tab_footer = """\
1767db96d56Sopenharmony_ci        /* Sentinel */
1777db96d56Sopenharmony_ci        {0, 0}
1787db96d56Sopenharmony_ci};
1797db96d56Sopenharmony_ci"""
1807db96d56Sopenharmony_ci
1817db96d56Sopenharmony_ciext_src_footer = """\
1827db96d56Sopenharmony_ciextern DL_IMPORT(int) PyImport_ExtendInittab(struct _inittab *newtab);
1837db96d56Sopenharmony_ci
1847db96d56Sopenharmony_ciint PyInitFrozenExtensions()
1857db96d56Sopenharmony_ci{
1867db96d56Sopenharmony_ci        return PyImport_ExtendInittab(extensions);
1877db96d56Sopenharmony_ci}
1887db96d56Sopenharmony_ci
1897db96d56Sopenharmony_ci"""
190