mirror of
https://github.com/python/cpython.git
synced 2025-07-23 19:25:40 +00:00

Currently custom modules (the array set on PyImport_FrozenModules) replace all the frozen stdlib modules. That can be problematic and is unlikely to be what the user wants. This change treats the custom frozen modules as additions instead. They take precedence over all other frozen modules except for those needed to bootstrap the import system. If the "code" field of an entry in the custom array is NULL then that frozen module is treated as disabled, which allows a custom entry to disable a frozen stdlib module. This change allows us to get rid of is_essential_frozen_module() and simplifies the logic for which frozen modules should be ignored. https://bugs.python.org/issue45395
234 lines
6.1 KiB
C
234 lines
6.1 KiB
C
/* This is built as a stand-alone executable by the Makefile, and helps turn
|
|
modules into frozen modules (like Lib/importlib/_bootstrap.py
|
|
into Python/importlib.h).
|
|
|
|
This is used directly by Tools/scripts/freeze_modules.py, and indirectly by "make regen-frozen".
|
|
|
|
See Python/frozen.c for more info.
|
|
*/
|
|
|
|
#include <Python.h>
|
|
#include <marshal.h>
|
|
#include <pycore_import.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h> // malloc()
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#ifndef MS_WINDOWS
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
/* To avoid a circular dependency on frozen.o, we create our own structure
|
|
of frozen modules instead, left deliberately blank so as to avoid
|
|
unintentional import of a stale version of _frozen_importlib. */
|
|
|
|
static const struct _frozen no_modules[] = {
|
|
{0, 0, 0} /* sentinel */
|
|
};
|
|
static const struct _module_alias aliases[] = {
|
|
{0, 0} /* sentinel */
|
|
};
|
|
|
|
const struct _frozen *_PyImport_FrozenBootstrap;
|
|
const struct _frozen *_PyImport_FrozenStdlib;
|
|
const struct _frozen *_PyImport_FrozenTest;
|
|
const struct _frozen *PyImport_FrozenModules;
|
|
const struct _module_alias *_PyImport_FrozenAliases;
|
|
|
|
static const char header[] =
|
|
"/* Auto-generated by Programs/_freeze_module.c */";
|
|
|
|
static void
|
|
runtime_init(void)
|
|
{
|
|
PyConfig config;
|
|
PyConfig_InitIsolatedConfig(&config);
|
|
|
|
config.site_import = 0;
|
|
|
|
PyStatus status;
|
|
status = PyConfig_SetString(&config, &config.program_name,
|
|
L"./_freeze_module");
|
|
if (PyStatus_Exception(status)) {
|
|
PyConfig_Clear(&config);
|
|
Py_ExitStatusException(status);
|
|
}
|
|
|
|
/* Don't install importlib, since it could execute outdated bytecode. */
|
|
config._install_importlib = 0;
|
|
config._init_main = 0;
|
|
|
|
status = Py_InitializeFromConfig(&config);
|
|
PyConfig_Clear(&config);
|
|
if (PyStatus_Exception(status)) {
|
|
Py_ExitStatusException(status);
|
|
}
|
|
}
|
|
|
|
static const char *
|
|
read_text(const char *inpath)
|
|
{
|
|
FILE *infile = fopen(inpath, "rb");
|
|
if (infile == NULL) {
|
|
fprintf(stderr, "cannot open '%s' for reading\n", inpath);
|
|
return NULL;
|
|
}
|
|
|
|
struct _Py_stat_struct stat;
|
|
if (_Py_fstat_noraise(fileno(infile), &stat)) {
|
|
fprintf(stderr, "cannot fstat '%s'\n", inpath);
|
|
fclose(infile);
|
|
return NULL;
|
|
}
|
|
size_t text_size = (size_t)stat.st_size;
|
|
|
|
char *text = (char *) malloc(text_size + 1);
|
|
if (text == NULL) {
|
|
fprintf(stderr, "could not allocate %ld bytes\n", (long) text_size);
|
|
fclose(infile);
|
|
return NULL;
|
|
}
|
|
size_t n = fread(text, 1, text_size, infile);
|
|
fclose(infile);
|
|
|
|
if (n < text_size) {
|
|
fprintf(stderr, "read too short: got %ld instead of %ld bytes\n",
|
|
(long) n, (long) text_size);
|
|
free(text);
|
|
return NULL;
|
|
}
|
|
|
|
text[text_size] = '\0';
|
|
return (const char *)text;
|
|
}
|
|
|
|
static PyObject *
|
|
compile_and_marshal(const char *name, const char *text)
|
|
{
|
|
char *filename = (char *) malloc(strlen(name) + 10);
|
|
sprintf(filename, "<frozen %s>", name);
|
|
PyObject *code = Py_CompileStringExFlags(text, filename,
|
|
Py_file_input, NULL, 0);
|
|
free(filename);
|
|
if (code == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
PyObject *marshalled = PyMarshal_WriteObjectToString(code, Py_MARSHAL_VERSION);
|
|
Py_CLEAR(code);
|
|
if (marshalled == NULL) {
|
|
return NULL;
|
|
}
|
|
assert(PyBytes_CheckExact(marshalled));
|
|
|
|
return marshalled;
|
|
}
|
|
|
|
static char *
|
|
get_varname(const char *name, const char *prefix)
|
|
{
|
|
size_t n = strlen(prefix);
|
|
char *varname = (char *) malloc(strlen(name) + n + 1);
|
|
(void)strcpy(varname, prefix);
|
|
for (size_t i = 0; name[i] != '\0'; i++) {
|
|
if (name[i] == '.') {
|
|
varname[n++] = '_';
|
|
}
|
|
else {
|
|
varname[n++] = name[i];
|
|
}
|
|
}
|
|
varname[n] = '\0';
|
|
return varname;
|
|
}
|
|
|
|
static void
|
|
write_code(FILE *outfile, PyObject *marshalled, const char *varname)
|
|
{
|
|
unsigned char *data = (unsigned char *) PyBytes_AS_STRING(marshalled);
|
|
size_t data_size = PyBytes_GET_SIZE(marshalled);
|
|
|
|
fprintf(outfile, "const unsigned char %s[] = {\n", varname);
|
|
for (size_t n = 0; n < data_size; n += 16) {
|
|
size_t i, end = Py_MIN(n + 16, data_size);
|
|
fprintf(outfile, " ");
|
|
for (i = n; i < end; i++) {
|
|
fprintf(outfile, "%u,", (unsigned int) data[i]);
|
|
}
|
|
fprintf(outfile, "\n");
|
|
}
|
|
fprintf(outfile, "};\n");
|
|
}
|
|
|
|
static int
|
|
write_frozen(const char *outpath, const char *inpath, const char *name,
|
|
PyObject *marshalled)
|
|
{
|
|
/* Open the file in text mode. The hg checkout should be using the eol extension,
|
|
which in turn should cause the EOL style match the C library's text mode */
|
|
FILE *outfile = fopen(outpath, "w");
|
|
if (outfile == NULL) {
|
|
fprintf(stderr, "cannot open '%s' for writing\n", outpath);
|
|
return -1;
|
|
}
|
|
|
|
fprintf(outfile, "%s\n", header);
|
|
char *arrayname = get_varname(name, "_Py_M__");
|
|
write_code(outfile, marshalled, arrayname);
|
|
free(arrayname);
|
|
|
|
if (ferror(outfile)) {
|
|
fprintf(stderr, "error when writing to '%s'\n", outpath);
|
|
return -1;
|
|
}
|
|
fclose(outfile);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
const char *name, *inpath, *outpath;
|
|
|
|
_PyImport_FrozenBootstrap = no_modules;
|
|
_PyImport_FrozenStdlib = no_modules;
|
|
_PyImport_FrozenTest = no_modules;
|
|
PyImport_FrozenModules = NULL;
|
|
_PyImport_FrozenAliases = aliases;
|
|
|
|
if (argc != 4) {
|
|
fprintf(stderr, "need to specify the name, input and output paths\n");
|
|
return 2;
|
|
}
|
|
name = argv[1];
|
|
inpath = argv[2];
|
|
outpath = argv[3];
|
|
|
|
runtime_init();
|
|
|
|
const char *text = read_text(inpath);
|
|
if (text == NULL) {
|
|
goto error;
|
|
}
|
|
|
|
PyObject *marshalled = compile_and_marshal(name, text);
|
|
free((char *)text);
|
|
if (marshalled == NULL) {
|
|
goto error;
|
|
}
|
|
|
|
int res = write_frozen(outpath, inpath, name, marshalled);
|
|
Py_DECREF(marshalled);
|
|
if (res != 0) {
|
|
goto error;
|
|
}
|
|
|
|
Py_Finalize();
|
|
return 0;
|
|
|
|
error:
|
|
PyErr_Print();
|
|
Py_Finalize();
|
|
return 1;
|
|
}
|