/* * Extract all binaries (type 'b') and all data files (type 'x') to the filesystem * and checks for dependencies (type 'd'). If dependencies are found, extract them. * * 'Multipackage' feature includes dependencies. Dependencies are files in other * .exe files. Having files in other executables allows share binary files among * executables and thus reduce the final size of the executable. */ int pyi_launch_extract_binaries(ARCHIVE_STATUS *archive_status) { int retcode = 0; ptrdiff_t index = 0; /* * archive_pool[0] is reserved for the main process, the others for dependencies. */ ARCHIVE_STATUS *archive_pool[_MAX_ARCHIVE_POOL_LEN]; TOC * ptoc = archive_status->tocbuff; /* Clean memory for archive_pool list. */ memset(&archive_pool, 0, _MAX_ARCHIVE_POOL_LEN * sizeof(ARCHIVE_STATUS *)); /* Current process is the 1st item. */ archive_pool[0] = archive_status; VS("LOADER: Extracting binaries\n"); while (ptoc < archive_status->tocend) { if (ptoc->typcd == ARCHIVE_ITEM_BINARY || ptoc->typcd == ARCHIVE_ITEM_DATA || ptoc->typcd == ARCHIVE_ITEM_ZIPFILE) { if (pyi_arch_extract2fs(archive_status, ptoc)) { retcode = -1; break; /* No need to extract other items in case of error. */ } } else { /* 'Multipackage' feature - dependency is stored in different executables. */ if (ptoc->typcd == ARCHIVE_ITEM_DEPENDENCY) { if (_extract_dependency(archive_pool, ptoc->name) == -1) { retcode = -1; break; /* No need to extract other items in case of error. */ } } } ptoc = pyi_arch_increment_toc_ptr(archive_status, ptoc); } /* * Free memory allocated for archive_pool data. Do not free memory * of the main process - start with 2nd item. */ for (index = 1; archive_pool[index] != NULL; index++) { pyi_arch_status_free_memory(archive_pool[index]); } return retcode; }
/* * Check if binaries need to be extracted. If not, this is probably a onedir solution, * and a child process will not be required on windows. */ int pyi_launch_need_to_extract_binaries(ARCHIVE_STATUS *archive_status) { TOC * ptoc = archive_status->tocbuff; while (ptoc < archive_status->tocend) { if (ptoc->typcd == ARCHIVE_ITEM_BINARY || ptoc->typcd == ARCHIVE_ITEM_DATA || ptoc->typcd == ARCHIVE_ITEM_ZIPFILE) return true; if (ptoc->typcd == ARCHIVE_ITEM_DEPENDENCY) { return true; } ptoc = pyi_arch_increment_toc_ptr(archive_status, ptoc); } return false; }
/* Extract a file identifed by filename from the archive associated to status. */ static int extractDependencyFromArchive(ARCHIVE_STATUS *status, const char *filename) { TOC * ptoc = status->tocbuff; VS("LOADER: Extracting dependencies from archive\n"); while (ptoc < status->tocend) { if (strcmp(ptoc->name, filename) == 0) { if (pyi_arch_extract2fs(status, ptoc)) { return -1; } } ptoc = pyi_arch_increment_toc_ptr(status, ptoc); } return 0; }
/* * Install PYZ * Return non zero on failure */ int pyi_pylib_install_zlibs(ARCHIVE_STATUS *status) { TOC * ptoc; VS("LOADER: Installing PYZ archive with Python modules.\n"); /* Iterate through toc looking for zlibs (PYZ, type 'z') */ ptoc = status->tocbuff; while (ptoc < status->tocend) { if (ptoc->typcd == ARCHIVE_ITEM_PYZ) { VS("LOADER: PYZ archive: %s\n", ptoc->name); pyi_pylib_install_zlib(status, ptoc); } ptoc = pyi_arch_increment_toc_ptr(status, ptoc); } return 0; }
/* * Run scripts * Return non zero on failure */ int pyi_pylib_run_scripts(ARCHIVE_STATUS *status) { unsigned char *data; char buf[PATH_MAX]; int rc = 0; TOC * ptoc = status->tocbuff; PyObject *__main__ = PI_PyImport_AddModule("__main__"); PyObject *__file__; VS("LOADER: Running scripts\n"); /* Iterate through toc looking for scripts (type 's') */ while (ptoc < status->tocend) { if (ptoc->typcd == ARCHIVE_ITEM_PYSOURCE) { /* Get data out of the archive. */ data = pyi_arch_extract(status, ptoc); /* Set the __file__ attribute within the __main__ module, for full compatibility with normal execution. */ strcpy(buf, ptoc->name); strcat(buf, ".py"); VS("LOADER: Running %s\n", buf); __file__ = PI_PyString_FromStringAndSize(buf, strlen(buf)); PI_PyObject_SetAttrString(__main__, "__file__", __file__); Py_DECREF(__file__); /* Run it */ rc = PI_PyRun_SimpleString((char *) data); /* log errors and abort */ if (rc != 0) { VS("LOADER: RC: %d from %s\n", rc, ptoc->name); return rc; } free(data); } ptoc = pyi_arch_increment_toc_ptr(status, ptoc); } return 0; }
/* * Import modules embedded in the archive - return 0 on success */ int pyi_pylib_import_modules(ARCHIVE_STATUS *status) { PyObject *marshal; PyObject *marshaldict; PyObject *loadfunc; TOC *ptoc; PyObject *co; PyObject *mod; PyObject *meipass_obj; char * meipass_ansi; VS("LOADER: setting sys._MEIPASS\n"); // TODO extract function pyi_char_to_pyobject if(is_py2) { #ifdef _WIN32 meipass_ansi = pyi_win32_utf8_to_mbs_sfn(NULL, status->mainpath, 0); if(!meipass_ansi) { FATALERROR("Failed to encode _MEIPASS as ANSI.\n"); return -1; } meipass_obj = PI_PyString_FromString(meipass_ansi); free(meipass_ansi); #else meipass_obj = PI_PyString_FromString(status->mainpath); #endif } else { #ifdef _WIN32 meipass_obj = PI_PyUnicode_Decode(status->mainpath, strlen(status->mainpath), "utf-8", "strict"); #else meipass_obj = PI_PyUnicode_DecodeFSDefault(status->mainpath); #endif } if(!meipass_obj) { FATALERROR("Failed to get _MEIPASS as PyObject.\n"); return -1; } PI_PySys_SetObject("_MEIPASS", meipass_obj); VS("LOADER: importing modules from CArchive\n"); /* Get the Python function marshall.load * Here we collect some reference to PyObject that we don't dereference * Doesn't matter because the objects won't be going away anyway. */ marshal = PI_PyImport_ImportModule("marshal"); marshaldict = PI_PyModule_GetDict(marshal); loadfunc = PI_PyDict_GetItemString(marshaldict, "loads"); /* Iterate through toc looking for module entries (type 'm') * this is normally just bootstrap stuff (archive and iu) */ ptoc = status->tocbuff; while (ptoc < status->tocend) { if (ptoc->typcd == ARCHIVE_ITEM_PYMODULE || ptoc->typcd == ARCHIVE_ITEM_PYPACKAGE) { unsigned char *modbuf = pyi_arch_extract(status, ptoc); VS("LOADER: extracted %s\n", ptoc->name); /* .pyc/.pyo files have 8 bytes header. Skip it and load marshalled * data form the right point. */ if (is_py2) { co = PI_PyObject_CallFunction(loadfunc, "s#", modbuf+8, ntohl(ptoc->ulen)-8); } else { // It looks like from python 3.3 the header // size was changed to 12 bytes. co = PI_PyObject_CallFunction(loadfunc, "y#", modbuf+12, ntohl(ptoc->ulen)-12); }; if (co != NULL) { VS("LOADER: callfunction returned...\n"); mod = PI_PyImport_ExecCodeModule(ptoc->name, co); } else { // TODO callfunctions might return NULL - find yout why and foor what modules. VS("LOADER: callfunction returned NULL"); mod = NULL; } /* Check for errors in loading */ if (mod == NULL) { FATALERROR("mod is NULL - %s", ptoc->name); } if (PI_PyErr_Occurred()) { PI_PyErr_Print(); PI_PyErr_Clear(); } free(modbuf); } ptoc = pyi_arch_increment_toc_ptr(status, ptoc); } return 0; }
/* * A toc entry of type 'o' holds runtime options * toc->name is the arg * this is so you can freeze in command line args to Python */ static int pyi_pylib_set_runtime_opts(ARCHIVE_STATUS *status) { int unbuffered = 0; TOC *ptoc = status->tocbuff; wchar_t wchar_tmp[PATH_MAX+1]; /* * Startup flags - default values. 1 means enabled, 0 disabled. */ /* Suppress 'import site'. */ *PI_Py_NoSiteFlag = 1; /* Needed by getpath.c from Python. */ *PI_Py_FrozenFlag = 1; /* Suppress writing bytecode files (*.py[co]) */ *PI_Py_DontWriteBytecodeFlag = 1; /* Do not try to find any packages in user's site directory. */ *PI_Py_NoUserSiteDirectory = 1; /* This flag ensures PYTHONPATH and PYTHONHOME are ignored by Python. */ *PI_Py_IgnoreEnvironmentFlag = 1; /* Disalbe verbose imports by default. */ *PI_Py_VerboseFlag = 0; /* Override some runtime options by custom values from PKG archive. * User is allowed to changes these options. */ for (; ptoc < status->tocend; ptoc = pyi_arch_increment_toc_ptr(status, ptoc)) { if (ptoc->typcd == ARCHIVE_ITEM_RUNTIME_OPTION) { if(0 == strncmp(ptoc->name, "pyi-", 4)) { VS("LOADER: Bootloader option: %s\n", ptoc->name); continue; // Not handled here - use pyi_arch_get_option(status, ...) } VS("LOADER: Runtime option: %s\n", ptoc->name); switch (ptoc->name[0]) { case 'v': *PI_Py_VerboseFlag = 1; break; case 'u': unbuffered = 1; break; case 'W': if (is_py2) { PI_Py2Sys_AddWarnOption(&ptoc->name[2]); } else { // TODO: what encoding is ptoc->name? May not be important // as all known Wflags are ASCII. if ((size_t)-1 == mbstowcs(wchar_tmp, &ptoc->name[2], PATH_MAX)) { FATALERROR("Failed to convert Wflag %s using mbstowcs " "(invalid multibyte string)", &ptoc->name[2]); return -1; } PI_PySys_AddWarnOption(wchar_tmp); }; break; case 'O': *PI_Py_OptimizeFlag = 1; break; } } } if (unbuffered) { #ifdef _WIN32 _setmode(fileno(stdin), _O_BINARY); _setmode(fileno(stdout), _O_BINARY); #endif fflush(stdout); fflush(stderr); setbuf(stdin, (char *)NULL); setbuf(stdout, (char *)NULL); setbuf(stderr, (char *)NULL); } return 0; }
/* * Run scripts * Return non zero on failure */ int pyi_launch_run_scripts(ARCHIVE_STATUS *status) { unsigned char *data; char buf[PATH_MAX]; size_t namelen; TOC * ptoc = status->tocbuff; PyObject *__main__; PyObject *__file__; PyObject *main_dict; PyObject *code, *retval; __main__ = PI_PyImport_AddModule("__main__"); if (!__main__) { FATALERROR("Could not get __main__ module."); return -1; } main_dict = PI_PyModule_GetDict(__main__); if (!main_dict) { FATALERROR("Could not get __main__ module's dict."); return -1; } /* Iterate through toc looking for scripts (type 's') */ while (ptoc < status->tocend) { if (ptoc->typcd == ARCHIVE_ITEM_PYSOURCE) { /* Get data out of the archive. */ data = pyi_arch_extract(status, ptoc); /* Set the __file__ attribute within the __main__ module, * for full compatibility with normal execution. */ namelen = strnlen(ptoc->name, PATH_MAX); if (namelen >= PATH_MAX-strlen(".py")-1) { FATALERROR("Name exceeds PATH_MAX\n"); return -1; } strcpy(buf, ptoc->name); strcat(buf, ".py"); VS("LOADER: Running %s\n", buf); if (is_py2) { __file__ = PI_PyString_FromString(buf); } else { __file__ = PI_PyUnicode_FromString(buf); }; PI_PyObject_SetAttrString(__main__, "__file__", __file__); Py_DECREF(__file__); /* Unmarshall code object */ code = PI_PyMarshal_ReadObjectFromString((const char *) data, ntohl(ptoc->ulen)); if (!code) { FATALERROR("Failed to unmarshal code object for %s\n", ptoc->name); PI_PyErr_Print(); return -1; } /* Run it */ retval = PI_PyEval_EvalCode(code, main_dict, main_dict); /* If retval is NULL, an error occured. Otherwise, it is a Python object. * (Since we evaluate module-level code, which is not allowed to return an * object, the Python object returned is always None.) */ if (!retval) { PI_PyErr_Print(); /* If the error was SystemExit, PyErr_Print calls exit() without * returning. So don't print "Failed to execute" on SystemExit. */ FATALERROR("Failed to execute script %s\n", ptoc->name); return -1; } free(data); } ptoc = pyi_arch_increment_toc_ptr(status, ptoc); } return 0; }