How to achieve common tasks with the new Clang toolchain in 12.1
The new Clang toolchain is here! RAD Studio and C++Builder 12.1 are shipping the entirely revised, wholly updated C++ compiler, linker, STL, runtime, and more – which you can also use in parallel with the old Win64 toolchain to ease upgrading until we remove the previous legacy toolchain in future.
We know this is a ‘version 1’ release and it’s different to our previous toolchains in some ways, and so we’ve put together this post on how to achieve specific things with the new toolchain. This covers both features that are not present yet but are coming in a future release, and things you can achieve now but it’s not necessarily obvious how. We may update this post if we have more info, too.
Table of Contents
The release and status!
We’ve been open about the work we’ve been doing and our status, which has been great and I hope we’ll continue doing! Here are the main communications about it, starting with the most recent:
- Feb 2024: a webinar, Behind the Build for 12.1 – this is the most recently technical overview and is very worth watching
The 12.1 release webinar skimmed lightly over what is and isn’t present in 12.1 because the Behind the Build went into a lot of detail. This blog post will also clarify.
- Nov 2023: released 12.0 with the Clang Preview
- Sep 2023: Behind the Build for 12.0
- March 2023: Blog, ‘What’s coming for C++Builder’ – note some technical details changed since this was posted, but you can see our goals. I recommend watching the 12.1 Behind the Build for up to date information on status in what we shipped in 12.1.
We strongly recommend watching the Feb 2024 Behind the Build for technical info about what we’ve released in 12.1, and also reading the upgrade documentation. Because the technology is different, and because this is our initial release, there are differences in features.
Common Tasks
Fast Compiling
Bcc64x does not yet have the –jobs parameter, which allows you to saturate multiple CPUs. That will come in future. We recommend using TwineCompile, which is available free with your subscription in GetIt. We worked with the developer during the beta period and it supports the new toolchain.
The new Clang and old Clang are very close in compile performance (the main difference is the linker, which is much faster in the new Clang.) Clang 15 does more than Clang 5, but despite that remains very similar in performance. Here are the results of compiling the Xerces library, in release mode, including link time. This is 300 files on a 4-core VM.
Xerces Release Build (includes link time) – 300 files, 4-core VM. Build time is in minutes:seconds | |
Old Clang Win64, without TwineCompile | 2:13 |
New Clang 64-bit Modern, without TwineCompile | 2:18 |
Old Clang Win64, with TwineCompile | 00:38 |
New Clang 64-bit Modern, with TwineCompile | 00:41 |
You get a linear decrease in compile time roughly following the number of CPU cores – here, at four cores, it’s about three and a half times faster. This follows to, say, ten to twelve times faster with sixteen cores.
Packages
In 12.1, the new Clang only supports linking packages statically. We know this is important, and dynamic linking is coming!
Most of our Delphi packages are provided (see Behind the Build at 22:16), and they are provided as .libs, and you can link them in to your EXE or DLLs. (That is, just build your projects – they will be linked in automatically because C++Builder’s headers use auto-linking.)
In general, packages are architecturally useful, and also technically useful in that multiple copies of the VCL can cause issues (eg if a DLL uses the VCL.) We know this, and dynamic package consumption is coming. We had a quality focus on what we are shipping this release, ie that what we ship is the best it can be, and so we focused on making sure static packages worked well.
C++-source packages are similarly only buildable as static libs right now, and so building BPL files for C++ packages are also planned.
CMake
CMake support for the new toolchain was not included in 12.1. The main reason was to reduce scope, to let us focus on the toolchain itself, not third party tooling.
Like packages, this is coming.
While we have some hacks we used internally to test building large CMake-only source, they are not suitable for sharing, unfortunately – please wait until we have CMake officially.
Imports
We’ve had a few bug reports around missing imports from import64.lib. We’re looking into this.
Boost
We have released Boost on GetIt for 12.1 for classic, clang Win32, and old Clang Win64; and right now we’re working on Boost for the new toolchain. Please stay tuned.
Preprocessing source and Compiling to Assembly
Currently when you preprocess or compile to assembly within the IDE, it fails. However, the command line shown in the Build Output works. The reason is that the msbuild targets to execute these commands is trying to execute the old toolchain’s DLL compiler. There are two workarounds:
- Try to preprocess (for example) in the IDE; copy the command line; change the temporary output filename to your-preferred-name.i, and run in a RAD Studio Command Prompt. The same process works for compiling to assembly.
- Edit the Codegear.Cpp.Targets file (note: make a backup first.) This lets the commands run as expected in the IDE.
[crayon-673f1ecc48640696689329/]
Creating DLL Import Libraries
To import a random DLL, you need an import library. Any existing COFF one should work with our linker (this includes import libraries made by third parties for their DLLs, intended for use with MSVC.)
However, if you need to generate your own, you first need the definition (.def) file for the DLL. To do this, you can use the gendef.exe file from LLVM-MinGW. We aren’t shipping this yet, so you need to download it. Note that the following is third party and not verified or virus-checked by us, but the official llvm-mingw site recommends Martin Storsjö’s github for release. One release is: https://github.com/mstorsjo/llvm-mingw/releases/tag/20220906
Once you’ve found a copy of gendef.exe, run:
[crayon-673f1ecc48673507307424/]
This will create file.def.
Note: some people have found that the filename in the .def file is incorrect. A .def file is plain text: you can open it and ensure it contains LIBRARY <dllname>.dll where <dllname> might be wrong: we’ve seen just “a” instead of the actual DLL name.
You can then use our new linker to generate the import library (run this at a RAD Studio Command Prompt, or run rsvars.bat in a normal command prompt):
[crayon-673f1ecc4867b315170999/]
(Or, using the LLVM tools, use llvm-dlltool.exe.)
And you now have an import library for your DLL: for file.dll, you have a file.lib import library.
Creating a DLL import library when building a DLL
Do you want to do the same thing, but not for a third-party DLL but for your own DLL? The IDE does this automatically when building, but you can do this on the command line:
[crayon-673f1ecc4867d732318741/]
Or
[crayon-673f1ecc4867f551897707/]
Both variations create the import library at the same time as the DLL.
Building a Delphi component / package for C++ Win64 Modern
When you build a package, you need it to be able to be used by the new Win64 target platform, not just the old one. If you don’t do this, then C++ Win64 Modern customers will see your component greyed out in the Palette when they have their project using the Win64 Modern target platform. (There’s also the technical building side: for Delphi, there is only one Win64, but for C++ there are two: ELF and COFF, and you need to provide the COFF static library files for your package.) So, how do you update your components?
In your package, right-click Target Platforms and add both Windows 64-bit and Windows 64-bit (Modern). If you rebuild each platform now, you will get the output files that are required – that is, bcc64x can link against the (static) package built for the Windows 64-bit Modern platform.
However, the package also needs to be installed in the IDE, and the IDE tracks which platforms a component can be used in (this is used so that a Windows-only component can’t be used in a macOS app, for example.) Now that you’ve added both Win64 platforms, rebuild the Win32 designtime package. This is the package that is installed in the IDE and contains the info about which platforms are ok for the components it contains. Now, when you install the component, and hover your mouse over it in the Palette, you will see Windows 64-bit Modern listed as a platform.
The files you’re generating are:
- Win32: <package>.lib (package as a static library) and <package>.bpi (dynamic package import library)
- For Win32, a component package should be split into two packages, designtime (for the IDE, Win32 only) and runtime (for the app, every platform, ie Win32, Win64, and Win64 Modern.)
- Win64: <package>.a (package as a static library) and <package>.bpi (dynamic package import library)
- Win64X* / Windows 64-bit Modern: <package>.lib (package as a static library — see note above re dynamic packages coming in future)
* We use the “x” naming quite often because the compiler is named bcc64x.
You’ve achieved two things:
- Provided the binary files required to link in the package when an app is built by adding the Windows 64-bit Modern platform to your runtime package
- Told the IDE that the components are usable for Windows 64-bit modern: after adding the platform to the project, by rebuilding the designtime Win32 package and reinstalling it in the IDE
Let us know how you like it!
The new toolchain is a foundational release, and means many good things for the future and what we’ll be able to do in the future. We’ve aimed to make the right decisions when building it: you can see evidence of that everywhere, from the platform conventions to choice of C runtime to 64-bit binaries to the focus on quality vs feature breadth. We’ve also worked very hard to minimise differences and ensure compatibility with your existing code. Check out our documentation for more info.
We’re looking forward to what we release using this toolchain in future, and are keen to see how well it works for you now!
Safe Harbour Statement
Any plans discussed represent our intentions as of this date, and our development plans and priorities are subject to change, due to competitive factors, availability of resources and other matters common to all independent software vendors.
Accordingly, we can’t offer any commitments or other forms of assurance that we will ultimately release any or all of the described products on the schedule or in the order described, or at all.
These general indications of development schedules or “product roadmaps” should not be interpreted or construed as any form of a commitment, and our customers’ rights to upgrades, updates, enhancements and other maintenance releases will be set forth only in the applicable software license agreement.
IMPORTANT: Features are not committed until completed and Generally Available (GA) released