Clang C++跨编译器 - 从Mac OS X生成Windows可执行文件

42

我在我的 Mac 上使用 Clang 编译器,使用 Xcode 创建了一个 C++ 应用程序。

我想编译我的源文件以创建可在 Windows 机器上运行的可执行文件,但我无法让 Clang 生成可执行文件。

以下是我尝试过的方法:

clang++ -std=c++11 -stdlib=libc++ -arch x86_64 class1.cpp class2.cpp... -o executable.exe

这将创建一个可执行文件,但是无法运行(Windows提示我应用程序为16位,无法在64位上运行-不理解此问题-)

clang++ -std=c++11 -stdlib=libc++ -target i386-pc-win32 class1.cpp class2.cpp 
出现了奇怪的问题,每当我使用-target标志时,就会出现错误,指出编译器找不到 <iostream> 头文件,但在其他情况下没有这种情况。
我尝试过使用-Ipath/to/iostreamfolder/,但结果并没有改善。
有什么建议吗?谢谢!
我还尝试了'-triple x86-pc-win32'标志,但是我收到了如下警告:clang: warning: argument unused during compilation: '-triple x86-pc-win32'

1
你正在生成哪种可执行格式?file executable.exe 命令输出了什么?也许你正在创建一个有着 .exe 文件扩展名的 OS X 二进制文件。 - tadman
在Unix环境中,executable.exe是文档类型。当我将它传输到Windows并尝试从命令行调用时,我会收到错误消息:“由于与64位Windows版本不兼容,该程序或功能无法启动或运行”。 - gavlaaaaaaaa
3
请使用命令file executable.exe查看它认为该文件是什么类型。file工具通常很擅长识别你创建的文件类型。如果它显示"Mach-O 64-bit executable x86_64",那么你创建了错误的二进制文件。如果显示"PE32 executable for MS Windows",那么你就在正确的轨道上。 - tadman
是的,我得到了Mach-O 64... 我明白在我提供的示例中生成这个可执行文件时,我没有指定架构平台,因此我得到了这个可执行文件。然而,我尝试指定win32等平台的任何尝试都被编译器忽略或不喜欢。 - gavlaaaaaaaa
4个回答

56

以下是使用llvm/clang在Mac OS X上逐步构建Hello World .exe的说明。

使用Clang/LLVM在Mac OS X上交叉编译Hello World为Windows

使用homebrew安装llvm。这将包括clang和llvm链接器。

brew install llvm

你需要访问Visual Studio C++库和头文件,这些可以通过Windows 10虚拟机(VM)或Windows 10计算机上的Visual Studio 2017获得。在Windows上安装Visual Studio,并通过Visual Studio Installer包含以下“单独组件”:

  • Windows通用CRT SDK
  • Windows通用C运行时
  • 针对UWP的Windows 10 SDK (X.X.X.X) : C++
  • VC++ 2017 vXXX 工具集(x86,x64)
  • Visual C++ 2017 Redistributable Update
  • C++/CLI支持

从你的Mac获取MSVC库和头文件的访问权限。

  • (选项1) 使用你的Windows虚拟机并创建主机和客户机之间的共享文件夹。
  • (选项2) 在你的Windows计算机上创建远程共享并从你的Mac连接到它。
  • (选项3) 按照任何授权条款将库和头文件复制到你的Mac上。

查找与以下相对应的llvm+MSVC安装中的特定目录:

// LLVM:
INCLUDES: /usr/local/Cellar/llvm/5.0.0/include

// MSVC:
INCLUDES: "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.11.25503\include"
LIBS: "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.11.25503\lib\x86"

// C Runtime Library (CRT):
INCLUDES: "C:\Program Files (x86)\Windows Kits\10\Include\10.0.15063.0\ucrt"
LIBS: "C:\Program Files (x86)\Windows Kits\10\Include\10.0.15063.0\ucrt"

// User-Mode Library (UM):
INCLUDES: "C:\Program Files (x86)\Windows Kits\10\Include\10.0.15063.0\um"
LIBS: "C:\Program Files (x86)\Windows Kits\10\Lib\10.0.15063.0\um\x86"

// 'Shared' includes:
INCLUDES: "C:\Program Files (x86)\Windows Kits\10\Include\10.0.15063.0\shared"

// WinRT includes:
INCLUDES: "C:\Program Files (x86)\Windows Kits\10\Include\10.0.15063.0\winrt"

// Figure out your MSC 'version', e.g.
Visual C++ 2012 (11.0)   -->     MSC_VER=1700
Visual C++ 2013 (12.0)   -->     MSC_VER=1800
Visual C++ 2015 (14.0)   -->     MSC_VER=1900
Visual C++ 2017 (15.0)   -->     MSC_VER=1910

创建你的Hello World源代码:

// hello.cc

#include <cstdio>

int main(int argc, char* argv[]) {
  printf("Hello, World!\n");

  return 0;
}

