(root)/
Python-3.11.7/
Programs/
_freeze_module.c
       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  int _Py_Deepfreeze_Init(void)
      27  {
      28      return 0;
      29  }
      30  /* Empty finalizer for deepfrozen modules */
      31  void
      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  
      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  runtime_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  
      83  static const char *
      84  read_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  
     120  static PyObject *
     121  compile_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  
     142  static char *
     143  get_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  
     160  static void
     161  write_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  
     178  static int
     179  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      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  
     204  int
     205  main(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  
     245  error:
     246      PyErr_Print();
     247      Py_Finalize();
     248      return 1;
     249  }
     250