/* Convert a UTF-8 string to an ANSI string, also attempting to get the MS-DOS
   ShortFileName if the string is a filename. ShortFileName allows Python 2.7 to
   accept filenames which cannot encode in the current ANSI codepage.

   Preserves the filename's original basename, since the bootloader code depends on
   the unmodified basename. Assumes that the basename can be encoded using the current
   ANSI codepage.

   This is a workaround for <https://github.com/pyinstaller/pyinstaller/issues/298>.

   Copies the converted string to `dest`, which must be a buffer
   of at least PATH_MAX characters. Returns 'dest' if successful.

   Returns NULL and logs error reason if encoding fails.
 */
char * pyi_win32_utf8_to_mbs_sfn_keep_basename(char * dest, const char * src) {
    char * mbs_buffer;
    char * mbs_sfn_buffer;
    char basename[PATH_MAX];
    char dirname[PATH_MAX];

    /* Convert path to mbs*/
    mbs_buffer = pyi_win32_utf8_to_mbs(NULL, src, 0);
    if(NULL == mbs_buffer) {
        return NULL;
    }

    /* Convert path again to mbs, this time with SFN */
    mbs_sfn_buffer = pyi_win32_utf8_to_mbs_sfn(NULL, src, 0);
    if(NULL == mbs_sfn_buffer) {
        free(mbs_buffer);
        return NULL;
    }

    pyi_path_basename(basename, mbs_buffer);
    pyi_path_dirname(dirname, mbs_sfn_buffer);
    pyi_path_join(dest, dirname, basename);
    free(mbs_buffer);
    free(mbs_sfn_buffer);
    return dest;
}
示例#2
0
/*
 * Install a zlib from a toc entry.
 *
 * Must be called after Py_Initialize (i.e. after pyi_pylib_start_python)
 *
 * The installation is done by adding an entry like
 *    absolute_path/dist/hello_world/hello_world?123456
 * to sys.path. The end number is the offset where the
 * Python bootstrap code should read the zip data.
 * Return non zero on failure.
 * NB: This entry is removed from sys.path by the bootstrap scripts.
 */
int pyi_pylib_install_zlib(ARCHIVE_STATUS *status, TOC *ptoc)
{
	int rc = 0;
	int zlibpos = status->pkgstart + ntohl(ptoc->pos);
	PyObject * sys_path, *zlib_entry, *archivename_obj;
	char *archivename;

	/* Note that sys.path contains PyString on py2, and PyUnicode on py3. Ensure
	 * that filenames are encoded or decoded correctly.
	 */
	if(is_py2) {
#ifdef _WIN32
		/* Must be MBCS encoded. Use SFN if possible.
		 *
		 * We could instead pass the UTF-8 encoded form and modify FrozenImporter to
		 * decode it on Windows, but this breaks the convention that `sys.path`
		 * entries on Windows are MBCS encoded, and may interfere with any code
		 * that inspects `sys.path`
		 *
		 * We could also pass the zlib path through a channel other than `sys.path`
		 * to sidestep that requirement, but there's not much benefit as this only
		 * improves non-codepage/non-SFN compatibility for the zlib and not any other
		 * importable modules.
		 */

		archivename = pyi_win32_utf8_to_mbs_sfn(NULL, status->archivename, 0);
		if(NULL == archivename) {
		    FATALERROR("Failed to convert %s to ShortFileName\n", status->archivename);
			return -1;
		}
#else
		/* Use system-provided path. No encoding required. */
		archivename = status->archivename;
#endif
		zlib_entry = PI_PyString_FromFormat("%s?%d", archivename, zlibpos);
		if(archivename != status->archivename) free(archivename);

	} else {
#ifdef _WIN32
		/* Decode UTF-8 to PyUnicode */
		archivename_obj = PI_PyUnicode_Decode(status->archivename,
											  strlen(status->archivename),
											  "utf-8",
											  "strict");
#else
		/* Decode locale-encoded filename to PyUnicode object using Python's
		 * preferred decoding method for filenames.
		 */
		archivename_obj = PI_PyUnicode_DecodeFSDefault(status->archivename);
#endif
		zlib_entry = PI_PyUnicode_FromFormat("%U?%d", archivename_obj, zlibpos);
		PI_Py_DecRef(archivename_obj);
	}

	sys_path = PI_PySys_GetObject("path");
	if(NULL == sys_path) {
		FATALERROR("Installing PYZ: Could not get sys.path\n");
		PI_Py_DecRef(zlib_entry);
		return -1;
	}

	rc = PI_PyList_Append(sys_path, zlib_entry);
	if(rc) {
		FATALERROR("Failed to append to sys.path\n");
	}

	return rc;
}
示例#3
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;
}
示例#4
0
/*
 * Start python - return 0 on success
 */
