Как правильно отправить программу, созданную с использованием MSYS2 и MingW, конечным пользователям?

Я создаю приложение C для Windows, используя Msys2 и MingW.

Это приложение будет поставляться на настольные компьютеры Windows конечных пользователей, не разбирающихся в технологиях. У меня два вопроса по этому поводу:

  1. Каков наилучший способ отправки программы Windows, созданной с использованием Msys64 и MingW, на другой компьютер с Windows, на котором они не установлены? Некоторые источники в Интернете и здесь, на SO, говорят, что у нас нет другого выбора, кроме как распространять файл exe в том же каталоге вместе с MingW dll, от которых он зависит. Или, чтобы статически связать exe с MingW dlls. Но я хотел бы убедиться, что это действительно стандартные способы сделать это. Кажется странным, что у MingW не было лучшего способа.

  2. Предположим, что указанный exe также в какой-то момент использует LoadLibrary API для динамической загрузки dll (например, система плагинов). Этот dll также был построен с использованием MingW (и в этом случае, конечно, будет поставляться конечным пользователям вместе с exe). Есть ли что-то особенное, что мне нужно сделать, чтобы убедиться, что dll будет успешно загружен на пользовательскую машину без установленного MingW?

  3. В дополнение к 2: нужна ли упомянутой DLL библиотека импорта (.lib), присутствующая во время компиляции на машине разработки и/или во время запуска на конечной машине?

РЕДАКТИРОВАТЬ: Позвольте мне уточнить, я не использую MSYS2 в качестве оболочки командной строки. Я работаю напрямую с Mingw gcc через Windows cmd и просто получил Mingw из каталога загрузки MSYS2.


person Aviv Cohn    schedule 28.03.2020    source источник


Ответы (2)


Я предполагаю, что этот вопрос касается MSYS2 и mingw-w64, поскольку не существует такого понятия, как «msys64».

MSYS2 предлагает три разные целевые системы:

  • Windows 32-разрядная
  • 64-разрядная версия Windows
  • MSYS2

каждый из которых имеет совершенно отдельные деревья исходного кода для своих инструментов разработки и пакетов при установке MSYS2.

Это не следует путать с различными системами сборки — системой сборки может быть Win32 или Win64. Например, если вы выбрали Win64 в качестве системы сборки и решили установить все три целевые системы, то у вас будут деревья msys64/mingw32, msys64/mingw64 и msys64/usr.

Установщик MSYS2 создает сценарии запуска для каждой установленной цели.


Если вы ориентируетесь на Win32 или Win64, то скомпилированные двоичные файлы (exe или dll) можно распространять как отдельные продукты. Вы можете сделать статическую сборку с помощью gcc или clang, создав один автономный исполняемый файл, или вы можете сделать сборку, которая зависит от DLL, которые вы распространяете с исполняемым файлом.

Для бинарных дистрибутивов стандартно включать все файлы, которые им требуются и которые не предоставляются целевой операционной системой, это не является особенностью mingw-w64.

Путь поиска LoadLibrary описан в документации MSDN .

Если вы нацелены на MSYS2, то полученный двоичный файл СЛЕДУЕТ запускать в оболочке MSYS2. Эта цель предлагает некоторые функции POSIX, которые напрямую не поддерживаются mingw-w64. Его можно считать форком Cygwin. Можно было бы распространять двоичный файл, предназначенный для MSYS2, вместе с набором библиотек DLL, скопированных из установки MSYS2, которые вы идентифицируете с помощью средства отслеживания зависимостей.

person M.M    schedule 28.03.2020

Я не вижу недостатков в этом решении.
Я не являюсь большим пользователем Windows, но должен признать, что решение неявного поиска динамических библиотек там, где стоит исполняемый файл, очень помогает.
Вы размещаете весь контент вашего приложения (исполняемых файлов и динамических библиотек/плагинов) в любом месте, и как только вам удастся запустить исполняемый файл, все остальное будет найдено.

Конечно, если вы планируете поставлять много разных приложений с общим подмножеством динамических библиотек, было бы лучше поместить все эти библиотеки в общее место и соответствующим образом настроить переменную среды PATH.
Но это не стоит усилий. для одного приложения.

В приложении на основе mingw-w64 (не совсем Msys64 и MingW, но очень похожем) (с плагинами), которое я поставил несколько месяцев назад, я только что предоставил libgcc_s_seh-1.dll, libstdc++-6.dll и libwinpthread-1.dll. как дополнение к моим собственным двоичным файлам, и это работало без каких-либо проблем.
Использование objdump.exe -p my_program.exe (затем рекурсивно на отображаемом результате) помогает найти необходимые динамические библиотеки (например, ldd в Linux или otool -L в Macosx).

Это то, что мне нравится в решении, похожем на mingw: оно создает собственное приложение Windows, которое не зависит от многих других необычных компонентов (которые пользователю пришлось бы сначала извлекать).


Нет необходимости иметь дело с некоторыми .lib файлами; достаточно создать файл .dll и связать его (см. пример ниже).
Он работает точно так же, как мы в UNIX (с файлами .so).
Я действительно не знаю, почему Visual-C++ полагается на файл so сложная комбинация файлов .lib и .dll...


Я только что перепроверил этот простой пример.

файл prog.cpp

#include <windows.h>
#include <iostream>

__declspec(dllimport)
int
my_library_function(int arg);

