1998-05-19 17:18:37 -03:00
|
|
|
"""Extension management for Windows.
|
|
|
|
|
|
|
|
Under Windows it is unlikely the .obj files are of use, as special compiler options
|
2000-07-16 09:04:32 -03:00
|
|
|
are needed (primarily to toggle the behavior of "public" symbols.
|
1998-05-19 17:18:37 -03:00
|
|
|
|
2013-08-25 19:32:56 -03:00
|
|
|
I don't consider it worth parsing the MSVC makefiles for compiler options. Even if
|
1998-05-19 17:18:37 -03:00
|
|
|
we get it just right, a specific freeze application may have specific compiler
|
|
|
|
options anyway (eg, to enable or disable specific functionality)
|
|
|
|
|
2011-03-16 06:05:33 -03:00
|
|
|
So my basic strategy is:
|
1998-05-19 17:18:37 -03:00
|
|
|
|
1998-08-25 11:06:55 -03:00
|
|
|
* Have some Windows INI files which "describe" one or more extension modules.
|
|
|
|
(Freeze comes with a default one for all known modules - but you can specify
|
|
|
|
your own).
|
1998-05-19 17:18:37 -03:00
|
|
|
* This description can include:
|
|
|
|
- The MSVC .dsp file for the extension. The .c source file names
|
2013-08-25 19:32:56 -03:00
|
|
|
are extracted from there.
|
1998-08-25 11:06:55 -03:00
|
|
|
- Specific compiler/linker options
|
1998-05-19 17:18:37 -03:00
|
|
|
- Flag to indicate if Unicode compilation is expected.
|
|
|
|
|
|
|
|
At the moment the name and location of this INI file is hardcoded,
|
|
|
|
but an obvious enhancement would be to provide command line options.
|
|
|
|
"""
|
|
|
|
|
2002-09-11 17:36:02 -03:00
|
|
|
import os, sys
|
1998-05-25 23:51:40 -03:00
|
|
|
try:
|
2004-07-18 03:16:08 -03:00
|
|
|
import win32api
|
1998-05-25 23:51:40 -03:00
|
|
|
except ImportError:
|
2004-07-18 03:16:08 -03:00
|
|
|
win32api = None # User has already been warned
|
1998-05-19 17:18:37 -03:00
|
|
|
|
|
|
|
class CExtension:
|
2004-07-18 03:16:08 -03:00
|
|
|
"""An abstraction of an extension implemented in C/C++
|
|
|
|
"""
|
|
|
|
def __init__(self, name, sourceFiles):
|
|
|
|
self.name = name
|
|
|
|
# A list of strings defining additional compiler options.
|
|
|
|
self.sourceFiles = sourceFiles
|
|
|
|
# A list of special compiler options to be applied to
|
|
|
|
# all source modules in this extension.
|
|
|
|
self.compilerOptions = []
|
|
|
|
# A list of .lib files the final .EXE will need.
|
|
|
|
self.linkerLibs = []
|
|
|
|
|
|
|
|
def GetSourceFiles(self):
|
|
|
|
return self.sourceFiles
|
|
|
|
|
|
|
|
def AddCompilerOption(self, option):
|
|
|
|
self.compilerOptions.append(option)
|
|
|
|
def GetCompilerOptions(self):
|
|
|
|
return self.compilerOptions
|
|
|
|
|
|
|
|
def AddLinkerLib(self, lib):
|
|
|
|
self.linkerLibs.append(lib)
|
|
|
|
def GetLinkerLibs(self):
|
|
|
|
return self.linkerLibs
|
1998-05-19 17:18:37 -03:00
|
|
|
|
2000-07-13 12:45:17 -03:00
|
|
|
def checkextensions(unknown, extra_inis, prefix):
|
2004-07-18 03:16:08 -03:00
|
|
|
# Create a table of frozen extensions
|
|
|
|
|
|
|
|
defaultMapName = os.path.join( os.path.split(sys.argv[0])[0], "extensions_win32.ini")
|
|
|
|
if not os.path.isfile(defaultMapName):
|
|
|
|
sys.stderr.write("WARNING: %s can not be found - standard extensions may not be found\n" % defaultMapName)
|
|
|
|
else:
|
|
|
|
# must go on end, so other inis can override.
|
|
|
|
extra_inis.append(defaultMapName)
|
|
|
|
|
|
|
|
ret = []
|
|
|
|
for mod in unknown:
|
|
|
|
for ini in extra_inis:
|
|
|
|
# print "Looking for", mod, "in", win32api.GetFullPathName(ini),"...",
|
|
|
|
defn = get_extension_defn( mod, ini, prefix )
|
|
|
|
if defn is not None:
|
|
|
|
# print "Yay - found it!"
|
|
|
|
ret.append( defn )
|
|
|
|
break
|
|
|
|
# print "Nope!"
|
|
|
|
else: # For not broken!
|
|
|
|
sys.stderr.write("No definition of module %s in any specified map file.\n" % (mod))
|
|
|
|
|
|
|
|
return ret
|
1998-05-19 17:18:37 -03:00
|
|
|
|
2000-07-13 12:45:17 -03:00
|
|
|
def get_extension_defn(moduleName, mapFileName, prefix):
|
2004-07-18 03:16:08 -03:00
|
|
|
if win32api is None: return None
|
|
|
|
os.environ['PYTHONPREFIX'] = prefix
|
|
|
|
dsp = win32api.GetProfileVal(moduleName, "dsp", "", mapFileName)
|
|
|
|
if dsp=="":
|
|
|
|
return None
|
|
|
|
|
|
|
|
# We allow environment variables in the file name
|
|
|
|
dsp = win32api.ExpandEnvironmentStrings(dsp)
|
|
|
|
# If the path to the .DSP file is not absolute, assume it is relative
|
|
|
|
# to the description file.
|
|
|
|
if not os.path.isabs(dsp):
|
|
|
|
dsp = os.path.join( os.path.split(mapFileName)[0], dsp)
|
|
|
|
# Parse it to extract the source files.
|
|
|
|
sourceFiles = parse_dsp(dsp)
|
|
|
|
if sourceFiles is None:
|
|
|
|
return None
|
|
|
|
|
|
|
|
module = CExtension(moduleName, sourceFiles)
|
|
|
|
# Put the path to the DSP into the environment so entries can reference it.
|
|
|
|
os.environ['dsp_path'] = os.path.split(dsp)[0]
|
|
|
|
os.environ['ini_path'] = os.path.split(mapFileName)[0]
|
|
|
|
|
|
|
|
cl_options = win32api.GetProfileVal(moduleName, "cl", "", mapFileName)
|
|
|
|
if cl_options:
|
|
|
|
module.AddCompilerOption(win32api.ExpandEnvironmentStrings(cl_options))
|
|
|
|
|
|
|
|
exclude = win32api.GetProfileVal(moduleName, "exclude", "", mapFileName)
|
|
|
|
exclude = exclude.split()
|
|
|
|
|
|
|
|
if win32api.GetProfileVal(moduleName, "Unicode", 0, mapFileName):
|
|
|
|
module.AddCompilerOption('/D UNICODE /D _UNICODE')
|
|
|
|
|
|
|
|
libs = win32api.GetProfileVal(moduleName, "libs", "", mapFileName).split()
|
|
|
|
for lib in libs:
|
|
|
|
module.AddLinkerLib(win32api.ExpandEnvironmentStrings(lib))
|
|
|
|
|
|
|
|
for exc in exclude:
|
|
|
|
if exc in module.sourceFiles:
|
2015-10-30 01:10:57 -03:00
|
|
|
module.sourceFiles.remove(exc)
|
2004-07-18 03:16:08 -03:00
|
|
|
|
|
|
|
return module
|
1998-05-19 17:18:37 -03:00
|
|
|
|
|
|
|
# Given an MSVC DSP file, locate C source files it uses
|
|
|
|
# returns a list of source files.
|
|
|
|
def parse_dsp(dsp):
|
2004-07-18 03:16:08 -03:00
|
|
|
# print "Processing", dsp
|
|
|
|
# For now, only support
|
|
|
|
ret = []
|
|
|
|
dsp_path, dsp_name = os.path.split(dsp)
|
|
|
|
try:
|
2019-03-30 03:33:02 -03:00
|
|
|
with open(dsp, "r") as fp:
|
|
|
|
lines = fp.readlines()
|
2007-01-10 12:19:56 -04:00
|
|
|
except IOError as msg:
|
2004-07-18 03:16:08 -03:00
|
|
|
sys.stderr.write("%s: %s\n" % (dsp, msg))
|
|
|
|
return None
|
|
|
|
for line in lines:
|
|
|
|
fields = line.strip().split("=", 2)
|
|
|
|
if fields[0]=="SOURCE":
|
|
|
|
if os.path.splitext(fields[1])[1].lower() in ['.cpp', '.c']:
|
|
|
|
ret.append( win32api.GetFullPathName(os.path.join(dsp_path, fields[1] ) ) )
|
|
|
|
return ret
|
1998-05-19 17:18:37 -03:00
|
|
|
|
|
|
|
def write_extension_table(fname, modules):
|
2004-07-18 03:16:08 -03:00
|
|
|
fp = open(fname, "w")
|
|
|
|
try:
|
|
|
|
fp.write (ext_src_header)
|
|
|
|
# Write fn protos
|
|
|
|
for module in modules:
|
|
|
|
# bit of a hack for .pyd's as part of packages.
|
|
|
|
name = module.name.split('.')[-1]
|
|
|
|
fp.write('extern void init%s(void);\n' % (name) )
|
|
|
|
# Write the table
|
|
|
|
fp.write (ext_tab_header)
|
|
|
|
for module in modules:
|
|
|
|
name = module.name.split('.')[-1]
|
|
|
|
fp.write('\t{"%s", init%s},\n' % (name, name) )
|
|
|
|
|
|
|
|
fp.write (ext_tab_footer)
|
|
|
|
fp.write(ext_src_footer)
|
|
|
|
finally:
|
|
|
|
fp.close()
|
1998-05-19 17:18:37 -03:00
|
|
|
|
|
|
|
|
|
|
|
ext_src_header = """\
|
|
|
|
#include "Python.h"
|
|
|
|
"""
|
|
|
|
|
|
|
|
ext_tab_header = """\
|
|
|
|
|
|
|
|
static struct _inittab extensions[] = {
|
|
|
|
"""
|
|
|
|
|
|
|
|
ext_tab_footer = """\
|
|
|
|
/* Sentinel */
|
|
|
|
{0, 0}
|
|
|
|
};
|
|
|
|
"""
|
|
|
|
|
|
|
|
ext_src_footer = """\
|
1999-11-02 11:44:40 -04:00
|
|
|
extern DL_IMPORT(int) PyImport_ExtendInittab(struct _inittab *newtab);
|
1998-05-19 17:18:37 -03:00
|
|
|
|
|
|
|
int PyInitFrozenExtensions()
|
|
|
|
{
|
2004-07-18 03:16:08 -03:00
|
|
|
return PyImport_ExtendInittab(extensions);
|
1998-05-19 17:18:37 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
"""
|