int pyi_pylib_start_python(ARCHIVE_STATUS *status)
{
    /* Set sys.path, sys.prefix, and sys.executable so dynamic libs will load.
     *
     * The Python APIs we use here (Py_SetProgramName, Py_SetPythonHome)
     * specify their argument should be a "string in static storage".
     * That is, the APIs use the string pointer as given and will neither copy
     * its contents nor free its memory.
     *
     * NOTE: Statics are zero-initialized. */
	static char pypath[2*PATH_MAX + 14];
	static char pypath_sfn[2*PATH_MAX +14];
	static char pyhome[PATH_MAX+1];
	static char progname[PATH_MAX+1];

    /* Wide string forms of the above, for Python 3. */
	static wchar_t pypath_w[PATH_MAX+1];
	static wchar_t pyhome_w[PATH_MAX+1];
	static wchar_t progname_w[PATH_MAX+1];

    if (is_py2) {
#ifdef _WIN32
		/* Use ShortFileName - affects sys.executable */
		if(!pyi_win32_utf8_to_mbs_sfn(progname, status->archivename, PATH_MAX)) {
			FATALERROR("Failed to convert progname to wchar_t\n");
			return -1;
		}
#else
		/* Use system-provided filename. No encoding. */
		strncpy(progname, status->archivename, PATH_MAX);
#endif
      	PI_Py2_SetProgramName(progname);
    } else {
		/* Decode using current locale */
		if(!pyi_locale_char2wchar(progname_w, status->archivename, PATH_MAX)) {
			FATALERROR("Failed to convert progname to wchar_t\n");
			return -1;
		}
        // In Python 3 Py_SetProgramName() should be called before Py_SetPath().
        PI_Py_SetProgramName(progname_w);
    };

    /* Set sys.path */
    VS("LOADER: Manipulating environment (sys.path, sys.prefix)\n");
    if(is_py2) {
    	/* sys.path = [mainpath] */
    	strncpy(pypath, status->mainpath, strlen(status->mainpath));
    } else {
    	/* sys.path = [base_library, mainpath] */
        strncpy(pypath, status->mainpath, strlen(status->mainpath));
		strncat(pypath, PYI_SEPSTR, strlen(PYI_SEPSTR));
		strncat(pypath, "base_library.zip", strlen("base_library.zip"));
		strncat(pypath, PYI_PATHSEPSTR, strlen(PYI_PATHSEPSTR));
		strncat(pypath, status->mainpath, strlen(status->mainpath));
    };
    /*
     * On Python 3, we must set sys.path to have base_library.zip before
     * calling Py_Initialize as it needs `encodings` and other modules.
     */
    if (!is_py2) {
        /* Decode using current locale */
		if(!pyi_locale_char2wchar(pypath_w, pypath, PATH_MAX)) {
			FATALERROR("Failed to convert pypath to wchar_t\n");
			return -1;
		}
	    VS("LOADER: Pre-init sys.path is %s\n", pypath);
        PI_Py_SetPath(pypath_w);
    };

    /* Set sys.prefix and sys.exec_prefix using Py_SetPythonHome */
    if (is_py2) {
#ifdef _WIN32
    	if(!pyi_win32_utf8_to_mbs_sfn(pyhome, status->mainpath, PATH_MAX)) {
		    FATALERROR("Failed to convert pyhome to ANSI (invalid multibyte string)\n");
		    return -1;
		}
#else
	    strcpy(pyhome, status->mainpath);
#endif
        VS("LOADER: sys.prefix is %s\n", pyhome);
        PI_Py2_SetPythonHome(pyhome);
    } else {
        /* Decode using current locale */
		if(!pyi_locale_char2wchar(pyhome_w, status->mainpath, PATH_MAX)) {
			FATALERROR("Failed to convert pyhome to wchar_t\n");
			return -1;
		}
        VS("LOADER: sys.prefix is %s\n", status->mainpath);
        PI_Py_SetPythonHome(pyhome_w);
    };

    /* Start python. */
    VS("LOADER: Setting runtime options\n");
    pyi_pylib_set_runtime_opts(status);

	/*
	 * Py_Initialize() may rudely call abort(), and on Windows this triggers the error
	 * reporting service, which results in a dialog box that says "Close program", "Check
	 * for a solution", and also "Debug" if Visual Studio is installed. The dialog box
	 * makes it frustrating to run the test suite.
	 *
	 * For debug builds of the bootloader, disable the error reporting before calling
	 * Py_Initialize and enable it afterward.
	 */

#if defined(_WIN32) && defined(LAUNCH_DEBUG)
	SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
#endif

	VS("LOADER: Initializing python\n");
	PI_Py_Initialize();

#if defined(_WIN32) && defined(LAUNCH_DEBUG)
	SetErrorMode(0);
#endif

	/*
	 * Set sys.path list.
	 * Python's default sys.path is no good - it includes the working directory
	 * and the folder containing the executable. Replace sys.path with only
	 * the paths we want.
	 */
	VS("LOADER: Overriding Python's sys.path\n");
	VS("LOADER: Post-init sys.path is %s\n", pypath);
	if (is_py2) {
#ifdef _WIN32
	    if(!pyi_win32_utf8_to_mbs_sfn(pypath_sfn, pypath, PATH_MAX)) {
			FATALERROR("Failed to convert pypath to ANSI (invalid multibyte string)\n");
		}
	    PI_Py2Sys_SetPath(pypath_sfn);
#else
	    PI_Py2Sys_SetPath(pypath);
#endif
	} else {
	   PI_PySys_SetPath(pypath_w);
	};

    /* Setting sys.argv should be after Py_Initialize() call. */
    if(pyi_pylib_set_sys_argv(status)) {
        return -1;
    }

	/* Check for a python error */
	if (PI_PyErr_Occurred())
	{
		FATALERROR("Error detected starting Python VM.");
		return -1;
	}

	return 0;
}