Difference between revisions of "Cross-compiling with LDC"

From D Wiki
Jump to: navigation, search
m (Tweaking the LDC configuration file: Fix Android triple)
(Revise a bit)
Line 8: Line 8:
  
 
* <code>x86_64-linux-gnu</code>: Linux x86_64 with glibc
 
* <code>x86_64-linux-gnu</code>: Linux x86_64 with glibc
* <code>x86_64-apple-darwin16.7.0</code>: macOS
+
* <code>x86_64-apple-darwin</code>: macOS
 
* <code>x86_64-windows-msvc</code>: Windows x64
 
* <code>x86_64-windows-msvc</code>: Windows x64
 
* <code>i686-linux-musl</code>: Linux x86 with musl
 
* <code>i686-linux-musl</code>: Linux x86 with musl
Line 14: Line 14:
 
* <code>armv6-linux-gnueabihf</code>: Linux ARMv6 with glibc and hard-float ABI
 
* <code>armv6-linux-gnueabihf</code>: Linux ARMv6 with glibc and hard-float ABI
 
* <code>armv7a-unknown-linux-androideabi</code>: Android ARMv7-A
 
* <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>aarch64-linux-gnu</code>: Linux AArch64 with glibc
 
* <code>wasm32-unknown-unknown-webassembly</code>: 32-bit WebAssembly
 
* <code>wasm32-unknown-unknown-webassembly</code>: 32-bit WebAssembly
  
 
Run <code>ldc2 -version</code> to check your default/host triple (<tt>Default target</tt>).
 
Run <code>ldc2 -version</code> to check your default/host triple (<tt>Default target</tt>).
 
== Generating LLVM IR ==
 
 
Generating textual LLVM IR (<code>-output-ll</code>) and LLVM bitcode (<code>-output-bc</code>) works for all supported targets, on all hosts.
 
 
* <code>ldc2 -mtriple=x86_64-apple-darwin16.7.0 -output-ll foo.d</code>: generates a <tt>foo.ll</tt> text file with the textual LLVM IR for macOS
 
  
 
== Cross-compiling ==
 
== Cross-compiling ==
  
Cross-compiling object files (<code>-c</code>) and textual assembly files (<code>-output-s</code>) works for all targets supported by the LLVM which was linked against LDC, which depends on the LLVM CMake build configuration (enabled target architectures).
+
The supported targets depend on how the LLVM linked against your LDC was built (target architectures enabled in the CMake build command-line).
 
Running <code>ldc2 -version</code> shows the list of enabled/registered LLVM backends.
 
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-windows-msvc -c foo.d</code>: generates a <tt>foo.obj</tt> COFF object file for Win64
 +
* <code>ldc2 -mtriple=x86_64-apple-darwin -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 ==
Line 57: Line 54:
 
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 63: Line 60:
 
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.
  
==== Non-Apple POSIX targets ====
+
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.
 
 
The preferred way is to install a gcc toolchain for cross-compilation, e.g., the <tt>gcc-aarch64-linux-gnu</tt> package on Debian/Ubuntu hosts when targeting Linux/AArch64. It includes the C libraries as well as a cross-linker (<tt>aarch64-linux-gnu-gcc</tt>) configured for those libs and startup object files etc.
 
 
 
=== Cross-linker ===
 
 
 
==== Windows targets ====
 
 
 
For Windows targets, LLVM's LLD works nicely as cross-linker on all hosts. Official prebuilt LDC packages feature an integrated LLD, which can be enabled via <code>-link-internally</code>. Usually, you don't need to use that command-line option explicitly; LDC should default to it automatically on non-Windows hosts.
 
  
 
==== Non-Apple POSIX targets ====
 
==== Non-Apple POSIX targets ====
  
The preferred way is to use the cross-gcc installed in the previous step as linker driver, as it is preconfigured for the accompanying C runtime libs and object files.
+
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.
  
* Option 1: set the <tt>CC</tt> environment variable, e.g., <code>CC=aarch64-linux-gnu-gcc ldc2 …</code>
+
=== Tweaking the LDC configuration file ===
* Option 2: use LDC's <code>-gcc</code> switch, e.g., <code>ldc2 -gcc=aarch64-linux-gnu-gcc …</code>
 
  
=== 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.
  
This is the last step. :) 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 section for a Win64 target, assuming the <tt>lib</tt> directory from the prebuilt LDC Win64 package was copied to <tt><LDC root>/lib-win64</tt>:
+
* Exemplary section for a Win64 target, assuming the <tt>lib</tt> directory from the prebuilt LDC windows-x64 package was copied to <tt><LDC root>/lib-win64</tt>:
 
<pre>
 
<pre>
 
"x86_64-.*-windows-msvc":
 
"x86_64-.*-windows-msvc":
Line 98: Line 86:
 
</pre>
 
</pre>
  
* Exemplary section for a Linux/AArch64 target, assuming the <tt>lib</tt> directory from the prebuilt LDC AArch64 package was copied to <tt><LDC root>/lib-aarch64</tt> and the cross-gcc binary is found via <tt>aarch64-linux-gnu-gcc</tt>:
+
* Exemplary section for a Linux AArch64 target, assuming the <tt>lib</tt> directory from the prebuilt LDC linux-aarch64 package was copied to <tt><LDC root>/lib-aarch64</tt> and the cross-gcc executable is found via <tt>aarch64-linux-gnu-gcc</tt>:
 
<pre>
 
<pre>
 
"aarch64-.*-linux-gnu":
 
"aarch64-.*-linux-gnu":
Line 113: Line 101:
 
