компилировать исполняемый файл python для работы вне основного каталога python - PullRequest
0 голосов
/ 31 мая 2018

Я работаю над упаковкой и распространением некоторых приложений Python в Windows, объединяя среду выполнения Python, пакеты Python для приложений и некоторые исполняемые файлы для запуска приложений Python.Подход заключается в том, чтобы просто изменить исходный код для python.exe для запуска приложений, но принять аргументы командной строки для таких вещей, как имена файлов данных.

Ниже приведен пример исходного кода C ++ для одного из исполняемых файлов:

// source for my_python_application1

#include "stdafx.h"
#include "Windows.h"
#include "Python.h"

wchar_t SWITCH[] = L"-m";
wchar_t APP[] = L"my_python_application1.main";

int wmain(int argc, wchar_t **argv) {

    int newargc;
    newargc = argc + 2;

    // can use this to modify the PythonPath for specific distributions
    // _putenv("PYTHONPATH=\"\"");

    wchar_t **newargv = new wchar_t*[newargc];
    newargv[0] = argv[0];
    newargv[1] = SWITCH;
    newargv[2] = APP;

    for (int i = 1; i < argc; i++) {
        newargv[i + 2] = argv[i];
    }
    return Py_Main(newargc, newargv);
    // return Py_Main(argc, argv);
}

Функционально это позволяет достичь всего, что мне нужно для этого, но я страдаю от определенной природы ОКР, которая заставляет меня хотеть организовать вещи определенным образом.Я хотел бы иметь структуру, подобную следующей

/application_suite
    /python_runtime
        python.exe
        python36.dll
        (and everything else in a python dir)
    /python_applications
    my_python_application1.exe
    my_python_application2.exe

Однако, поскольку mypythonapplication1/2.exe - это в основном модифицированные файлы python.exe, чтобы они работали должным образом (загрузите DLL-файл python, модули импорта,получить доступ ко всем функциям наземных разметок, необходимых для соединения модулей), они должны быть расположены в каталоге / python_runtime.

Мне интересно, есть ли способ компилировать эти исполняемые файлы, чтобы их можно было расположить вструктура каталогов, которую я представил, но знаю, что они представляют собой каталог python_runtime и всю его структуру, расположенную в относительном пути «./python_runtime» или где-либо еще, так что все это ведет себя хорошо независимо от того, где конечный пользователь установил распределение приложений.

1 Ответ

0 голосов
/ 31 мая 2018

Предупреждение перед ответом Я не программист на C / C ++.Возможно, здесь есть плохие практики C ++, поэтому, пожалуйста, используйте то, что вы найдете в этом ответе, с недоверием.

Требования для достижения этого поведения следующие:

  1. Мы должны получить каталог пользовательского исполняемого файла
  2. Мы должны установить для переменной среды PYTHONHOME значение %executable_dir%\runtime
  3. Мы должны установить для переменной среды PYTHONPATH значение %executable_dir%\apps, чтобыPython знает, где живут наши пакеты Python.Это также очищает любые общесистемные настройки, так что в дистрибутиве не используются другие настройки среды Python
  4. Я не знаю, если это необходимо, но я добавляю каталог runtime в начале пути
  5. Мы должны динамически загрузить функцию Py_Main из требуемой библиотеки DLL.Поскольку мы не ожидаем, что среда выполнения будет на пути перед выполнением, мы должны динамически находить dll из %executable_dir%\runtime\python36.dll.

Следующий исходный код работает, когда я компилировал в Visual Studio 2017, безФайлы заголовков Python и не указана dll в компоновщике

// source code for custom my_python_application1
// requires _CRT_SECURE_NO_WARNINGS flag to compile deprecated path operations

#include "stdafx.h"
#include <string>
#include <sstream>
#include <iostream>
#include "Windows.h"
#include "Shlwapi.h"
// #include "Python.h"  // don't need this as we are dynamically loading the library of choice

#pragma comment(lib, "Shlwapi.lib")
__pragma(warning(disable:4996))  // # _CRT_SECURE_NO_DEPRECIATE

wchar_t SWITCH[] = L"-m";
wchar_t APP[] = L"my_python_application1.main";

typedef int(__stdcall *py_main_function)(int, wchar_t**);

int wmain(int argc, wchar_t **argv) {

    int newargc;
    newargc = argc + 2;

    // determine the path of the executable so we know the absolute path
    // of the python runtime and application directories
    wchar_t executable_dir[MAX_PATH];
    if (GetModuleFileName(NULL, executable_dir, MAX_PATH) == 0)
        return -1;
    PathRemoveFileSpec(executable_dir);
    std::wstring executable_dir_string(executable_dir);

    // now set the relevant environment variables so that the environment works as it is supposed to
    std::wstring python_home(L"PYTHONHOME=" + executable_dir_string + L"\\runtime");
    _wputenv(python_home.c_str());

    std::wstring python_path(L"PYTHONPATH=" + executable_dir_string + L"\\apps");
    _wputenv(python_path.c_str());

    // put the python runtime at the front of the path
    std::wstringstream ss;
    ss << "PATH=" << executable_dir << "\\runtime;" << getenv("PATH");
    std::wstring path_string (ss.str());
    _wputenv(path_string.c_str());

    wchar_t **newargv = new wchar_t*[newargc];
    newargv[0] = argv[0];
    newargv[1] = SWITCH;
    newargv[2] = APP;

    for (int i = 1; i < argc; i++) {
        newargv[i + 2] = argv[i];
    }

    // dynamically load the python dll
    std::wstring python_dll(executable_dir_string + L"\\runtime\\python36.dll");
    HINSTANCE hGetProcIDDLL = LoadLibrary(python_dll.c_str());
    py_main_function Py_Main = (py_main_function)GetProcAddress(hGetProcIDDLL, "Py_Main");

    //now call Py_Main with our arguments
    return Py_Main(newargc, newargv);
    // return Py_Main(argc, argv);

}
...