使用clang进行编译:

clang -target i686-pc-win32 \
  -fms-compatibility-version=19 \
  -fms-extensions \
  -fdelayed-template-parsing \ 
  -fexceptions \
  -mthread-model posix \
  -fno-threadsafe-statics \
  -Wno-msvc-not-found \
  -DWIN32 \
  -D_WIN32 \
  -D_MT \
  -D_DLL \
  -Xclang -disable-llvm-verifier \
  -Xclang '--dependent-lib=msvcrt' \
  -Xclang '--dependent-lib=ucrt' \
  -Xclang '--dependent-lib=oldnames' \
  -Xclang '--dependent-lib=vcruntime' \
  -D_CRT_SECURE_NO_WARNINGS \
  -D_CRT_NONSTDC_NO_DEPRECATE \
  -U__GNUC__ \
  -U__gnu_linux__ \
  -U__GNUC_MINOR__ \
  -U__GNUC_PATCHLEVEL__ \
  -U__GNUC_STDC_INLINE__  \
  -I/usr/local/Cellar/llvm/5.0.0/include \
  -I/c/Program\ Files\ (x86)/Microsoft\ Visual\ Studio/2017/Community/VC/Tools/MSVC/14.11.25503/include \
  -I/c/Program\ Files\ (x86)/Windows\ Kits/10/Include/10.0.15063.0/ucrt \
  -I/c/Program\ Files\ (x86)/Windows\ Kits/10/Include/10.0.15063.0/shared \
  -I/c/Program\ Files\ (x86)/Windows\ Kits/10/Include/10.0.15063.0/winrt \
  -c hello.cc -o hello.o

使用clang驱动的lld链接器进行链接:

clang -fuse-ld=lld -target i686-pc-win32 -Wl,-machine:x86 -fmsc-version=1900 \
  -o hello.exe hello.o \
  -L/external/code8-cc/cc/msvctoolchain/x86/lib/msvc \
  -L/external/code8-cc/cc/msvctoolchain/x86/lib/um \
  -L/code8-cc/cc/msvctoolchain/x86/lib/ucrt
  -nostdlib -lmsvcrt -Wno-msvc-not-found 

将 hello.exe 复制到您的 Windows 计算机或 Windows 虚拟机中,并在 PowerShell 中运行:

.\hello.exe

要构建 64 位版本,请更改为 '-target x86_64-pc-win32','-Wl,-machine:x64',并链接到 x64 库。


1
假设从任何Linux软件包管理器安装了现代的LLVM,我期望剩余的步骤能够正常工作。 - Erik MC
如果我在Ubuntu上工作且文件是.scala而不是.cpp,那么命令应该是什么? - NaseemMahasneh
我创建了一个CMake工具链文件,可以轻松地进行交叉编译项目。我使用了LLVM的工具链作为基础,并进行了调整,因为我正在运行WSL-Ubuntu并希望使用已安装的工具链。 - Julian Kirsch

29

Clang原则上可以用作交叉编译器:与大多数编译器不同,clang/LLVM在同一个二进制文件中包含了不同平台的组件(例如代码生成器、汇编器和链接器)。

然而,在生产环境中试图将其用作交叉编译器时,您会遇到许多问题:

  • 你需要平台库和头文件。要生成一个在Windows上运行的可执行文件,你需要Windows头文件和Windows库,你想要链接的库,如果你是动态链接,则需要导入库,如果是静态链接,则需要静态库。你应该能够从Visual Studio的安装中获得这些。

  • 许多C++特性,如名称重整和RTTI支持,在Windows上并不完整。使用Clang在Windows上编译时,你会遇到同样的问题。如今,Windows C++支持几乎完整

  • LLVM项目包括lld链接器,它已经足够成熟,可以在x86 Windows上进行自举,因此可能适用于作为跨平台链接器,但lld尚未成为clang发行版的标准部分。OS X上的Clang仍然使用OS X平台链接器ld,Windows上的Clang也是如此(link.exe)。你需要获取lld并找出如何与它链接,或者找到其他跨平台链接器。

  • clang驱动程序并没有编写成跨平台编译器驱动程序。你可能需要做更多的手工工作来运行跨平台编译。看一下clang -###的输出:clang驱动程序为你构造了该命令,但你可能需要手动完成与clang驱动程序相同的工作。由于clang在跨平台编译方面得到的测试要少得多,所以你可能会遇到更多的错误。

  • Xcode无法帮助你解决这些问题。它可以配置clang来为OS X或iOS构建,但你必须手动配置跨平台构建到Windows。