int
main()
{
  std::cout << "~~~~ entering " << __func__ << " ~~~~\n";
  int result=my_library_function(123);
  std::cout << "result=" << result << '\n';
  std::cout << "~~~~ still in " << __func__ << " ~~~~\n";
  HINSTANCE lib=LoadLibrary("my_plugin.dll");
  if(lib)
  {
    FARPROC symbol=GetProcAddress(lib, "my_plugin_function");
    if(symbol)
    {
      int (*fnct)(int)=NULL;
      memcpy(&fnct, &symbol, sizeof(fnct));
      int result=fnct(123);
      std::cout << "result=" << result << '\n';
    }
    FreeLibrary(lib);
  }
  std::cout << "~~~~ leaving " << __func__ << " ~~~~\n";
  return 0;
}

файл my_library.cpp

#include <iostream>

__declspec(dllexport)
int
my_library_function(int arg)
{
  std::cout << "~~~~ entering " << __func__ << " ~~~~\n";
  std::cout << "arg=" << arg << '\n';
  std::cout << "~~~~ leaving " << __func__ << " ~~~~\n";
  return 2*arg;
}

файл my_plugin.cpp

#include <iostream>

extern "C" __declspec(dllexport)
int
my_plugin_function(int arg)
{
  std::cout << "~~~~ entering " << __func__ << " ~~~~\n";
  std::cout << "arg=" << arg << '\n';
  std::cout << "~~~~ leaving " << __func__ << " ~~~~\n";
  return 2*arg;
}

процесс сборки

==== compiling [opt=0] my_plugin.cpp ====
g++ -o my_plugin.o my_plugin.cpp -c   -g -O0  -MMD -pedantic -Wall -Wextra -Wconversion -Wno-unused -Wno-unused-parameter -Werror -Wfatal-errors -UNDEBUG  -std=c++17 -Wno-missing-braces -Wno-sign-conversion

==== linking [opt=0] my_plugin.dll ====
g++ -shared -o my_plugin.dll my_plugin.o   -g -O0

==== compiling [opt=0] my_library.cpp ====
g++ -o my_library.o my_library.cpp -c   -g -O0  -MMD -pedantic -Wall -Wextra -Wconversion -Wno-unused -Wno-unused-parameter -Werror -Wfatal-errors -UNDEBUG  -std=c++17 -Wno-missing-braces -Wno-sign-conversion

==== linking [opt=0] my_library.dll ====
g++ -shared -o my_library.dll my_library.o   -g -O0

==== compiling [opt=0] prog.cpp ====
g++ -o prog.o prog.cpp -c   -g -O0  -MMD -pedantic -Wall -Wextra -Wconversion -Wno-unused -Wno-unused-parameter -Werror -Wfatal-errors -UNDEBUG  -std=c++17 -Wno-missing-braces -Wno-sign-conversion

==== linking [opt=0] prog.exe ====
g++ -o prog.exe prog.o   -g -O0 -lmy_library

исполнение

C:\Work\PluginTest>prog.exe
~~~~ entering main ~~~~
~~~~ entering my_library_function ~~~~
arg=123
~~~~ leaving my_library_function ~~~~
result=246
~~~~ still in main ~~~~
~~~~ entering my_plugin_function ~~~~
arg=123
~~~~ leaving my_plugin_function ~~~~
result=246
~~~~ leaving main ~~~~

C:\Work\PluginTest>

Это все еще работает, когда каталог mingw64 (содержащий набор инструментов) переименовывается, как только файлы libgcc_s_seh-1.dll, libstdc++-6.dll и libwinpthread-1.dll помещаются в тот же каталог, что и prog.exe.
Это даже работает, когда prog.exe запускается щелчком (а не из командная строка); чтобы увидеть это, мне пришлось добавить бесконечный цикл в конце программы, чтобы окно оставалось открытым достаточно долго, чтобы увидеть отображаемые сообщения.

person prog-fh    schedule 28.03.2020
comment
Эй, спасибо за ваш ответ. Таким образом, по вашему опыту, DLL, скомпилированная с использованием Mingw и динамически загружаемая (также известная как LoadLibrary), должна иметь возможность работать на другом компьютере, отличном от Mingw, если в загружаемом файле exe рядом с ним находятся библиотеки DLL MingW в каталоге? - person Aviv Cohn; 28.03.2020
comment
@AvivCohn, насколько я помню, да (по крайней мере, с mingw-w64). И я просто распаковываю mingw-w64 локально (система не знает об установке, я даже могу переименовать или стереть этот каталог установки), поэтому конфигурация для тестирования такая же, как и для клиента без установленных инструментов. Как только я перезагружусь в Windows, я попробую еще раз (давно это было) и вернусь к вам. - person prog-fh; 28.03.2020
comment
@AvivCohn Я только что проверил (см. редактирование), и он работает точно так, как я помнил. Имейте в виду, что я использовал mingw-w64, а не msys64+mingw (могут быть небольшие различия). - person prog-fh; 29.03.2020
comment
@AvivCohn было добавлено редактирование, чтобы ответить на третий вопрос, который вы добавили впоследствии к своему вопросу (о .lib файлах). - person prog-fh; 29.03.2020
comment
Здравствуйте, еще раз спасибо за ваш подробный вклад. Быстрый вопрос: когда я использую Dependency Walker для поиска зависимостей моего .exe, скомпилированного с использованием GCC из Mingw-w64, он не показывает зависимости от libgcc*, хотя почти все программы, скомпилированные с использованием GCC, должны зависеть от libgcc*. Любая идея, почему это так? - person Aviv Cohn; 11.04.2020