</pre>
 
</pre>
  
* Exemplary section for an Android/ARMv7-A target on a Linux x64 host, assuming the <tt>lib</tt> directory from the prebuilt LDC armv7a package was copied to <tt><LDC root>/lib-armv7a</tt> and the Android NDK r20 has been unzipped into <tt>/home/me</tt>:
+
* Exemplary section for an Android ARMv7-A target on a Linux x64 host, assuming the <tt>lib</tt> directory from the prebuilt LDC android-armv7a package was copied to <tt><LDC root>/lib-armv7a</tt> and the Android NDK r20b has been unzipped into <tt>/home/me</tt>:
 
<pre>
 
<pre>
 
"armv7a-.*-linux-android":
 
"armv7a-.*-linux-android":
Line 120: Line 108:
 
         "-defaultlib=phobos2-ldc,druntime-ldc",
 
         "-defaultlib=phobos2-ldc,druntime-ldc",
 
         "-link-defaultlib-shared=false",
 
         "-link-defaultlib-shared=false",
         "-gcc=/home/me/android-ndk-r20/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi21-clang",
+
         "-gcc=/home/me/android-ndk-r20b/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi21-clang",
 
         "-linker=bfd",
 
         "-linker=bfd",
 
         "-mcpu=cortex-a8",
 
         "-mcpu=cortex-a8",
Line 127: Line 115:
 
         "%%ldcbinarypath%%/../lib-armv7a",
 
         "%%ldcbinarypath%%/../lib-armv7a",
 
     ];
 
     ];
 +
    rpath = "";
 
};
 
};
 
</pre>
 
</pre>
  
These settings will be used automatically when specifying a matching target triple, so that <code>ldc2 -mtriple=x86_64-windows-msvc foo.d</code> is enough to generate a <tt>foo.exe</tt> Win64 executable.
+
* Exemplary section for an Android AArch64 target on a Linux x64 host, assuming the <tt>lib</tt> directory from the prebuilt LDC android-aarch64 package was copied to <tt><LDC root>/lib-aarch64</tt> and the Android NDK r20b has been unzipped into <tt>/home/me</tt>:
 +
<pre>
 +
"aarch64-.*-linux-android":
 +
{
 +
    switches = [
 +
        "-defaultlib=phobos2-ldc,druntime-ldc",
 +
        "-link-defaultlib-shared=false",
 +
        "-gcc=/home/me/android-ndk-r20b/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang",
 +
        "-linker=bfd",
 +
    ];
 +
    lib-dirs = [
 +
        "%%ldcbinarypath%%/../lib-aarch64",
 +
    ];
 +
    rpath = "";
 +
};
 +
</pre>
  
 
== Cross-compiling with dub ==
 
== Cross-compiling with dub ==

Revision as of 14:31, 20 December 2019

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 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 glibc
  • x86_64-apple-darwin: macOS
  • x86_64-windows-msvc: Windows x64
  • i686-linux-musl: Linux x86 with musl
  • i686-windows-msvc: Windows x86
  • armv6-linux-gnueabihf: Linux ARMv6 with glibc and hard-float ABI
  • armv7a-unknown-linux-androideabi: Android ARMv7-A
  • aarch64-linux-android: Android AArch64
  • aarch64-linux-gnu: Linux AArch64 with glibc
  • wasm32-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 Win64
  • ldc2 -mtriple=x86_64-apple-darwin -c foo.d: generates a foo.o Mach-O object file for macOS
  • ldc2 -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 archive
  • ldc2 -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 executables and shared libraries

Cross-linking requires more work, as we have these requirements:

  1. Default libraries (e.g., druntime and Phobos) for the target, as (non-betterC) binaries are linked against these default libraries.
  2. 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.
  3. A cross-linker.

Default libraries

If there's a prebuilt LDC package for your desired target, then the simplest variant is to download it 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.

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 section for a Win64 target, 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",
        "-link-defaultlib-shared=false",
    ];
    lib-dirs = [
        "%%ldcbinarypath%%/../lib-win64",
    ];
};
  • Exemplary section for a Linux AArch64 target, 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";
};
  • Exemplary section for an Android ARMv7-A target on a Linux x64 host, assuming the lib directory from the prebuilt LDC android-armv7a package was copied to <LDC root>/lib-armv7a and the Android NDK r20b has been unzipped into /home/me:
"armv7a-.*-linux-android":
{
    switches = [
        "-defaultlib=phobos2-ldc,druntime-ldc",
        "-link-defaultlib-shared=false",
        "-gcc=/home/me/android-ndk-r20b/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi21-clang",
        "-linker=bfd",
        "-mcpu=cortex-a8",
    ];
    lib-dirs = [
        "%%ldcbinarypath%%/../lib-armv7a",
    ];
    rpath = "";
};
  • Exemplary section for an Android AArch64 target on a Linux x64 host, assuming the lib directory from the prebuilt LDC android-aarch64 package was copied to <LDC root>/lib-aarch64 and the Android NDK r20b has been unzipped into /home/me:
"aarch64-.*-linux-android":
{
    switches = [
        "-defaultlib=phobos2-ldc,druntime-ldc",
        "-link-defaultlib-shared=false",
        "-gcc=/home/me/android-ndk-r20b/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang",
        "-linker=bfd",
    ];
    lib-dirs = [
        "%%ldcbinarypath%%/../lib-aarch64",
    ];
    rpath = "";
};

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 → AArch64), real.max will overflow to infinity, real.min_normal may underflow to 0, CTFE computations will be performed with lower host precision etc.