新しいClangツールチェーンが登場しました!
エンバカデロでは、以前こちらのブログでもご紹介させていただきましたが「doing it right(正しいことをする)」を目標としてWin64コンパイラを再設計いたしました。C++Builder 12.1では、完全に改訂され、全面的に更新されたC++コンパイラ、リンカ、STL、ランタイムなどをリリースしています。
Table of Contents
C++Builder 12.1の新しいClangツールチェーンとは?
C++Builder 12.1の新しいClangツールチェーンの特徴は、以下の通りです。
- Clang 15ベースのWindows 64ビットプラットフォーム向けのコンパイラセット
- 生成されるオブジェクトファイルは、Windowsプラットフォーム標準のCOFF形式
- 生成されるデバッグファイルは、Windowsプラットフォーム標準のPDB形式(必要であれば、WinDBGなどの他のデバッガーも使用可能)。
- 新しいリンカは、従来のilinkと比較して約4倍も高速で、メモリ不足も解消
- 他の多くのオープンソースが利用している高品質なLLVMのSTL(libc++)を利用できる
- 古いClangツールチェーンでは、独自のCランタイムを使用していましたが、新しいClangツールチェーンでは、Windows OSに組み込まれた標準的なUCRT(ユニバーサルCランタイム)を使用
つまり、新しいClangツールチェーンを使用することで、従来のC++Builder(古いClangツールチェーン)には存在していた制約に捕らわれることなく、大量のメモリ要件に対応、世間で普及している多くのオープンソースライブラリやSTLの利用、そしてWindowsプラットフォーム標準という互換性を遺憾なく享受することが出来ます。
なお、12.1でリリースされた新しいClangツールチェーンは、「バージョン1」の段階であり、古いWin64ツールチェーンとは異なる点が存在いたします。また12.1のC++ツールチェーンで実装されている機能、あるいは実装できていない機能があります。
12.1 でリリースされた内容に関する技術情報については、2024 年 2 月の「Behind the Build for 12.1」をご覧いただき、新しいWin64ツールチェーンのアップグレードに関するドキュメントも併せてご覧いただくことを強くお勧めします。
古いClangツールチェーンはどうなるの?
将来的に古いWin64ツールチェーンを廃止するまでの間、アップグレードを容易にするために、新しいWin64ツールチェーンと並行して使用することができますので、その点はどうかご安心ください。
C++Builder 12.1の新しいClangツールチェーンの詳細
このブログは、C++BuilderのプロダクトマネージャであるDavid Millingtonのブログを抜粋した日本語の抄訳となります。
詳しくは、以下のブログ(英語)をご参照ください。
- 「The new Clang Toolchain in C++Builder 12.1!」
- 「How to achieve common tasks with the new Clang toolchain in 12.1」
高速なコンパイル
Bcc64xには、並列コンパイラを有効にする -jobs パラメータがまだ実装されていませんが、将来的に実装する予定です。現時点ではTwineCompileを使用することをお勧めします。TwineCompileは有効なアップデートサブスクリプションをお持ちのお客様は、GetIt から無料でダウンロードいただけます。エンバカデロではベータ期間中にTwineCompileの開発者と協力し、新しいツールチェーンをサポートしています。
新しい Clang と古い Clang のコンパイル 速度はほとんど変わりませんが、リンカの速度は、新しい Clang の方がはるかに高速です。Clang 15 は Clang 5 より多くの機能を備えていますが、パフォーマンス面では大きなボトルネックは無く、それほど変わりません。 以下は、リンク時間を含むリリース モードにて4コア VM 上で Xercesライブラリ(300 ファイル)をビルドした時間 です。
Xercesリリースビルド(リンク時間を含む) – 4コア VM 上で 300 ファイルをビルドした時間 単位は 分:秒 | |
古いClang Win64 (TwineCompile 利用無) | 2:13 |
新しいClang Win64(モダン) TwineCompile 利用無 | 2:18 |
古いClang Win64 TwineCompile 利用有 | 00:38 |
新しいClang Win64(モダン) TwineCompile 利用有 | 00:41 |
CPUコアの数にほぼ比例してコンパイル時間が直線的に短縮しています。今回の検証では4コアを使用していますが、約 3.5 倍高速になります。例えば、16コアでは10倍から12倍速ぐらいになると予想されます。
パッケージ
12.1の新しいWin64 Clangでは、パッケージを静的にリンクする方法のみサポートしています。エンバカデロでは、ダイナミックリンクが必要であることも理解しております。
エンバカデロのDelphiパッケージのほとんどは提供されており(「Behind the Build for 12.1」の22:16を参照)、Delphiパッケージの.libとして提供されているので、EXEやDLLにリンクすることができます。(つまり、プロジェクトをビルドするだけです。C++Builderのヘッダーには自動リンク機能があるため、プロジェクトは自動的にリンクされます。)
一般に、パッケージはアーキテクチャ的に便利ですが、VCL の複数のコピーが問題を引き起こす可能性があるという点で技術的にも役立ちます (たとえば、DLL が VCL を使用する場合等)。まず12.1では、パッケージのスタティックリンクが適切に動作することを重点に置きました。将来的にはパッケージのダイナミックリンクもサポートする予定です。またC++ソースパッケージも同様に、現時点ではスタティックライブラリとしてのみビルド可能ですが、C++パッケージ向けのBPLファイルのビルドも計画されています。
CMake
12.1では、CMakeは新しいC++Win64ツールチェーンはサポートしておりません。その理由ですが、まず優先順位として新しいツールチェイン自体に焦点を当ててサポートに集中し、パッケージでの説明と同様にサードパーティのツールも順次に対応していく計画です。CMakeを正式にサポートするまでは、もうしばらくお待ちください。
Imports
import64.libからのインポートが見つからないというバグが何点か報告されており、現在調査中です。
Boost
12.1では、従来のBorlandコンパイラ(bcc32)、Clang Win32(bcc32c)、そして古いClang Win64 (bcc64)に対応したBoostは、GetIt経由でダウンロードいただけます。現在、新しいツールチェーン向けのBoostに取り組んでいます。どうかご期待ください。
ソースの前処理とアセンブリへのコンパイル
現在、C++BuilderのIDE内でプリプロセスやアセンブリへのコンパイルを行うと失敗します。しかし、ビルド出力に表示されるコマンドラインは動作します。その理由は、これらのコマンドを実行する msbuild ターゲットが古いツールチェーンの DLL コンパイラを実行しようとしているためです。回避策は2つあります。
- たとえば、IDE でプリプロセスを実行して、コマンドラインをコピーし、一時出力ファイル名を your-preferred-name.i に変更し、RAD Studio コマンド プロンプトで実行します。アセンブリにコンパイルする場合も同じ手順です。
- Codegear.Cpp.Targetsファイルを編集してください。(注釈:最初に必ず当該ファイルのバックアップを取ってください)。これにより、IDEでコマンドが期待通りに実行されるようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
<Target Name="__CLANGPreprocessCreate" DependsOnTargets="$(PreprocessDependsOnTargets);"> <PropertyGroup> <CLANG_RunPreprocessor>true</CLANG_RunPreprocessor> <CLANG_OutputFileName>$(PreprocessOutputFilename)</CLANG_OutputFileName> <CLANG_EmitNativeObject>false</CLANG_EmitNativeObject> <!-- Workaround for RS-68975. We must use cpp tools intead --> <!-- !!! CHANGED --> <RunBCCOutOfProcess Condition="'$(Platform)'=='$(cWin64xPlatform)'">true</RunBCCOutOfProcess> <RunBCCOutOfProcess Condition="'$(Platform)'!='$(cWin64xPlatform)'">false</RunBCCOutOfProcess> </PropertyGroup> <ItemGroup> <CppFiles Include="@(InputFile)"/> </ItemGroup> </Target> <Target Name="CLANGPreprocess" DependsOnTargets="__CLANGPreprocessCreate;_CLANGCoreCompile"/> <Target Name="__CLANGToAssemblyCreate" DependsOnTargets="$(ToAssemblyDependsOnTargets);"> <PropertyGroup> <CLANG_EmitNativeAssembly>true</CLANG_EmitNativeAssembly> <CLANG_OutputFileName>$(AssemblyOutputFilename)</CLANG_OutputFileName> <CLANG_EmitNativeObject>false</CLANG_EmitNativeObject> <!-- !!! ADDED --> <RunBCCOutOfProcess Condition="'$(Platform)'=='$(cWin64xPlatform)'">true</RunBCCOutOfProcess> <RunBCCOutOfProcess Condition="'$(Platform)'!='$(cWin64xPlatform)'">false</RunBCCOutOfProcess> </PropertyGroup> <ItemGroup> <CppFiles Include="@(InputFile)"/> </ItemGroup> </Target> |
DLLインポートライブラリの作成
ランダムなDLLをインポートするには、インポートライブラリが必要です。既存のCOFFライブラリであれば、リンカで動作するはずです (これには、MSVC での使用を目的として、サードパーティが DLL向けに作成したインポート ライブラリが含まれます)。
ただし、独自の DLL を作成する必要がある場合は、まずDLLの定義ファイル(.def)が必要です。これを行うには、LLVM-MinGWのgendef.exeファイルを使用します。これはまだ出荷されていませんので、ダウンロードする必要があります。 以下はサードパーティのものであり、エンバカデロによる検証やウイルスチェックは行われていないことに注意してください。しかし、llvm-mingw公式サイトではMartin Storsjö氏のgithubでのリリースを推奨しています。
推奨されているリリースは、以下の通りです。
https://github.com/mstorsjo/llvm-mingw/releases/tag/20220906
上記のサイトから.zipやtar.gzなどアーカイブをダウンロードし、展開してください。
例えば、llvm-mingw-20220906-msvcrt-x86_64.zipを展開すると、binフォルダ内にgendef.exeが見つかります。
file.defを作成するには、gendef.exeを実行してください。
1 |
gendef file.dll |
注意: .defファイルのファイル名が正しくないことに気づいた人もいます。.defファイルはプレーンテキストです。ファイルを開いて、LIBRARY <dllname>.dllが含まれていることを確認できます。<dllname> が間違っている可能性があります。実際の DLL 名の代わりに「a」だけが表示されています。
その後、新しいリンカを使用してインポートライブラリを生成できます(RAD Studioのコマンドプロンプトでこれを実行するか、通常のコマンドプロンプトでrsvars.batを実行します)。
1 |
ld.lld.exe -m i386pep --out-implib file.lib file.def |
(または、LLVMツールを使用する場合は、llvm-dlltool.exeを使用する)。
これでDLL のインポート ライブラリが作成されました。file.dll の場合は、file.lib インポート ライブラリとして作成されます。
DLLビルド時のDLLインポートライブラリの作成
同様の方法をサードパーティ DLL ではなく、お客様自身の DLL に対して実行したいですか? IDEはビルド時に自動的にこれを行いますが、コマンドラインで行うこともできます。
1 |
bcc64x -tD -Xlinker --out-implib=dll.lib dll.cpp |
もしくは
1 |
bcc64x -tD dll.cpp -Wl,--out-implib,dll.lib |
どちらの方法でも、DLLと一緒にインポートライブラリが作成されます。
C++ Win64 モダン向けのDelphiコンポーネント/パッケージのビルド
パッケージをビルドする際には、古いWin64 ターゲット プラットフォームだけでなく、新しいWin64ターゲットプラットフォームでも使用可能にする必要があります。
もしC++ Win64(モダン)向けにビルドを行わないと、プロジェクトがWin64(モダン)ターゲットプラットフォームを使用しているときに、パレットでコンポーネントがグレーアウトされます。
実は、これには技術的なビルドの側面もあります。Delphiの場合、Win64 は 1 つだけですが、C++の場合は新旧併せてWin64は2つあり、バイナリフォーマットもそれぞれELFとCOFFというように形式が異なります。C++ Win64(モダン) 向けのパッケージをビルドするためには、COFFスタティックライブラリファイルを提供する必要があります。
では、どのようにコンポーネントを更新するのでしょうか?
パッケージで[ターゲットプラットフォーム]を選択し、マウスを右クリックして[プラットフォームの追加]からWindows 64ビットとWindows 64ビット (モダン)の両方を追加します。つまり、bcc64xはWindows 64ビット (モダン)プラットフォーム向けにビルドされた(静的な)パッケージに対してリンクすることができます。
ただし、パッケージはIDEにもインストールする必要があり、IDEはコンポーネントがどのプラットフォームで使用できるかをチェックしています(例えば、Windows専用コンポーネントはmacOSアプリで使用できません)。新旧のWin64プラットフォームの両方を追加したので、Win32 設計時パッケージを再ビルドします。IDEにインストールされるパッケージの情報内には、コンポーネントやどのプラットフォームで使用可能かといった情報が含まれています。更新したパッケージをIDEへインストールすることによって、パレットでコンポーネントにマウスカーソルを合わせると、サポートプラットフォームの一覧にWindows 64-bit(モダン)も表示され、使用可能になります。
生成されるファイルは以下の通りです。
- Win32: <パッケージ>.lib(静的ライブラリとしてのパッケージ)と<パッケージ>.bpi(動的パッケージインポートライブラリ)
- Win32の場合、コンポーネントパッケージは、設計時(IDE向け、Win32のみ)とランタイム(アプリ向け、各プラットフォーム、つまりWin32、Win64、Win64 モダン)の2つのパッケージに分かれます。
- Win64: <package>.a (静的ライブラリとしてのパッケージ) および <package>.bpi (動的パッケージインポートライブラリ)
- Win64X* / Windows 64ビットモダン: <パッケージ>.lib (静的ライブラリとしてのパッケージ — 将来提供される動的パッケージに関する上記の注を参照してください)
* コンパイラがbcc64xという名前なので、”x “という名前をよく使用します。
上述したパッケージの再ビルドのプロセスによって、以下の 2点が行われます。
- ランタイムパッケージにWindows 64-bit (モダン)プラットフォームを追加することで、アプリのビルド時にパッケージにリンクするために必要なバイナリファイルを提供
- プロジェクトにプラットフォームを追加した後、設計時 Win32パッケージを再ビルドして IDE に再インストールすることで、コンポーネントが Windows 64 ビット(モダン)で使用可能であることを IDEへ通知
まとめ
新しいツールチェーンは基盤となるリリースであり、将来に向けて多くの優れたメリットや新たな可能性を持っています。 冒頭でも申し上げましたが、このリリースはまだ「バージョン1」であるため、現状ではまだ実装できていない機能もございます。
ただ、12.1では、プラットフォームの規約からCランタイムの選択、64ビットバイナリの選択、品質と機能の幅広さの比較に至るまで、あらゆるところにその痕跡を見ることができます。また違いを最小限に抑え、既存のコードとの互換性を確保するために多大な努力を行ってきました。詳しくはこちらのドキュメントをご覧ください。
エンバカデロでは、この新しいツールチェーンを使用して今後リリースされる製品を非常に楽しみにしていますし、今現在、開発者のためにどれだけ役に立っているのか、是非フィードバックください!
免責事項: このブログ記事で説明しているRAD Studioの将来のバージョンにおける新機能や改善点は、いずれも開発が完了しGA版がリリースされるまでコミットされません。
Design. Code. Compile. Deploy.
Start Free Trial Upgrade Today
Free Delphi Community Edition Free C++Builder Community Edition