LCOV - code coverage report
Current view: | top level - Programs - _freeze_module.c (source / functions) |  |  | Hit | Total | Coverage
Test: | CPython 3.12 LCOV report [commit 5e6661bce9] |  | Lines: | 88 | 120 | 73.3 %
Date: | 2023-03-20 08:15:36 |  | Functions: | 8 | 9 | 88.9 %
|  |  | Branches: | 23 | 38 | 60.5 %

               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                 :            :

Generated by: LCOV version 1.14