将使用 MSYS2 和 MingW 构建的程序发送给最终用户的正确方法是什么?

What is the correct way to ship a program built using MSYS2 and MingW to end users?

我正在使用 Msys2 和 MingW 在 Windows 上构建一个 C 应用程序。

此应用程序将发送到不精通技术的最终用户的桌面 Windows 机器上。我有两个问题:

  1. 将使用 Msys64 和 MingW 构建的 Windows 程序发送到未安装这些的不同 Windows 机器的最佳实践方法是什么?互联网上和 SO 上的一些消息来源说我们别无选择,只能将 exe 文件与它所依赖的 MingW dlls 一起分发到同一目录中。或者,使用 MingW dll 静态 link exe。但是,我想确定这些是否确实是这样做的标准方法。 MingW 没有更好的方法似乎很奇怪。

  2. 假设上述 exe 也在某些时候使用 LoadLibrary API 动态加载 dll(想想插件系统)。 dll 也是使用 MingW 构建的(在这种情况下,当然会与 exe 一起发送给最终用户)。我需要做些什么来确保 dll 在没有安装 MingW 的用户机器上成功加载?

  3. 除了 2:上述 DLL 是否需要在开发机器上编译期间存在导入库 (.lib),在终端机器上启动期间 and/or?

编辑:请让我澄清一下,我 不是 使用 MSYS2 作为命令行 shell。我通过 Windows cmd 直接使用 Mingw gcc,并且只是从 MSYS2 下载目录中获取 Mingw。

我看不出这个解决方案有任何缺点。
我不是一个 Windows 用户,但我必须承认解决方案 隐式查找可执行文件所在的动态库 很有帮助。
您将应用程序的全部内容(可执行文件和 动态 libraries/plugins) 随处随你管理 运行 可执行文件将找到其他所有内容。

当然,如果您计划交付许多不同的应用程序 使用动态库的公共子集,它可能会更好 将所有这些库放在一个共同的地方并调整 相应的 PATH 环境变量。
但是对于一个应用来说,不值得。

在 mingw-w64 中(不完全是 Msys64 和 MingW,但非常接近) 基于我几个月前交付的应用程序(带插件), 我刚刚提供了 libgcc_s_seh-1.dll、libstdc++-6.dll 和 libwinpthread-1.dll 作为我自己的二进制文件的补充,它 工作没有任何问题。
使用 objdump.exe -p my_program.exe (然后递归 在显示的结果上)有助于找到所需的动态 库(如 Linux 上的 ldd 或 Macosx 上的 otool -L)。

这就是我喜欢的类似 mingw 的解决方案:它使本地 Windows 不依赖于许多其他异常的应用程序 组件(用户必须首先检索)。


部分.lib文件无需处理;建立一个 .dll 并对其进行链接就足够了(请参见下面的示例)。
它的工作方式与我们在 UNIX 上的工作方式完全相同(使用 .so 个文件)。
我真的不知道为什么 Visual-C++ 依赖一个如此复杂的 .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.dlllibwinpthread-1.dll 放在 与 prog.exe.
相同的目录 它甚至可以在 prog.exe 通过单击(不是 从命令行);看到这个我必须添加一个无限循环 在程序结束时保持 window 打开足够长的时间 查看显示的消息。

我认为这个问题是关于 MSYS2 和 mingw-w64 的,因为没有 "msys64" 这样的东西。

MSYS2 提供三种不同的 target 系统:

  • Windows 32 位
  • Windows 64 位
  • MSYS2

在 MSYS2 安装下,每个开发工具和包都有完全独立的源代码树。

不要将其与不同的构建系统混淆——构建系统可以是 Win32 或 Win64。例如,如果您选择 Win64 作为构建系统并选择安装所有三个目标系统,那么您将拥有树 msys64/mingw32msys64/mingw64msys64/usr

MSYS2 安装程序为您安装的每个目标创建启动脚本。


如果您的目标是 Win32 或 Win64,则编译后的二进制文件(exe 或 dll)可以作为独立产品分发。您可以使用 gcc 或 clang 进行静态构建,生成单个独立的可执行文件,或者您可以进行依赖于随可执行文件分发的 DLL 的构建。

二进制分发包包含目标操作系统未提供的所有文件是标准的,这不是 mingw-w64 的特性。

LoadLibrary 搜索路径描述为 in the MSDN documentation .

如果您以 MSYS2 为目标,那么生成的二进制文件应该在 MSYS2 shell 下 运行。该目标提供了一些 POSIX 特性,mingw-w64 不直接支持这些特性。它可以被认为是 Cygwin 的一个分支。可以分发针对 MSYS2 的二进制文件以及从您使用依赖项跟踪器识别的 MSYS2 安装复制的一堆 DLL。