Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
allendowney
GitHub Repository: allendowney/cpython
Path: blob/main/Tools/freeze/checkextensions_win32.py
12 views
1
"""Extension management for Windows.
2
3
Under Windows it is unlikely the .obj files are of use, as special compiler options
4
are needed (primarily to toggle the behavior of "public" symbols.
5
6
I don't consider it worth parsing the MSVC makefiles for compiler options. Even if
7
we get it just right, a specific freeze application may have specific compiler
8
options anyway (eg, to enable or disable specific functionality)
9
10
So my basic strategy is:
11
12
* Have some Windows INI files which "describe" one or more extension modules.
13
(Freeze comes with a default one for all known modules - but you can specify
14
your own).
15
* This description can include:
16
- The MSVC .dsp file for the extension. The .c source file names
17
are extracted from there.
18
- Specific compiler/linker options
19
- Flag to indicate if Unicode compilation is expected.
20
21
At the moment the name and location of this INI file is hardcoded,
22
but an obvious enhancement would be to provide command line options.
23
"""
24
25
import os, sys
26
try:
27
import win32api
28
except ImportError:
29
win32api = None # User has already been warned
30
31
class CExtension:
32
"""An abstraction of an extension implemented in C/C++
33
"""
34
def __init__(self, name, sourceFiles):
35
self.name = name
36
# A list of strings defining additional compiler options.
37
self.sourceFiles = sourceFiles
38
# A list of special compiler options to be applied to
39
# all source modules in this extension.
40
self.compilerOptions = []
41
# A list of .lib files the final .EXE will need.
42
self.linkerLibs = []
43
44
def GetSourceFiles(self):
45
return self.sourceFiles
46
47
def AddCompilerOption(self, option):
48
self.compilerOptions.append(option)
49
def GetCompilerOptions(self):
50
return self.compilerOptions
51
52
def AddLinkerLib(self, lib):
53
self.linkerLibs.append(lib)
54
def GetLinkerLibs(self):
55
return self.linkerLibs
56
57
def checkextensions(unknown, extra_inis, prefix):
58
# Create a table of frozen extensions
59
60
defaultMapName = os.path.join( os.path.split(sys.argv[0])[0], "extensions_win32.ini")
61
if not os.path.isfile(defaultMapName):
62
sys.stderr.write("WARNING: %s can not be found - standard extensions may not be found\n" % defaultMapName)
63
else:
64
# must go on end, so other inis can override.
65
extra_inis.append(defaultMapName)
66
67
ret = []
68
for mod in unknown:
69
for ini in extra_inis:
70
# print "Looking for", mod, "in", win32api.GetFullPathName(ini),"...",
71
defn = get_extension_defn( mod, ini, prefix )
72
if defn is not None:
73
# print "Yay - found it!"
74
ret.append( defn )
75
break
76
# print "Nope!"
77
else: # For not broken!
78
sys.stderr.write("No definition of module %s in any specified map file.\n" % (mod))
79
80
return ret
81
82
def get_extension_defn(moduleName, mapFileName, prefix):
83
if win32api is None: return None
84
os.environ['PYTHONPREFIX'] = prefix
85
dsp = win32api.GetProfileVal(moduleName, "dsp", "", mapFileName)
86
if dsp=="":
87
return None
88
89
# We allow environment variables in the file name
90
dsp = win32api.ExpandEnvironmentStrings(dsp)
91
# If the path to the .DSP file is not absolute, assume it is relative
92
# to the description file.
93
if not os.path.isabs(dsp):
94
dsp = os.path.join( os.path.split(mapFileName)[0], dsp)
95
# Parse it to extract the source files.
96
sourceFiles = parse_dsp(dsp)
97
if sourceFiles is None:
98
return None
99
100
module = CExtension(moduleName, sourceFiles)
101
# Put the path to the DSP into the environment so entries can reference it.
102
os.environ['dsp_path'] = os.path.split(dsp)[0]
103
os.environ['ini_path'] = os.path.split(mapFileName)[0]
104
105
cl_options = win32api.GetProfileVal(moduleName, "cl", "", mapFileName)
106
if cl_options:
107
module.AddCompilerOption(win32api.ExpandEnvironmentStrings(cl_options))
108
109
exclude = win32api.GetProfileVal(moduleName, "exclude", "", mapFileName)
110
exclude = exclude.split()
111
112
if win32api.GetProfileVal(moduleName, "Unicode", 0, mapFileName):
113
module.AddCompilerOption('/D UNICODE /D _UNICODE')
114
115
libs = win32api.GetProfileVal(moduleName, "libs", "", mapFileName).split()
116
for lib in libs:
117
module.AddLinkerLib(win32api.ExpandEnvironmentStrings(lib))
118
119
for exc in exclude:
120
if exc in module.sourceFiles:
121
module.sourceFiles.remove(exc)
122
123
return module
124
125
# Given an MSVC DSP file, locate C source files it uses
126
# returns a list of source files.
127
def parse_dsp(dsp):
128
# print "Processing", dsp
129
# For now, only support
130
ret = []
131
dsp_path, dsp_name = os.path.split(dsp)
132
try:
133
with open(dsp, "r") as fp:
134
lines = fp.readlines()
135
except IOError as msg:
136
sys.stderr.write("%s: %s\n" % (dsp, msg))
137
return None
138
for line in lines:
139
fields = line.strip().split("=", 2)
140
if fields[0]=="SOURCE":
141
if os.path.splitext(fields[1])[1].lower() in ['.cpp', '.c']:
142
ret.append( win32api.GetFullPathName(os.path.join(dsp_path, fields[1] ) ) )
143
return ret
144
145
def write_extension_table(fname, modules):
146
fp = open(fname, "w")
147
try:
148
fp.write (ext_src_header)
149
# Write fn protos
150
for module in modules:
151
# bit of a hack for .pyd's as part of packages.
152
name = module.name.split('.')[-1]
153
fp.write('extern void init%s(void);\n' % (name) )
154
# Write the table
155
fp.write (ext_tab_header)
156
for module in modules:
157
name = module.name.split('.')[-1]
158
fp.write('\t{"%s", init%s},\n' % (name, name) )
159
160
fp.write (ext_tab_footer)
161
fp.write(ext_src_footer)
162
finally:
163
fp.close()
164
165
166
ext_src_header = """\
167
#include "Python.h"
168
"""
169
170
ext_tab_header = """\
171
172
static struct _inittab extensions[] = {
173
"""
174
175
ext_tab_footer = """\
176
/* Sentinel */
177
{0, 0}
178
};
179
"""
180
181
ext_src_footer = """\
182
extern DL_IMPORT(int) PyImport_ExtendInittab(struct _inittab *newtab);
183
184
int PyInitFrozenExtensions()
185
{
186
return PyImport_ExtendInittab(extensions);
187
}
188
189
"""
190
191