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