Difference between revisions of "Cross-compiling with LDC"
(Created page with "LDC is an implicit cross-compiler, i.e., you don't need multiple LDC executables for each host → target combination. This page shows how to set up LDC for cross-compilation...") |
m (Add importC notes) |
||
(21 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
− | LDC is an implicit cross-compiler, i.e., you don't need multiple LDC executables for each host → target combination. This page shows how to set up LDC for cross-compilation and -linking. | + | LDC is an implicit cross-compiler, i.e., if you have an LDC binary, you can cross-compile, and you don't need multiple LDC executables for each host → target combination. This page shows how to set up LDC for cross-compilation and -linking. |
== The -mtriple command-line option == | == The -mtriple command-line option == | ||
− | The fundamental instrument for cross-compilation is LDC's < | + | The fundamental instrument for cross-compilation is LDC's <code>-mtriple</code> command-line option (the corresponding clang switch is <tt>-target</tt>). It defines the target for code-generation, incl. architecture and operating system. |
Here are some popular triples: | Here are some popular triples: | ||
− | * < | + | * <code>x86_64-linux-gnu</code>: Linux x86_64 with glibc |
− | * < | + | * <code>x86_64-apple-macos10.12</code>: macOS x86_64 ≥ 10.12 |
− | * < | + | * <code>x86_64-windows-msvc</code>: Windows x64 |
− | * < | + | * <code>i686-linux-musl</code>: Linux x86 with musl |
− | * < | + | * <code>i686-windows-msvc</code>: Windows x86 |
− | * < | + | * <code>armv6-linux-gnueabihf</code>: Linux ARMv6 with glibc and hard-float ABI |
− | * < | + | * <code>armv7a-unknown-linux-androideabi</code>: Android ARMv7-A |
− | * < | + | * <code>aarch64-linux-android</code>: Android AArch64 |
+ | * <code>aarch64-linux-gnu</code>: Linux AArch64 with glibc | ||
+ | * <code>arm64-apple-macos11.0</code>: macOS arm64 ≥ 11.0 | ||
+ | * <code>arm64-apple-ios12.0</code>: iOS arm64 ≥ 12.0 | ||
+ | * <code>wasm32-unknown-unknown-webassembly</code>: 32-bit WebAssembly | ||
− | Run < | + | Run <code>ldc2 -version</code> to check your default/host triple (<tt>Default target</tt>). |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
== Cross-compiling == | == Cross-compiling == | ||
− | + | The supported targets depend on how the LLVM linked against your LDC was built (target architectures enabled in the CMake build command-line). | |
− | Running < | + | Running <code>ldc2 -version</code> shows the list of enabled/registered LLVM backends. |
− | * < | + | * <code>ldc2 -mtriple=x86_64-windows-msvc -c foo.d</code>: generates a <tt>foo.obj</tt> COFF object file for Win64 |
+ | * <code>ldc2 -mtriple=x86_64-apple-macos11 -c foo.d</code>: generates a <tt>foo.o</tt> Mach-O object file for macOS | ||
+ | * <code>ldc2 -mtriple=aarch64-linux-gnu -c foo.d</code>: generates a <tt>foo.o</tt> ELF object file for Linux AArch64 | ||
== Generating static libraries == | == Generating static libraries == | ||
− | Generating suited static libraries (< | + | Generating suited static libraries (<code>-lib</code>) works for all targets, on all hosts. |
− | * < | + | * <code>ldc2 -mtriple=x86_64-linux-gnu -lib foo.o</code>: generates a <tt>libfoo.a</tt> archive |
− | * < | + | * <code>ldc2 -mtriple=x86_64-windows-msvc -lib foo.obj</code>: generates a <tt>foo.lib</tt> library |
As long as your LLVM features the backend for your target, you can obviously directly cross-compile and -archive a library: | As long as your LLVM features the backend for your target, you can obviously directly cross-compile and -archive a library: | ||
− | * < | + | * <code>ldc2 -mtriple=… -lib foo.d</code> |
== Cross-linking executables and shared libraries == | == Cross-linking executables and shared libraries == | ||
Line 47: | Line 47: | ||
# Default libraries (e.g., druntime and Phobos) for the target, as (non-<tt>betterC</tt>) binaries are linked against these default libraries. | # Default libraries (e.g., druntime and Phobos) for the target, as (non-<tt>betterC</tt>) binaries are linked against these default libraries. | ||
− | # C runtime libraries (and startup object files etc.) for the target, as druntime and Phobos don't completely reinvent the wheel and build on top of a C runtime (glibc, musl, Visual C++, Bionic, …). This also applies to < | + | # C runtime libraries (and startup object files etc.) for the target, as druntime and Phobos don't completely reinvent the wheel and build on top of a C runtime (glibc, musl, Visual C++, Bionic, …). This also applies to <code>-betterC</code> binaries. |
# A cross-linker. | # A cross-linker. | ||
=== Default libraries === | === Default libraries === | ||
− | If there's a prebuilt LDC package for your desired target, then the simplest variant is to download it and copy the <tt>lib[32,64]</tt> subdirectories to your host LDC installation (or wherever you like). | + | If there's a [https://github.com/ldc-developers/ldc/releases/ prebuilt LDC package] for your desired target, then the simplest variant is to download it (matching the version of your host LDC) and copy the <tt>lib[32,64]</tt> subdirectories to your host LDC installation (or wherever you like). |
If there's no prebuilt LDC package or you prefer cross-compiling druntime and Phobos yourself, check out [[Building LDC runtime libraries]]. | If there's no prebuilt LDC package or you prefer cross-compiling druntime and Phobos yourself, check out [[Building LDC runtime libraries]]. | ||
− | === C runtime libraries === | + | === C runtime libraries and cross-linker === |
==== Windows targets ==== | ==== Windows targets ==== | ||
Line 62: | Line 62: | ||
The WinSDK and Visual C++ libraries are included in the prebuilt LDC Windows packages since v1.13 (in the <tt>mingw</tt> subdirectory in the <tt>lib</tt> directories), so you're already set after copying the lib directories in the previous step. | The WinSDK and Visual C++ libraries are included in the prebuilt LDC Windows packages since v1.13 (in the <tt>mingw</tt> subdirectory in the <tt>lib</tt> directories), so you're already set after copying the lib directories in the previous step. | ||
− | + | For Windows targets, LLVM's LLD works nicely as cross-linker on all hosts. Official prebuilt LDC packages feature an integrated LLD and use it by default for Windows targets on non-Windows hosts. | |
− | + | ==== Apple targets ==== | |
− | + | Xcode comes with a cross-compilation toolchain for non-native other Apple targets (arm64, iOS, simulators...). Apple clang needs something like <tt>-target arm64-apple-ios12.0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk</tt> in the linking cmdline; these flags can be added as <tt>-Xcc</tt> flags passed to LDC. | |
− | |||
− | |||
− | |||
− | |||
==== Non-Apple POSIX targets ==== | ==== Non-Apple POSIX targets ==== | ||
− | The preferred way is to | + | The preferred way is to install a gcc/clang toolchain for cross-compilation, e.g., the <tt>gcc-aarch64-linux-gnu</tt> package on Debian/Ubuntu hosts when targeting Linux AArch64, or the [https://developer.android.com/ndk/downloads NDK] for Android targets. These toolchains include the C libraries as well as a cross-linker-driver (e.g., <tt>aarch64-linux-gnu-gcc</tt> or <tt>aarch64-linux-android21-clang</tt>) configured for those libs and startup object files etc. |
− | + | === Tweaking the LDC configuration file === | |
− | |||
− | + | LDC needs information about where to find the target's libraries and which cross-linker to use. While you can specify that information on the command-line, it's tedious and error-prone, so the preferred way is to extend the <tt>etc/ldc2.conf</tt> configuration file by appending a section for your target triple. | |
− | + | These settings will be used automatically when specifying a matching target triple, so that e.g. <code>ldc2 -mtriple=x86_64-windows-msvc foo.d</code> is enough to generate a <tt>foo.exe</tt> Win64 executable. | |
− | + | Exemplary sections: | |
+ | {| class="wikitable" style="background-color: white;" | ||
+ | |- | ||
+ | | '''Win64'''<br><br>Assuming the <tt>lib</tt> directory from the prebuilt LDC windows-x64<br>package was copied to <tt><LDC root>/lib-win64</tt>. | ||
+ | | | ||
<pre> | <pre> | ||
"x86_64-.*-windows-msvc": | "x86_64-.*-windows-msvc": | ||
Line 89: | Line 88: | ||
switches = [ | switches = [ | ||
"-defaultlib=phobos2-ldc,druntime-ldc", | "-defaultlib=phobos2-ldc,druntime-ldc", | ||
− | |||
]; | ]; | ||
lib-dirs = [ | lib-dirs = [ | ||
Line 96: | Line 94: | ||
}; | }; | ||
</pre> | </pre> | ||
− | + | |- | |
− | + | | '''Linux AArch64'''<br><br>Assuming the <tt>lib</tt> directory from the prebuilt LDC linux-aarch64<br>package was copied to <tt><LDC root>/lib-aarch64</tt> and the<br>cross-gcc executable is found via <tt>aarch64-linux-gnu-gcc</tt>. | |
+ | | | ||
<pre> | <pre> | ||
"aarch64-.*-linux-gnu": | "aarch64-.*-linux-gnu": | ||
Line 111: | Line 110: | ||
}; | }; | ||
</pre> | </pre> | ||
+ | |- | ||
+ | | '''Android ARMv7-A'''<br><br>Assuming the <tt>lib</tt> directory from the prebuilt LDC android-armv7a<br>package was copied to <tt><LDC root>/lib-armv7a</tt> and the<br>Android NDK r26d has been unzipped into <tt>/home/me</tt>. | ||
+ | | | ||
+ | <pre> | ||
+ | "armv7a-.*-linux-android": | ||
+ | { | ||
+ | switches = [ | ||
+ | "-defaultlib=phobos2-ldc,druntime-ldc", | ||
+ | "-gcc=/home/me/android-ndk-r26d/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi29-clang", | ||
+ | ]; | ||
+ | lib-dirs = [ | ||
+ | "%%ldcbinarypath%%/../lib-armv7a", | ||
+ | ]; | ||
+ | rpath = "%%ldcbinarypath%%/../lib-armv7a"; | ||
+ | }; | ||
+ | </pre> | ||
+ | |- | ||
+ | | '''Android AArch64'''<br><br>Assuming the <tt>lib</tt> directory from the prebuilt LDC android-aarch64<br>package was copied to <tt><LDC root>/lib-aarch64</tt> and the<br>Android NDK r26d has been unzipped into <tt>/home/me</tt>. | ||
+ | | | ||
+ | <pre> | ||
+ | "aarch64-.*-linux-android": | ||
+ | { | ||
+ | switches = [ | ||
+ | "-defaultlib=phobos2-ldc,druntime-ldc", | ||
+ | "-gcc=/home/me/android-ndk-r26d/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android30-clang", | ||
+ | ]; | ||
+ | lib-dirs = [ | ||
+ | "%%ldcbinarypath%%/../lib-aarch64", | ||
+ | ]; | ||
+ | rpath = "%%ldcbinarypath%%/../lib-aarch64"; | ||
+ | }; | ||
+ | </pre> | ||
+ | |- | ||
+ | |- | ||
+ | | '''macOS x86_64'''<br><br>Assuming you are running on macOS/arm64 and have copied the<br><tt>lib</tt> directory from the prebuilt LDC osx-x86_64 package to<br><tt><LDC root>/lib-x64</tt>. | ||
+ | | | ||
+ | <pre> | ||
+ | "x86_64-apple-": | ||
+ | { | ||
+ | switches = [ | ||
+ | "-defaultlib=phobos2-ldc,druntime-ldc", | ||
+ | "-Xcc=-arch", | ||
+ | "-Xcc=x86_64", | ||
+ | "-Xcc=-isysroot", | ||
+ | "-Xcc=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk", | ||
+ | ]; | ||
+ | lib-dirs = [ | ||
+ | "%%ldcbinarypath%%/../lib-x64", | ||
+ | ]; | ||
+ | rpath = "%%ldcbinarypath%%/../lib-x64"; | ||
+ | }; | ||
+ | </pre> | ||
+ | |- | ||
+ | |} | ||
+ | |||
+ | == ImportC and C cross-preprocessing == | ||
+ | |||
+ | When cross-compiling and using importC (importing/compiling <tt>.c</tt> files), it's important that a matching C cross-preprocessor is used. It can be customized via <tt>-gcc</tt> and <tt>-Xcc</tt> / <tt>-P</tt>. By setting up <tt>etc/ldc2.conf</tt> for cross-linking as shown above, you should be set already. | ||
+ | |||
+ | == Cross-compiling with dub == | ||
+ | |||
+ | Starting with dub v1.18 (or v1.17+ bundled with official LDC v1.18 packages), you can cross-compile entire dub projects incl. their dependencies by simply adding <code>--arch=<LDC triple></code> in the dub command-line, after setting up your <tt>etc/ldc2.conf</tt> configuration file as detailed above. | ||
+ | |||
+ | == Limitations == | ||
− | + | LDC doesn't have a software compile-time real for arbitrary target <code>real</code> precision yet, but uses the host's <tt>real</tt> for storage and CTFE (exception: it uses 80-bit x87 compile-time precision on Windows/MSVC hosts). So if the target <tt>real</tt> features a higher precision (e.g., 32-bit ARM → x86, x86 → non-Apple AArch64), <code>real.max</code> will overflow to infinity, <code>real.min_normal</code> may underflow to 0, CTFE computations will be performed with lower host precision etc. | |
[[Category:LDC]] | [[Category:LDC]] |
Latest revision as of 11:22, 11 August 2024
LDC is an implicit cross-compiler, i.e., if you have an LDC binary, you can cross-compile, and you don't need multiple LDC executables for each host → target combination. This page shows how to set up LDC for cross-compilation and -linking.
Contents
The -mtriple command-line option
The fundamental instrument for cross-compilation is LDC's -mtriple
command-line option (the corresponding clang switch is -target). It defines the target for code-generation, incl. architecture and operating system.
Here are some popular triples:
x86_64-linux-gnu
: Linux x86_64 with glibcx86_64-apple-macos10.12
: macOS x86_64 ≥ 10.12x86_64-windows-msvc
: Windows x64i686-linux-musl
: Linux x86 with musli686-windows-msvc
: Windows x86armv6-linux-gnueabihf
: Linux ARMv6 with glibc and hard-float ABIarmv7a-unknown-linux-androideabi
: Android ARMv7-Aaarch64-linux-android
: Android AArch64aarch64-linux-gnu
: Linux AArch64 with glibcarm64-apple-macos11.0
: macOS arm64 ≥ 11.0arm64-apple-ios12.0
: iOS arm64 ≥ 12.0wasm32-unknown-unknown-webassembly
: 32-bit WebAssembly
Run ldc2 -version
to check your default/host triple (Default target).
Cross-compiling
The supported targets depend on how the LLVM linked against your LDC was built (target architectures enabled in the CMake build command-line).
Running ldc2 -version
shows the list of enabled/registered LLVM backends.
ldc2 -mtriple=x86_64-windows-msvc -c foo.d
: generates a foo.obj COFF object file for Win64ldc2 -mtriple=x86_64-apple-macos11 -c foo.d
: generates a foo.o Mach-O object file for macOSldc2 -mtriple=aarch64-linux-gnu -c foo.d
: generates a foo.o ELF object file for Linux AArch64
Generating static libraries
Generating suited static libraries (-lib
) works for all targets, on all hosts.
ldc2 -mtriple=x86_64-linux-gnu -lib foo.o
: generates a libfoo.a archiveldc2 -mtriple=x86_64-windows-msvc -lib foo.obj
: generates a foo.lib library
As long as your LLVM features the backend for your target, you can obviously directly cross-compile and -archive a library:
ldc2 -mtriple=… -lib foo.d
Cross-linking requires more work, as we have these requirements:
- Default libraries (e.g., druntime and Phobos) for the target, as (non-betterC) binaries are linked against these default libraries.
- C runtime libraries (and startup object files etc.) for the target, as druntime and Phobos don't completely reinvent the wheel and build on top of a C runtime (glibc, musl, Visual C++, Bionic, …). This also applies to
-betterC
binaries. - A cross-linker.
Default libraries
If there's a prebuilt LDC package for your desired target, then the simplest variant is to download it (matching the version of your host LDC) and copy the lib[32,64] subdirectories to your host LDC installation (or wherever you like).
If there's no prebuilt LDC package or you prefer cross-compiling druntime and Phobos yourself, check out Building LDC runtime libraries.
C runtime libraries and cross-linker
Windows targets
The WinSDK and Visual C++ libraries are included in the prebuilt LDC Windows packages since v1.13 (in the mingw subdirectory in the lib directories), so you're already set after copying the lib directories in the previous step.
For Windows targets, LLVM's LLD works nicely as cross-linker on all hosts. Official prebuilt LDC packages feature an integrated LLD and use it by default for Windows targets on non-Windows hosts.
Apple targets
Xcode comes with a cross-compilation toolchain for non-native other Apple targets (arm64, iOS, simulators...). Apple clang needs something like -target arm64-apple-ios12.0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk in the linking cmdline; these flags can be added as -Xcc flags passed to LDC.
Non-Apple POSIX targets
The preferred way is to install a gcc/clang toolchain for cross-compilation, e.g., the gcc-aarch64-linux-gnu package on Debian/Ubuntu hosts when targeting Linux AArch64, or the NDK for Android targets. These toolchains include the C libraries as well as a cross-linker-driver (e.g., aarch64-linux-gnu-gcc or aarch64-linux-android21-clang) configured for those libs and startup object files etc.
Tweaking the LDC configuration file
LDC needs information about where to find the target's libraries and which cross-linker to use. While you can specify that information on the command-line, it's tedious and error-prone, so the preferred way is to extend the etc/ldc2.conf configuration file by appending a section for your target triple.
These settings will be used automatically when specifying a matching target triple, so that e.g. ldc2 -mtriple=x86_64-windows-msvc foo.d
is enough to generate a foo.exe Win64 executable.
Exemplary sections:
Win64 Assuming the lib directory from the prebuilt LDC windows-x64 package was copied to <LDC root>/lib-win64. |
"x86_64-.*-windows-msvc": { switches = [ "-defaultlib=phobos2-ldc,druntime-ldc", ]; lib-dirs = [ "%%ldcbinarypath%%/../lib-win64", ]; }; |
Linux AArch64 Assuming the lib directory from the prebuilt LDC linux-aarch64 package was copied to <LDC root>/lib-aarch64 and the cross-gcc executable is found via aarch64-linux-gnu-gcc. |
"aarch64-.*-linux-gnu": { switches = [ "-defaultlib=phobos2-ldc,druntime-ldc", "-gcc=aarch64-linux-gnu-gcc", ]; lib-dirs = [ "%%ldcbinarypath%%/../lib-aarch64", ]; rpath = "%%ldcbinarypath%%/../lib-aarch64"; }; |
Android ARMv7-A Assuming the lib directory from the prebuilt LDC android-armv7a package was copied to <LDC root>/lib-armv7a and the Android NDK r26d has been unzipped into /home/me. |
"armv7a-.*-linux-android": { switches = [ "-defaultlib=phobos2-ldc,druntime-ldc", "-gcc=/home/me/android-ndk-r26d/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi29-clang", ]; lib-dirs = [ "%%ldcbinarypath%%/../lib-armv7a", ]; rpath = "%%ldcbinarypath%%/../lib-armv7a"; }; |
Android AArch64 Assuming the lib directory from the prebuilt LDC android-aarch64 package was copied to <LDC root>/lib-aarch64 and the Android NDK r26d has been unzipped into /home/me. |
"aarch64-.*-linux-android": { switches = [ "-defaultlib=phobos2-ldc,druntime-ldc", "-gcc=/home/me/android-ndk-r26d/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android30-clang", ]; lib-dirs = [ "%%ldcbinarypath%%/../lib-aarch64", ]; rpath = "%%ldcbinarypath%%/../lib-aarch64"; }; |
macOS x86_64 Assuming you are running on macOS/arm64 and have copied the lib directory from the prebuilt LDC osx-x86_64 package to <LDC root>/lib-x64. |
"x86_64-apple-": { switches = [ "-defaultlib=phobos2-ldc,druntime-ldc", "-Xcc=-arch", "-Xcc=x86_64", "-Xcc=-isysroot", "-Xcc=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk", ]; lib-dirs = [ "%%ldcbinarypath%%/../lib-x64", ]; rpath = "%%ldcbinarypath%%/../lib-x64"; }; |
ImportC and C cross-preprocessing
When cross-compiling and using importC (importing/compiling .c files), it's important that a matching C cross-preprocessor is used. It can be customized via -gcc and -Xcc / -P. By setting up etc/ldc2.conf for cross-linking as shown above, you should be set already.
Cross-compiling with dub
Starting with dub v1.18 (or v1.17+ bundled with official LDC v1.18 packages), you can cross-compile entire dub projects incl. their dependencies by simply adding --arch=<LDC triple>
in the dub command-line, after setting up your etc/ldc2.conf configuration file as detailed above.
Limitations
LDC doesn't have a software compile-time real for arbitrary target real
precision yet, but uses the host's real for storage and CTFE (exception: it uses 80-bit x87 compile-time precision on Windows/MSVC hosts). So if the target real features a higher precision (e.g., 32-bit ARM → x86, x86 → non-Apple AArch64), real.max
will overflow to infinity, real.min_normal
may underflow to 0, CTFE computations will be performed with lower host precision etc.