_freeze_module.c 6.6 KB

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