我相信可以组合LLVM环境,从而在OS X或Linux上构建一个C语言“Hello, World” Windows可执行文件,但还没有准备好让Xcode将“Windows”项目添加到可能的目标平台列表中。
如果你不是编译器开发者,最好将源代码复制到Windows机器上,并使用Visual Studio进行构建。如果你是或想成为编译器开发者,那么请帮助推动Clang的交叉编译能力。我认为Clang的通用驱动程序项目非常令人兴奋,我真的希望看到进展继续。
我已成功进行了相反的交叉编译:在Windows上编译Mac OS X可执行文件。对于小型程序,即直接编译.cpp文件,这很容易手动完成。
首先,Mac OS X开发工具带有“SDK”,其中包含特定操作系统的所有系统库和头文件。最大的挑战是找出如何在保留SDK中所有符号链接的同时将SDK传输到Windows。(由于某种原因,在Windows上创建符号链接需要提升权限,因此在OS X上使用符号链接生成tar.gz后,我必须在Windows上以管理员身份运行7zip才能正确扩展存档。)
一旦SDK在Windows上可用,只需一个标志便可告诉clang在哪里获取所有系统依赖项:-isysroot。这与-target标志结合使用就足以告诉clang如何为OS X生成完整的对象文件。
对于链接,我手动使用了lld,因为编译器驱动程序似乎不支持使用lld进行交叉链接。lld支持类似的标志来确定目标系统库。
最后一步只是将生成的可执行文件复制到OS X机器上,启用执行权限(Windows不支持相同的文件权限,因此在构建时不会设置执行位),然后运行结果。

1
是的,我发现将我的Mac引导到Windows并安装Visual Studio 2013是前进的方法。这显然很有效,但我想知道是否有交叉编译选项,因此提出了我的问题。谢谢。 - gavlaaaaaaaa
1
@DarioOO 您需要从Visual Studio的安装中提取Windows库和头文件。您还需要clang和lld的构建版本。由于这些领域的工作正在进行中,因此您应该从最新的源代码构建而不是使用发布的二进制文件。clang lld - bames53
1
@DarioOO 噢,这个问题是关于针对 Windows 的。如果你想在 Windows 上编译并针对其他平台,你可能会有更好的运气,因为 clang 已经完全支持了 Linux 和 OS X 平台。你需要从你想要针对的平台的安装中提取头文件和库,而不是从 VS 中提取。否则,流程将是相同的。 - bames53
@bames53,您能否提供有关如何在Windows上使用lld链接OS X二进制文件的更多详细信息?我正在尝试从Windows交叉编译到OS X,但lld似乎只支持Linux(ld.lld.exe)和Windows(ld-link.exe)目标。 - Karel Petranek
1
@KarelPetranek 请尝试:lld -flavor darwin - bames53
显示剩余3条评论

8

0
感谢xwin项目,现在可以轻松获取Windows头文件和库文件,并在其他平台如Mac或Linux上使用:
使用xwin安装头文件和库:
export WINSDK_PATH="$HOME/winsdk"
./xwin --accept-license splat --preserve-ms-arch-notation --output "$WINSDK_PATH"

安装clang、clang-cl、lld以及OpenMP所需的libomp。在Mac上,brew install llvm应该可以工作,在Debian/Ubuntu上,sudo apt install clang lld libomp-13-dev clang-tools。也可以使用conda进行安装:conda install clang llvm-openmp
使用clang-cl编译代码。Clang-cl可以像Microsoft编译器cl.exe一样使用。C运行时库可以使用选项/MT静态链接(默认)或使用/MD动态链接(对于大多数用途可能更好):
# 从C++代码创建程序
clang-cl  --target=x86_64-pc-windows-msvc -fuse-ld=lld /winsdkdir "$WINSDK_PATH/sdk" /vctoolsdir "$WINSDK_PATH/crt" /MD hello.cpp -o hello.exe

# 从C代码创建DLL并使用OpenMP、AVX2和LTO("/openmp"、"/arch:avx2"、"-flto")
clang-cl --target=x86_64-pc-windows-msvc -fuse-ld=lld /openmp /arch:avx2 -flto /LD /winsdkdir "$WINSDK_PATH/sdk" /vctoolsdir "$WINSDK_PATH/crt" /MD dll_test.c

# 不使用clang-cl的相同操作
clang -o dll_test.dll \
  -target x86_64-pc-windows-msvc \
  -isystem "$WINSDK_PATH/crt/include" \
  -isystem "$WINSDK_PATH/sdk/Include/ucrt" \
  -isystem "$WINSDK_PATH/sdk/Include/shared" \
  -isystem "$WINSDK_PATH/sdk/Include/um" \
  -Xclang --dependent-lib=msvcrt \
  -fuse-ld=lld "-L$WINSDK_PATH/crt/lib/x64" \
  "-L$WINSDK_PATH/sdk/Lib/ucrt/x64" \
  "-L$WINSDK_PATH/sdk/Lib/um/x64" \
  -flto \
  -shared \
  -march=x86-64-v3 \
  c_test.c

当使用clang而不是clang-cl时,添加-Xclang --dependent-lib=msvcrt会创建一个像/MD一样的动态链接二进制文件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接