Branch data Line data Source code
1 : : /* This is built as a stand-alone executable by the Makefile, and helps turn
2 : : modules into frozen modules (like Lib/importlib/_bootstrap.py
3 : : into Python/importlib.h).
4 : :
5 : : This is used directly by Tools/scripts/freeze_modules.py, and indirectly by "make regen-frozen".
6 : :
7 : : See Python/frozen.c for more info.
8 : :
9 : : Keep this file in sync with Programs/_freeze_module.py.
10 : : */
11 : :
12 : : #include <Python.h>
13 : : #include <marshal.h>
14 : : #include "pycore_fileutils.h" // _Py_stat_struct
15 : : #include <pycore_import.h>
16 : :
17 : : #include <stdio.h>
18 : : #include <stdlib.h> // malloc()
19 : : #include <sys/types.h>
20 : : #include <sys/stat.h>
21 : : #ifndef MS_WINDOWS
22 : : #include <unistd.h>
23 : : #endif
24 : :
25 : : /* Empty initializer for deepfrozen modules */
26 : 4 : int _Py_Deepfreeze_Init(void)
27 : : {
28 : 4 : return 0;
29 : : }
30 : : /* Empty finalizer for deepfrozen modules */
31 : : void
32 : 0 : _Py_Deepfreeze_Fini(void)
33 : : {
34 : 0 : }
35 : :
36 : : /* To avoid a circular dependency on frozen.o, we create our own structure
37 : : of frozen modules instead, left deliberately blank so as to avoid
38 : : unintentional import of a stale version of _frozen_importlib. */
39 : :
40 : : static const struct _frozen no_modules[] = {
41 : : {0, 0, 0} /* sentinel */
42 : : };
43 : : static const struct _module_alias aliases[] = {
44 : : {0, 0} /* sentinel */
45 : : };
46 : :
47 : : const struct _frozen *_PyImport_FrozenBootstrap;
48 : : const struct _frozen *_PyImport_FrozenStdlib;
49 : : const struct _frozen *_PyImport_FrozenTest;
50 : : const struct _frozen *PyImport_FrozenModules;
51 : : const struct _module_alias *_PyImport_FrozenAliases;
52 : :
53 : : static const char header[] =
54 : : "/* Auto-generated by Programs/_freeze_module.c */";
55 : :
56 : : static void
57 : 4 : runtime_init(void)
58 : : {
59 : : PyConfig config;
60 : 4 : PyConfig_InitIsolatedConfig(&config);
61 : :
62 : 4 : config.site_import = 0;
63 : :
64 : : PyStatus status;
65 : 4 : status = PyConfig_SetString(&config, &config.program_name,
66 : : L"./_freeze_module");
67 [ - + ]: 4 : if (PyStatus_Exception(status)) {
68 : 0 : PyConfig_Clear(&config);
69 : 0 : Py_ExitStatusException(status);
70 : : }
71 : :
72 : : /* Don't install importlib, since it could execute outdated bytecode. */
73 : 4 : config._install_importlib = 0;
74 : 4 : config._init_main = 0;
75 : :
76 : 4 : status = Py_InitializeFromConfig(&config);
77 : 4 : PyConfig_Clear(&config);
78 [ - + ]: 4 : if (PyStatus_Exception(status)) {
79 : 0 : Py_ExitStatusException(status);
80 : : }
81 : 4 : }
82 : :
83 : : static const char *
84 : 4 : read_text(const char *inpath)
85 : : {
86 : 4 : FILE *infile = fopen(inpath, "rb");
87 [ - + ]: 4 : if (infile == NULL) {
88 : 0 : fprintf(stderr, "cannot open '%s' for reading\n", inpath);
89 : 0 : return NULL;
90 : : }
91 : :
92 : : struct _Py_stat_struct stat;
93 [ - + ]: 4 : if (_Py_fstat_noraise(fileno(infile), &stat)) {
94 : 0 : fprintf(stderr, "cannot fstat '%s'\n", inpath);
95 : 0 : fclose(infile);
96 : 0 : return NULL;
97 : : }
98 : 4 : size_t text_size = (size_t)stat.st_size;
99 : :
100 : 4 : char *text = (char *) malloc(text_size + 1);
101 [ - + ]: 4 : if (text == NULL) {
102 : 0 : fprintf(stderr, "could not allocate %ld bytes\n", (long) text_size);
103 : 0 : fclose(infile);
104 : 0 : return NULL;
105 : : }
106 : 4 : size_t n = fread(text, 1, text_size, infile);
107 : 4 : fclose(infile);
108 : :
109 [ - + ]: 4 : if (n < text_size) {
110 : 0 : fprintf(stderr, "read too short: got %ld instead of %ld bytes\n",
111 : : (long) n, (long) text_size);
112 : 0 : free(text);
113 : 0 : return NULL;
114 : : }
115 : :
116 : 4 : text[text_size] = '\0';
117 : 4 : return (const char *)text;
118 : : }
119 : :
120 : : static PyObject *
121 : 4 : compile_and_marshal(const char *name, const char *text)
122 : : {
123 : 4 : char *filename = (char *) malloc(strlen(name) + 10);
124 : 4 : sprintf(filename, "<frozen %s>", name);
125 : 4 : PyObject *code = Py_CompileStringExFlags(text, filename,
126 : : Py_file_input, NULL, 0);
127 : 4 : free(filename);
128 [ - + ]: 4 : if (code == NULL) {
129 : 0 : return NULL;
130 : : }
131 : :
132 : 4 : PyObject *marshalled = PyMarshal_WriteObjectToString(code, Py_MARSHAL_VERSION);
133 [ + - ]: 4 : Py_CLEAR(code);
134 [ - + ]: 4 : if (marshalled == NULL) {
135 : 0 : return NULL;
136 : : }
137 : : assert(PyBytes_CheckExact(marshalled));
138 : :
139 : 4 : return marshalled;
140 : : }
141 : :
142 : : static char *
143 : 4 : get_varname(const char *name, const char *prefix)
144 : : {
145 : 4 : size_t n = strlen(prefix);
146 : 4 : char *varname = (char *) malloc(strlen(name) + n + 1);
147 : 4 : (void)strcpy(varname, prefix);
148 [ + + ]: 69 : for (size_t i = 0; name[i] != '\0'; i++) {
149 [ + + ]: 65 : if (name[i] == '.') {
150 : 2 : varname[n++] = '_';
151 : : }
152 : : else {
153 : 63 : varname[n++] = name[i];
154 : : }
155 : : }
156 : 4 : varname[n] = '\0';
157 : 4 : return varname;
158 : : }
159 : :
160 : : static void
161 : 4 : write_code(FILE *outfile, PyObject *marshalled, const char *varname)
162 : : {
163 : 4 : unsigned char *data = (unsigned char *) PyBytes_AS_STRING(marshalled);
164 : 4 : size_t data_size = PyBytes_GET_SIZE(marshalled);
165 : :
166 : 4 : fprintf(outfile, "const unsigned char %s[] = {\n", varname);
167 [ + + ]: 10555 : for (size_t n = 0; n < data_size; n += 16) {
168 : 10551 : size_t i, end = Py_MIN(n + 16, data_size);
169 : 10551 : fprintf(outfile, " ");
170 [ + + ]: 179336 : for (i = n; i < end; i++) {
171 : 168785 : fprintf(outfile, "%u,", (unsigned int) data[i]);
172 : : }
173 : 10551 : fprintf(outfile, "\n");
174 : : }
175 : 4 : fprintf(outfile, "};\n");
176 : 4 : }
177 : :
178 : : static int
179 : 4 : write_frozen(const char *outpath, const char *inpath, const char *name,
180 : : PyObject *marshalled)
181 : : {
182 : : /* Open the file in text mode. The hg checkout should be using the eol extension,
183 : : which in turn should cause the EOL style match the C library's text mode */
184 : 4 : FILE *outfile = fopen(outpath, "w");
185 [ - + ]: 4 : if (outfile == NULL) {
186 : 0 : fprintf(stderr, "cannot open '%s' for writing\n", outpath);
187 : 0 : return -1;
188 : : }
189 : :
190 : 4 : fprintf(outfile, "%s\n", header);
191 : 4 : char *arrayname = get_varname(name, "_Py_M__");
192 : 4 : write_code(outfile, marshalled, arrayname);
193 : 4 : free(arrayname);
194 : :
195 [ - + ]: 4 : if (ferror(outfile)) {
196 : 0 : fprintf(stderr, "error when writing to '%s'\n", outpath);
197 : 0 : return -1;
198 : : }
199 : 4 : fclose(outfile);
200 : 4 : return 0;
201 : : }
202 : :
203 : : int
204 : 4 : main(int argc, char *argv[])
205 : : {
206 : : const char *name, *inpath, *outpath;
207 : :
208 : 4 : _PyImport_FrozenBootstrap = no_modules;
209 : 4 : _PyImport_FrozenStdlib = no_modules;
210 : 4 : _PyImport_FrozenTest = no_modules;
211 : 4 : PyImport_FrozenModules = NULL;
212 : 4 : _PyImport_FrozenAliases = aliases;
213 : :
214 [ - + ]: 4 : if (argc != 4) {
215 : 0 : fprintf(stderr, "need to specify the name, input and output paths\n");
216 : 0 : return 2;
217 : : }
218 : 4 : name = argv[1];
219 : 4 : inpath = argv[2];
220 : 4 : outpath = argv[3];
221 : :
222 : 4 : runtime_init();
223 : :
224 : 4 : const char *text = read_text(inpath);
225 [ - + ]: 4 : if (text == NULL) {
226 : 0 : goto error;
227 : : }
228 : :
229 : 4 : PyObject *marshalled = compile_and_marshal(name, text);
230 : 4 : free((char *)text);
231 [ - + ]: 4 : if (marshalled == NULL) {
232 : 0 : goto error;
233 : : }
234 : :
235 : 4 : int res = write_frozen(outpath, inpath, name, marshalled);
236 : 4 : Py_DECREF(marshalled);
237 [ - + ]: 4 : if (res != 0) {
238 : 0 : goto error;
239 : : }
240 : :
241 : 4 : Py_Finalize();
242 : 4 : return 0;
243 : :
244 : 0 : error:
245 : 0 : PyErr_Print();
246 : 0 : Py_Finalize();
247 : 0 : return 1;
248 : : }
249 : :
|