Difference between revisions of "Building LDC runtime libraries"

From D Wiki
Jump to: navigation, search
m (Usage with LTO)
m (Adding common solutions to build runtime errors.)
 
(21 intermediate revisions by 2 users not shown)
Line 1: Line 1:
Starting with version 1.4, LDC ships with a small build tool to allow you to recompile the runtime standard library (and optionally the accompanying testrunners) the way you want, [https://github.com/ldc-developers/ldc/blob/master/runtime/ldc-build-runtime.d.in ldc-build-runtime].
+
Starting with version 1.4, LDC ships with a small build tool to allow you to recompile the D runtime and standard library (and optionally the accompanying testrunners) the way you want, [https://github.com/ldc-developers/ldc/blob/master/runtime/ldc-build-runtime.d.in ldc-build-runtime].
  
 
== Use cases ==
 
== Use cases ==
  
* Enabling Link-Time Optimization (LTO) for the runtime libraries by recompiling them with <tt>-flto</tt>. Linking against these may result in significant performance gains and smaller binaries.
+
* Link-Time Optimization (LTO) for the runtime libraries by recompiling with <tt>-flto</tt>, which produces smaller binaries and may show significant performance gains
* Enabling sanitizer checks via <tt>-fsanitize</tt>.
+
* Adding sanitizer checks via <tt>-fsanitize</tt>
* Cross-compilation/linking, as a set of existing druntime/Phobos libraries for the selected target platform is required to cross-compile and link D executables and shared libraries.
+
* Cross-compilation for other platforms, as the D runtime and standard library must be generated for your target platform before you can cross-compile and link D executables and shared libraries
** This scenario also includes porting LDC to a new target platform, i.e., working on the runtime libraries on an arbitrary already supported host platform while possibly patching the host LDC in parallel (ABI etc.) until the runtime libraries can be successfully cross-built to the new target and ideally all testrunners run successfully (in an emulator or natively on the target).
 
  
 
== Prerequisites ==
 
== Prerequisites ==
  
 
* [https://cmake.org/download/ CMake]
 
* [https://cmake.org/download/ CMake]
* Either [https://www.gnu.org/software/make/ Make] or [https://github.com/ninja-build/ninja/releases Ninja] (recommended, enable with <tt>--ninja</tt>)
+
* Either [https://www.gnu.org/software/make/ Make], the default, or [https://github.com/ninja-build/ninja/releases Ninja] (recommended, enable with <tt>--ninja</tt>)
 
* C toolchain (compiler, linker and libraries): gcc, clang, Microsoft Visual C++, …
 
* C toolchain (compiler, linker and libraries): gcc, clang, Microsoft Visual C++, …
* tar (can be worked around on Windows via <tt>--ldcSrcDir=/path/to/ldc-src</tt>)
 
  
 
== How it works ==
 
== How it works ==
Line 21: Line 19:
 
* Creating a build directory
 
* Creating a build directory
 
* Downloading & extracting the LDC source archive matching the LDC version
 
* Downloading & extracting the LDC source archive matching the LDC version
* Invoking CMake to generate the Makefile for the runtime libraries
+
* Invoking CMake to generate the Make or Ninja build script for the runtime libraries
* Invoking Make/Ninja to build the runtime libraries
+
* Calling Make or Ninja to build the runtime libraries
  
== Usage ==
+
The runtime libraries end up in the <tt>lib</tt> subdirectory of the build directory, i.e. <tt>./ldc-build-runtime.tmp/lib</tt> by default. You can replace your existing LDC runtime libraries or link against the new libraries by specifying their directory in the LDC command line: <tt>ldc2 … -L-L/path/to/ldc-build-runtime.tmp/lib</tt>.
 +
 
 +
== Basic usage ==
  
 
The primary aim is to allow specifying additional compiler/linker command-line options and customizing [https://github.com/ldc-developers/ldc/blob/master/runtime/CMakeLists.txt CMake variables].<br>Run <tt>ldc-build-runtime -h</tt> for the full list of command-line options.
 
The primary aim is to allow specifying additional compiler/linker command-line options and customizing [https://github.com/ldc-developers/ldc/blob/master/runtime/CMakeLists.txt CMake variables].<br>Run <tt>ldc-build-runtime -h</tt> for the full list of command-line options.
 
Basic usage:
 
  
 
<pre>
 
<pre>
ldc-build-runtime -j4 [--testrunners] --dFlags=… --cFlags=… --linkerFlags=… CMAKE_VAR1=value1 CMAKE_VAR2=value2 …
+
ldc-build-runtime [--ninja] [-j4] [--testrunners] [--dFlags=…] [--cFlags=…] [--linkerFlags=…] [CMAKE_VAR1=value1] [CMAKE_VAR2=value2 …]
 
</pre>
 
</pre>
  
Basic usage for cross-compilation:
+
For example, to prepare for link-time optimization between your user code and the static runtime libraries, you can recompile the runtime libraries with:
  
 
<pre>
 
<pre>
CC=cross-gcc ldc-build-runtime -j4 --dFlags="-mtriple=…;…" --cFlags=… --linkerFlags=… TARGET_SYSTEM=… …
+
ldc-build-runtime --ninja --dFlags="-flto=thin" BUILD_SHARED_LIBS=OFF
 
</pre>
 
</pre>
  
Here's an actual command used to cross-compile the standard library and test runners for Android/ARM, by using the Android NDK.  First, you set the path to your NDK and cross-compiler, then you run this ldc-build-runtime command, all one command which has been split up for legibility:
+
== Solution for common problems ==
<pre>
+
 
export NDK=/path/to/your/android-ndk-r15c
+
<li>Cannot check C compiler(CMakeTestCCompiler): Add '''CMAKE_C_COMPILER_WORKS=1''' and it will skip the C compiler check. </li>
export CC=$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/clang
+
 
 +
== Usage for cross-compilation ==
 +
 
 +
<syntaxhighlight lang=bash>
 +
CC=cross-gcc ldc-build-runtime [--ninja] [-j4] [--testrunners] --dFlags="-mtriple=…;…" [--cFlags=…] [--linkerFlags=…] --targetSystem=… …
 +
</syntaxhighlight>
 +
 
 +
For example, to cross-compile from linux/x64 with glibc to an Alpine linux/x64 container just requires specifying a C cross-compiler and a different cross-compilation triple (install the clang and musl packages in Arch linux to try this out):
 +
 
 +
<syntaxhighlight lang=bash>
 +
CC=musl-clang ldc-build-runtime --dFlags="-mtriple=x86_64-alpine-linux-musl"
 +
</syntaxhighlight>
 +
 
 +
From Linux/x86_64 to Linux/ARM:
 +
 
 +
<syntaxhighlight lang=bash>
 +
CC=arm-linux-gnueabihf-gcc ldc-build-runtime --ninja --dFlags="-mtriple=arm-linux-gnueabihf"
 +
</syntaxhighlight>
 +
 
 +
or to Linux/AArch64, ie 64-bit ARM (here using the <tt>aarch64-linux-gnu-gcc</tt> package in Arch linux):
 +
 
 +
<syntaxhighlight lang=bash>
 +
CC=aarch64-linux-gnu-gcc ldc-build-runtime --ninja --dFlags="-mtriple=aarch64-none-linux-gnu"
 +
</syntaxhighlight>
  
ldc-build-runtime BUILD_SHARED_LIBS=OFF C_SYSTEM_LIBS="m;c" TARGET_SYSTEM="Android;Linux;UNIX"
+
or to Linux/MIPS32:
  
--cFlags="-gcc-toolchain;$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64;-fpic;-ffunction-sections;-funwind-tables;-fstack-protector-strong;-Wno-invalid-command-line-argument;-Wno-unused-command-line-argument;-no-canonical-prefixes;-fno-integrated-as;-target;armv7-none-linux-androideabi;-march=armv7-a;-mfloat-abi=softfp;-mfpu=vfpv3-d16;-mthumb;-Os;-g;-DNDEBUG;-fomit-frame-pointer;-fno-strict-aliasing;-DANDROID;-Wa,--noexecstack;-Wformat;-Werror=format-security;-isystem;$NDK/platforms/android-21/arch-arm/usr/include"
+
<syntaxhighlight lang=bash>
 +
CC=mipsel-linux-gcc ldc-build-runtime --ninja --dFlags="-mtriple=mipsel-linux;-mcpu=mips32" --cFlags="-march=mips32"
 +
</syntaxhighlight>
  
--linkerFlags="-Wl,-z,nocopyreloc;--sysroot=$NDK/platforms/android-21/arch-arm;-lgcc;-gcc-toolchain;$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64;-target;armv7-none-linux-androideabi;-no-canonical-prefixes;-fuse-ld=bfd;-Wl,--fix-cortex-a8;-Wl,--no-undefined;-Wl,-z,noexecstack;-Wl,-z,relro;-Wl,-z,now;-Wl,--warn-shared-textrel;-Wl,--fatal-warnings;-fPIE;-pie"
+
From Windows to Linux/ARM, e.g., by using an official [http://gnutoolchains.com/raspberry/ Raspberry PI toolchain]:
  
--dFlags="-w;-mtriple=armv7-none-linux-android" --ninja --testrunners
+
<syntaxhighlight lang=bash>
</pre>
+
set CC=arm-linux-gnueabihf-gcc
 +
ldc-build-runtime --ninja --dFlags=-mtriple=arm-linux-gnueabihf --targetSystem=Linux;UNIX CMAKE_SYSTEM_NAME=Linux CMAKE_C_COMPILER_WORKS=True BUILD_SHARED_LIBS=OFF
 +
</syntaxhighlight>
  
If you built LDC yourself from a non-tagged source, you will most likely get an error when trying to download the LDC source archive. In that case, use the <tt>--ldcSrcDir=/path/to/ldc-src</tt> option to reuse your source tree.
+
From Linux, Mac or Windows to Android/ARMv7, assuming the [https://developer.android.com/ndk/downloads Android NDK] r21 has been unzipped into <tt>/home/me</tt>:
  
 +
<syntaxhighlight lang=bash>
 +
ldc-build-runtime --ninja \
 +
                  --dFlags="-mtriple=armv7a--linux-androideabi" \
 +
                  --targetSystem="Android;Linux;UNIX" \
 +
                  CMAKE_TOOLCHAIN_FILE="/home/me/android-ndk-r21/build/cmake/android.toolchain.cmake" \
 +
                  ANDROID_ABI=armeabi-v7a \
 +
                  ANDROID_NATIVE_API_LEVEL=21
 +
</syntaxhighlight>
  
== Usage with LTO ==
+
Check out the official [https://developer.android.com/ndk/guides/cmake Android NDK CMake guide] for possible values of the <tt>ANDROID_*</tt> variables, e.g., to target other architectures (and make sure to adapt the <tt>-mtriple</tt> flag in that case). For full instructions on Android cross-compilation, [[Build D for Android|see the Android page]]. It also shows examples of using ldc with the newly generated runtime libraries to compile your own code.
(very brief documentation that should be improved and expanded)
 
  
It is likely that your system's ranlib+ar cannot handle the LTO object files that LDC produces. To solve this, you will have to direct CMake to the LLVM tools that _can_ handle them. E.g.
+
From macOS/x64 to macOS/arm64:
<pre>
 
./ldc-build-runtime --dFlags='-flto=full' --cFlags='-flto=full' BUILD_SHARED_LIBS=OFF LD_FLAGS="-lto_library=/Users/johan/ldc/johan/build50/lib/libLTO-ldc.dylib" --ninja CMAKE_AR=/Users/johan/llvm/llvm50/install/bin/llvm-ar CMAKE_RANLIB=/Users/johan/llvm/llvm50/install/bin/llvm-ranlib
 
</pre>
 
This issue should be fixed after LDC is used to link the runtime, see https://github.com/ldc-developers/ldc/issues/2309.
 
  
OSX only: another issue is that a ThinLTO version of the standard library will result in linker errors due to this issue: https://github.com/ldc-developers/ldc/issues/2312 . (this is not an issue if gold or lld is used)
+
<syntaxhighlight lang=bash>
 +
ldc-build-runtime --ninja \
 +
                  --dFlags="-mtriple=arm64-apple-macos" \
 +
                  CMAKE_OSX_ARCHITECTURES=arm64 \
 +
                  CMAKE_OSX_DEPLOYMENT_TARGET=11.0
 +
</syntaxhighlight>
  
----
+
or to iOS/arm64:
  
 +
<syntaxhighlight lang=bash>
 +
ldc-build-runtime --ninja \
 +
                  --dFlags="-mtriple=arm64-apple-ios12.0;-fvisibility=hidden" \
 +
                  CMAKE_SYSTEM_NAME=iOS \
 +
                  CMAKE_OSX_ARCHITECTURES=arm64 \
 +
                  CMAKE_OSX_DEPLOYMENT_TARGET=12.0 \
 +
                  BUILD_SHARED_LIBS=OFF
 +
</syntaxhighlight>
 +
 
[[Category:LDC]]
 
[[Category:LDC]]

Latest revision as of 20:29, 3 February 2023

Starting with version 1.4, LDC ships with a small build tool to allow you to recompile the D runtime and standard library (and optionally the accompanying testrunners) the way you want, ldc-build-runtime.

Use cases

  • Link-Time Optimization (LTO) for the runtime libraries by recompiling with -flto, which produces smaller binaries and may show significant performance gains
  • Adding sanitizer checks via -fsanitize
  • Cross-compilation for other platforms, as the D runtime and standard library must be generated for your target platform before you can cross-compile and link D executables and shared libraries

Prerequisites

  • CMake
  • Either Make, the default, or Ninja (recommended, enable with --ninja)
  • C toolchain (compiler, linker and libraries): gcc, clang, Microsoft Visual C++, …

How it works

If run without special command-line options, ldc-build-runtime automates:

  • Creating a build directory
  • Downloading & extracting the LDC source archive matching the LDC version
  • Invoking CMake to generate the Make or Ninja build script for the runtime libraries
  • Calling Make or Ninja to build the runtime libraries

The runtime libraries end up in the lib subdirectory of the build directory, i.e. ./ldc-build-runtime.tmp/lib by default. You can replace your existing LDC runtime libraries or link against the new libraries by specifying their directory in the LDC command line: ldc2 … -L-L/path/to/ldc-build-runtime.tmp/lib.

Basic usage

The primary aim is to allow specifying additional compiler/linker command-line options and customizing CMake variables.
Run ldc-build-runtime -h for the full list of command-line options.

ldc-build-runtime [--ninja] [-j4] [--testrunners] [--dFlags=…] [--cFlags=…] [--linkerFlags=…] [CMAKE_VAR1=value1] [CMAKE_VAR2=value2 …]

For example, to prepare for link-time optimization between your user code and the static runtime libraries, you can recompile the runtime libraries with:

ldc-build-runtime --ninja --dFlags="-flto=thin" BUILD_SHARED_LIBS=OFF

Solution for common problems

  • Cannot check C compiler(CMakeTestCCompiler): Add CMAKE_C_COMPILER_WORKS=1 and it will skip the C compiler check.
  • Usage for cross-compilation

    CC=cross-gcc ldc-build-runtime [--ninja] [-j4] [--testrunners] --dFlags="-mtriple=…;…" [--cFlags=] [--linkerFlags=] --targetSystem=… …
    

    For example, to cross-compile from linux/x64 with glibc to an Alpine linux/x64 container just requires specifying a C cross-compiler and a different cross-compilation triple (install the clang and musl packages in Arch linux to try this out):

    CC=musl-clang ldc-build-runtime --dFlags="-mtriple=x86_64-alpine-linux-musl"
    

    From Linux/x86_64 to Linux/ARM:

    CC=arm-linux-gnueabihf-gcc ldc-build-runtime --ninja --dFlags="-mtriple=arm-linux-gnueabihf"
    

    or to Linux/AArch64, ie 64-bit ARM (here using the aarch64-linux-gnu-gcc package in Arch linux):

    CC=aarch64-linux-gnu-gcc ldc-build-runtime --ninja --dFlags="-mtriple=aarch64-none-linux-gnu"
    

    or to Linux/MIPS32:

    CC=mipsel-linux-gcc ldc-build-runtime --ninja --dFlags="-mtriple=mipsel-linux;-mcpu=mips32" --cFlags="-march=mips32"
    

    From Windows to Linux/ARM, e.g., by using an official Raspberry PI toolchain:

    set CC=arm-linux-gnueabihf-gcc
    ldc-build-runtime --ninja --dFlags=-mtriple=arm-linux-gnueabihf --targetSystem=Linux;UNIX CMAKE_SYSTEM_NAME=Linux CMAKE_C_COMPILER_WORKS=True BUILD_SHARED_LIBS=OFF
    

    From Linux, Mac or Windows to Android/ARMv7, assuming the Android NDK r21 has been unzipped into /home/me:

    ldc-build-runtime --ninja \
                      --dFlags="-mtriple=armv7a--linux-androideabi" \
                      --targetSystem="Android;Linux;UNIX" \
                      CMAKE_TOOLCHAIN_FILE="/home/me/android-ndk-r21/build/cmake/android.toolchain.cmake" \
                      ANDROID_ABI=armeabi-v7a \
                      ANDROID_NATIVE_API_LEVEL=21
    

    Check out the official Android NDK CMake guide for possible values of the ANDROID_* variables, e.g., to target other architectures (and make sure to adapt the -mtriple flag in that case). For full instructions on Android cross-compilation, see the Android page. It also shows examples of using ldc with the newly generated runtime libraries to compile your own code.

    From macOS/x64 to macOS/arm64:

    ldc-build-runtime --ninja \
                      --dFlags="-mtriple=arm64-apple-macos" \
                      CMAKE_OSX_ARCHITECTURES=arm64 \
                      CMAKE_OSX_DEPLOYMENT_TARGET=11.0
    

    or to iOS/arm64:

    ldc-build-runtime --ninja \
                      --dFlags="-mtriple=arm64-apple-ios12.0;-fvisibility=hidden" \
                      CMAKE_SYSTEM_NAME=iOS \
                      CMAKE_OSX_ARCHITECTURES=arm64 \
                      CMAKE_OSX_DEPLOYMENT_TARGET=12.0 \
                      BUILD_SHARED_LIBS=OFF