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