ยปCore Development>Code coverage>Tools/freeze/freeze.py

Python code coverage for Tools/freeze/freeze.py

#countcontent
1n/a#! /usr/bin/env python3
2n/a
3n/a"""Freeze a Python script into a binary.
4n/a
5n/ausage: freeze [options...] script [module]...
6n/a
7n/aOptions:
8n/a-p prefix: This is the prefix used when you ran ``make install''
9n/a in the Python build directory.
10n/a (If you never ran this, freeze won't work.)
11n/a The default is whatever sys.prefix evaluates to.
12n/a It can also be the top directory of the Python source
13n/a tree; then -P must point to the build tree.
14n/a
15n/a-P exec_prefix: Like -p but this is the 'exec_prefix', used to
16n/a install objects etc. The default is whatever sys.exec_prefix
17n/a evaluates to, or the -p argument if given.
18n/a If -p points to the Python source tree, -P must point
19n/a to the build tree, if different.
20n/a
21n/a-e extension: A directory containing additional .o files that
22n/a may be used to resolve modules. This directory
23n/a should also have a Setup file describing the .o files.
24n/a On Windows, the name of a .INI file describing one
25n/a or more extensions is passed.
26n/a More than one -e option may be given.
27n/a
28n/a-o dir: Directory where the output files are created; default '.'.
29n/a
30n/a-m: Additional arguments are module names instead of filenames.
31n/a
32n/a-a package=dir: Additional directories to be added to the package's
33n/a __path__. Used to simulate directories added by the
34n/a package at runtime (eg, by OpenGL and win32com).
35n/a More than one -a option may be given for each package.
36n/a
37n/a-l file: Pass the file to the linker (windows only)
38n/a
39n/a-d: Debugging mode for the module finder.
40n/a
41n/a-q: Make the module finder totally quiet.
42n/a
43n/a-h: Print this help message.
44n/a
45n/a-x module Exclude the specified module. It will still be imported
46n/a by the frozen binary if it exists on the host system.
47n/a
48n/a-X module Like -x, except the module can never be imported by
49n/a the frozen binary.
50n/a
51n/a-E: Freeze will fail if any modules can't be found (that
52n/a were not excluded using -x or -X).
53n/a
54n/a-i filename: Include a file with additional command line options. Used
55n/a to prevent command lines growing beyond the capabilities of
56n/a the shell/OS. All arguments specified in filename
57n/a are read and the -i option replaced with the parsed
58n/a params (note - quoting args in this file is NOT supported)
59n/a
60n/a-s subsystem: Specify the subsystem (For Windows only.);
61n/a 'console' (default), 'windows', 'service' or 'com_dll'
62n/a
63n/a-w: Toggle Windows (NT or 95) behavior.
64n/a (For debugging only -- on a win32 platform, win32 behavior
65n/a is automatic.)
66n/a
67n/a-r prefix=f: Replace path prefix.
68n/a Replace prefix with f in the source path references
69n/a contained in the resulting binary.
70n/a
71n/aArguments:
72n/a
73n/ascript: The Python script to be executed by the resulting binary.
74n/a
75n/amodule ...: Additional Python modules (referenced by pathname)
76n/a that will be included in the resulting binary. These
77n/a may be .py or .pyc files. If -m is specified, these are
78n/a module names that are search in the path instead.
79n/a
80n/aNOTES:
81n/a
82n/aIn order to use freeze successfully, you must have built Python and
83n/ainstalled it ("make install").
84n/a
85n/aThe script should not use modules provided only as shared libraries;
86n/aif it does, the resulting binary is not self-contained.
87n/a"""
88n/a
89n/a
90n/a# Import standard modules
91n/a
92n/aimport modulefinder
93n/aimport getopt
94n/aimport os
95n/aimport sys
96n/a
97n/a
98n/a# Import the freeze-private modules
99n/a
100n/aimport checkextensions
101n/aimport makeconfig
102n/aimport makefreeze
103n/aimport makemakefile
104n/aimport parsesetup
105n/aimport bkfile
106n/a
107n/a
108n/a# Main program
109n/a
110n/adef main():
111n/a # overridable context
112n/a prefix = None # settable with -p option
113n/a exec_prefix = None # settable with -P option
114n/a extensions = []
115n/a exclude = [] # settable with -x option
116n/a addn_link = [] # settable with -l, but only honored under Windows.
117n/a path = sys.path[:]
118n/a modargs = 0
119n/a debug = 1
120n/a odir = ''
121n/a win = sys.platform[:3] == 'win'
122n/a replace_paths = [] # settable with -r option
123n/a error_if_any_missing = 0
124n/a
125n/a # default the exclude list for each platform
126n/a if win: exclude = exclude + [
127n/a 'dos', 'dospath', 'mac', 'macpath', 'macfs', 'MACFS', 'posix', ]
128n/a
129n/a fail_import = exclude[:]
130n/a
131n/a # output files
132n/a frozen_c = 'frozen.c'
133n/a config_c = 'config.c'
134n/a target = 'a.out' # normally derived from script name
135n/a makefile = 'Makefile'
136n/a subsystem = 'console'
137n/a
138n/a # parse command line by first replacing any "-i" options with the
139n/a # file contents.
140n/a pos = 1
141n/a while pos < len(sys.argv)-1:
142n/a # last option can not be "-i", so this ensures "pos+1" is in range!
143n/a if sys.argv[pos] == '-i':
144n/a try:
145n/a options = open(sys.argv[pos+1]).read().split()
146n/a except IOError as why:
147n/a usage("File name '%s' specified with the -i option "
148n/a "can not be read - %s" % (sys.argv[pos+1], why) )
149n/a # Replace the '-i' and the filename with the read params.
150n/a sys.argv[pos:pos+2] = options
151n/a pos = pos + len(options) - 1 # Skip the name and the included args.
152n/a pos = pos + 1
153n/a
154n/a # Now parse the command line with the extras inserted.
155n/a try:
156n/a opts, args = getopt.getopt(sys.argv[1:], 'r:a:dEe:hmo:p:P:qs:wX:x:l:')
157n/a except getopt.error as msg:
158n/a usage('getopt error: ' + str(msg))
159n/a
160n/a # process option arguments
161n/a for o, a in opts:
162n/a if o == '-h':
163n/a print(__doc__)
164n/a return
165n/a if o == '-d':
166n/a debug = debug + 1
167n/a if o == '-e':
168n/a extensions.append(a)
169n/a if o == '-m':
170n/a modargs = 1
171n/a if o == '-o':
172n/a odir = a
173n/a if o == '-p':
174n/a prefix = a
175n/a if o == '-P':
176n/a exec_prefix = a
177n/a if o == '-q':
178n/a debug = 0
179n/a if o == '-w':
180n/a win = not win
181n/a if o == '-s':
182n/a if not win:
183n/a usage("-s subsystem option only on Windows")
184n/a subsystem = a
185n/a if o == '-x':
186n/a exclude.append(a)
187n/a if o == '-X':
188n/a exclude.append(a)
189n/a fail_import.append(a)
190n/a if o == '-E':
191n/a error_if_any_missing = 1
192n/a if o == '-l':
193n/a addn_link.append(a)
194n/a if o == '-a':
195n/a modulefinder.AddPackagePath(*a.split("=", 2))
196n/a if o == '-r':
197n/a f,r = a.split("=", 2)
198n/a replace_paths.append( (f,r) )
199n/a
200n/a # modules that are imported by the Python runtime
201n/a implicits = []
202n/a for module in ('site', 'warnings', 'encodings.utf_8', 'encodings.latin_1'):
203n/a if module not in exclude:
204n/a implicits.append(module)
205n/a
206n/a # default prefix and exec_prefix
207n/a if not exec_prefix:
208n/a if prefix:
209n/a exec_prefix = prefix
210n/a else:
211n/a exec_prefix = sys.exec_prefix
212n/a if not prefix:
213n/a prefix = sys.prefix
214n/a
215n/a # determine whether -p points to the Python source tree
216n/a ishome = os.path.exists(os.path.join(prefix, 'Python', 'ceval.c'))
217n/a
218n/a # locations derived from options
219n/a version = '%d.%d' % sys.version_info[:2]
220n/a flagged_version = version + sys.abiflags
221n/a if win:
222n/a extensions_c = 'frozen_extensions.c'
223n/a if ishome:
224n/a print("(Using Python source directory)")
225n/a binlib = exec_prefix
226n/a incldir = os.path.join(prefix, 'Include')
227n/a config_h_dir = exec_prefix
228n/a config_c_in = os.path.join(prefix, 'Modules', 'config.c.in')
229n/a frozenmain_c = os.path.join(prefix, 'Python', 'frozenmain.c')
230n/a makefile_in = os.path.join(exec_prefix, 'Makefile')
231n/a if win:
232n/a frozendllmain_c = os.path.join(exec_prefix, 'Pc\\frozen_dllmain.c')
233n/a else:
234n/a binlib = os.path.join(exec_prefix,
235n/a 'lib', 'python%s' % version,
236n/a 'config-%s' % flagged_version)
237n/a incldir = os.path.join(prefix, 'include', 'python%s' % flagged_version)
238n/a config_h_dir = os.path.join(exec_prefix, 'include',
239n/a 'python%s' % flagged_version)
240n/a config_c_in = os.path.join(binlib, 'config.c.in')
241n/a frozenmain_c = os.path.join(binlib, 'frozenmain.c')
242n/a makefile_in = os.path.join(binlib, 'Makefile')
243n/a frozendllmain_c = os.path.join(binlib, 'frozen_dllmain.c')
244n/a supp_sources = []
245n/a defines = []
246n/a includes = ['-I' + incldir, '-I' + config_h_dir]
247n/a
248n/a # sanity check of directories and files
249n/a check_dirs = [prefix, exec_prefix, binlib, incldir]
250n/a if not win:
251n/a # These are not directories on Windows.
252n/a check_dirs = check_dirs + extensions
253n/a for dir in check_dirs:
254n/a if not os.path.exists(dir):
255n/a usage('needed directory %s not found' % dir)
256n/a if not os.path.isdir(dir):
257n/a usage('%s: not a directory' % dir)
258n/a if win:
259n/a files = supp_sources + extensions # extensions are files on Windows.
260n/a else:
261n/a files = [config_c_in, makefile_in] + supp_sources
262n/a for file in supp_sources:
263n/a if not os.path.exists(file):
264n/a usage('needed file %s not found' % file)
265n/a if not os.path.isfile(file):
266n/a usage('%s: not a plain file' % file)
267n/a if not win:
268n/a for dir in extensions:
269n/a setup = os.path.join(dir, 'Setup')
270n/a if not os.path.exists(setup):
271n/a usage('needed file %s not found' % setup)
272n/a if not os.path.isfile(setup):
273n/a usage('%s: not a plain file' % setup)
274n/a
275n/a # check that enough arguments are passed
276n/a if not args:
277n/a usage('at least one filename argument required')
278n/a
279n/a # check that file arguments exist
280n/a for arg in args:
281n/a if arg == '-m':
282n/a break
283n/a # if user specified -m on the command line before _any_
284n/a # file names, then nothing should be checked (as the
285n/a # very first file should be a module name)
286n/a if modargs:
287n/a break
288n/a if not os.path.exists(arg):
289n/a usage('argument %s not found' % arg)
290n/a if not os.path.isfile(arg):
291n/a usage('%s: not a plain file' % arg)
292n/a
293n/a # process non-option arguments
294n/a scriptfile = args[0]
295n/a modules = args[1:]
296n/a
297n/a # derive target name from script name
298n/a base = os.path.basename(scriptfile)
299n/a base, ext = os.path.splitext(base)
300n/a if base:
301n/a if base != scriptfile:
302n/a target = base
303n/a else:
304n/a target = base + '.bin'
305n/a
306n/a # handle -o option
307n/a base_frozen_c = frozen_c
308n/a base_config_c = config_c
309n/a base_target = target
310n/a if odir and not os.path.isdir(odir):
311n/a try:
312n/a os.mkdir(odir)
313n/a print("Created output directory", odir)
314n/a except OSError as msg:
315n/a usage('%s: mkdir failed (%s)' % (odir, str(msg)))
316n/a base = ''
317n/a if odir:
318n/a base = os.path.join(odir, '')
319n/a frozen_c = os.path.join(odir, frozen_c)
320n/a config_c = os.path.join(odir, config_c)
321n/a target = os.path.join(odir, target)
322n/a makefile = os.path.join(odir, makefile)
323n/a if win: extensions_c = os.path.join(odir, extensions_c)
324n/a
325n/a # Handle special entry point requirements
326n/a # (on Windows, some frozen programs do not use __main__, but
327n/a # import the module directly. Eg, DLLs, Services, etc
328n/a custom_entry_point = None # Currently only used on Windows
329n/a python_entry_is_main = 1 # Is the entry point called __main__?
330n/a # handle -s option on Windows
331n/a if win:
332n/a import winmakemakefile
333n/a try:
334n/a custom_entry_point, python_entry_is_main = \
335n/a winmakemakefile.get_custom_entry_point(subsystem)
336n/a except ValueError as why:
337n/a usage(why)
338n/a
339n/a
340n/a # Actual work starts here...
341n/a
342n/a # collect all modules of the program
343n/a dir = os.path.dirname(scriptfile)
344n/a path[0] = dir
345n/a mf = modulefinder.ModuleFinder(path, debug, exclude, replace_paths)
346n/a
347n/a if win and subsystem=='service':
348n/a # If a Windows service, then add the "built-in" module.
349n/a mod = mf.add_module("servicemanager")
350n/a mod.__file__="dummy.pyd" # really built-in to the resulting EXE
351n/a
352n/a for mod in implicits:
353n/a mf.import_hook(mod)
354n/a for mod in modules:
355n/a if mod == '-m':
356n/a modargs = 1
357n/a continue
358n/a if modargs:
359n/a if mod[-2:] == '.*':
360n/a mf.import_hook(mod[:-2], None, ["*"])
361n/a else:
362n/a mf.import_hook(mod)
363n/a else:
364n/a mf.load_file(mod)
365n/a
366n/a # Alias "importlib._bootstrap" to "_frozen_importlib" so that the
367n/a # import machinery can bootstrap. Do the same for
368n/a # importlib._bootstrap_external.
369n/a mf.modules["_frozen_importlib"] = mf.modules["importlib._bootstrap"]
370n/a mf.modules["_frozen_importlib_external"] = mf.modules["importlib._bootstrap_external"]
371n/a
372n/a # Add the main script as either __main__, or the actual module name.
373n/a if python_entry_is_main:
374n/a mf.run_script(scriptfile)
375n/a else:
376n/a mf.load_file(scriptfile)
377n/a
378n/a if debug > 0:
379n/a mf.report()
380n/a print()
381n/a dict = mf.modules
382n/a
383n/a if error_if_any_missing:
384n/a missing = mf.any_missing()
385n/a if missing:
386n/a sys.exit("There are some missing modules: %r" % missing)
387n/a
388n/a # generate output for frozen modules
389n/a files = makefreeze.makefreeze(base, dict, debug, custom_entry_point,
390n/a fail_import)
391n/a
392n/a # look for unfrozen modules (builtin and of unknown origin)
393n/a builtins = []
394n/a unknown = []
395n/a mods = sorted(dict.keys())
396n/a for mod in mods:
397n/a if dict[mod].__code__:
398n/a continue
399n/a if not dict[mod].__file__:
400n/a builtins.append(mod)
401n/a else:
402n/a unknown.append(mod)
403n/a
404n/a # search for unknown modules in extensions directories (not on Windows)
405n/a addfiles = []
406n/a frozen_extensions = [] # Windows list of modules.
407n/a if unknown or (not win and builtins):
408n/a if not win:
409n/a addfiles, addmods = \
410n/a checkextensions.checkextensions(unknown+builtins,
411n/a extensions)
412n/a for mod in addmods:
413n/a if mod in unknown:
414n/a unknown.remove(mod)
415n/a builtins.append(mod)
416n/a else:
417n/a # Do the windows thang...
418n/a import checkextensions_win32
419n/a # Get a list of CExtension instances, each describing a module
420n/a # (including its source files)
421n/a frozen_extensions = checkextensions_win32.checkextensions(
422n/a unknown, extensions, prefix)
423n/a for mod in frozen_extensions:
424n/a unknown.remove(mod.name)
425n/a
426n/a # report unknown modules
427n/a if unknown:
428n/a sys.stderr.write('Warning: unknown modules remain: %s\n' %
429n/a ' '.join(unknown))
430n/a
431n/a # windows gets different treatment
432n/a if win:
433n/a # Taking a shortcut here...
434n/a import winmakemakefile, checkextensions_win32
435n/a checkextensions_win32.write_extension_table(extensions_c,
436n/a frozen_extensions)
437n/a # Create a module definition for the bootstrap C code.
438n/a xtras = [frozenmain_c, os.path.basename(frozen_c),
439n/a frozendllmain_c, os.path.basename(extensions_c)] + files
440n/a maindefn = checkextensions_win32.CExtension( '__main__', xtras )
441n/a frozen_extensions.append( maindefn )
442n/a with open(makefile, 'w') as outfp:
443n/a winmakemakefile.makemakefile(outfp,
444n/a locals(),
445n/a frozen_extensions,
446n/a os.path.basename(target))
447n/a return
448n/a
449n/a # generate config.c and Makefile
450n/a builtins.sort()
451n/a with open(config_c_in) as infp, bkfile.open(config_c, 'w') as outfp:
452n/a makeconfig.makeconfig(infp, outfp, builtins)
453n/a
454n/a cflags = ['$(OPT)']
455n/a cppflags = defines + includes
456n/a libs = [os.path.join(binlib, '$(LDLIBRARY)')]
457n/a
458n/a somevars = {}
459n/a if os.path.exists(makefile_in):
460n/a makevars = parsesetup.getmakevars(makefile_in)
461n/a for key in makevars:
462n/a somevars[key] = makevars[key]
463n/a
464n/a somevars['CFLAGS'] = ' '.join(cflags) # override
465n/a somevars['CPPFLAGS'] = ' '.join(cppflags) # override
466n/a files = [base_config_c, base_frozen_c] + \
467n/a files + supp_sources + addfiles + libs + \
468n/a ['$(MODLIBS)', '$(LIBS)', '$(SYSLIBS)']
469n/a
470n/a with bkfile.open(makefile, 'w') as outfp:
471n/a makemakefile.makemakefile(outfp, somevars, files, base_target)
472n/a
473n/a # Done!
474n/a
475n/a if odir:
476n/a print('Now run "make" in', odir, end=' ')
477n/a print('to build the target:', base_target)
478n/a else:
479n/a print('Now run "make" to build the target:', base_target)
480n/a
481n/a
482n/a# Print usage message and exit
483n/a
484n/adef usage(msg):
485n/a sys.stdout = sys.stderr
486n/a print("Error:", msg)
487n/a print("Use ``%s -h'' for help" % sys.argv[0])
488n/a sys.exit(2)
489n/a
490n/a
491n/amain()