モジュール機能を使ったC++ソースファイルをコンパイルするためには、そのソースファイルがインポートするモジュールのCMI1が必要になります。CMIはモジュールインターフェース単位から作成されます。
CMIに関する仕様は処理系によってまちまちなので、モジュール機能を使う場合はこのあたりの仕様に注意を払うとよいでしょう。
環境
- GCC 14.2.0
- Clang 18.1.8
サンプルプログラム
import <string_view>;
import bbb;
int main()
{
greet_twice("world");
}
export module aaa;
import <string_view>;
export void greet(std::string_view);
module aaa;
import <iostream>;
void greet(std::string_view name)
{
std::cout << "hello, " << name << std::endl;
}
export module bbb;
import <string_view>;
export void greet_twice(std::string_view);
module bbb;
import aaa;
void greet_twice(std::string_view name)
{
greet(name);
greet(name);
}
GCC
GCCではCMIはモジュールインターフェース単位をコンパイルすることで作成されます。CMIは作業ディレクトリ直下のgcm.cacheディレクトリに保存されます。モジュール名とCMIファイル名は次のように対応します。
| モジュール名 | CMIファイル名 |
|---|---|
abc |
abc.gcm |
abc.def |
abc.def.gcm |
abc:xyz |
abc-xyz.gcm |
CMIは必要になった際に自動的にgcm.cacheから探されるので、コンパイラに明示的にCMIファイル名を渡す必要はありません。
上のサンプルプログラムをビルドするためのコマンドは次のようになります。
CXX=g++
CXXFLAGS='-std=c++20 -fmodules-ts -fPIC'
LDFLAGS='-pie'
$CXX $CXXFLAGS -x c++-system-header -c iostream
$CXX $CXXFLAGS -x c++-system-header -c string_view
$CXX $CXXFLAGS -x c++ -c -o aaa.cppm.o aaa.cppm
$CXX $CXXFLAGS -c -o aaa.cpp.o aaa.cpp
$CXX $CXXFLAGS -x c++ -c -o bbb.cppm.o bbb.cppm
$CXX $CXXFLAGS -c -o bbb.cpp.o bbb.cpp
$CXX $CXXFLAGS -c -o main.cpp.o main.cpp
$CXX $LDFLAGS -o greet-twice main.cpp.o aaa.cppm.o aaa.cpp.o bbb.cppm.o bbb.cpp.o
-x lang- ソースファイルの種類を指定する。
-fmodules-ts- C++のモジュール機能を使用する。
Clang
ClangではCMI2はモジュールインターフェース単位をプリコンパイルすることで作成されます。CMIは拡張子が.pcmであるという点を除いてGCCと同じように命名されます。GCCの場合とは異なりCMIが格納される既定の場所は存在しないので、CMIの場所をコンパイラに伝える必要があります。
また、モジュールインターフェース単位のオブジェクトファイルはCMIをさらにコンパイルすることで作成されます。
上のサンプルプログラムをClangを使ってビルドするためのコマンドは次のようになります。
CXX=clang++
CXXFLAGS='-std=c++20 -fprebuilt-module-path=. -fPIC'
LDFLAGS='-pie'
$CXX $CXXFLAGS -x c++-system-header --precompile -o iostream.pcm iostream
$CXX $CXXFLAGS -x c++-system-header --precompile -o string_view.pcm string_view
CXXFLAGS+=' -fmodule-file=iostream.pcm'
CXXFLAGS+=' -fmodule-file=string_view.pcm'
$CXX $CXXFLAGS --precompile -o aaa.pcm aaa.cppm
$CXX $CXXFLAGS --precompile -o bbb.pcm bbb.cppm
$CXX $CXXFLAGS -c -o aaa.cppm.o aaa.pcm
$CXX $CXXFLAGS -c -o bbb.cppm.o bbb.pcm
$CXX $CXXFLAGS -c -o aaa.cpp.o aaa.cpp
$CXX $CXXFLAGS -c -o bbb.cpp.o bbb.cpp
$CXX $CXXFLAGS -c -o main.cpp.o main.cpp
$CXX $LDFLAGS -o greet-twice main.cpp.o aaa.cppm.o aaa.cpp.o bbb.cppm.o bbb.cpp.o
--precompile-fprebuilt-module-path=path-fmodule-file=file現段階ではヘッダーユニットのCMIは-fmodule-fileを使って指定することしかできませんが、この制限は将来的には取り払われる予定とのことです。その場合は次の2行は必要なくなります。
CXXFLAGS+=' -fmodule-file=iostream.pcm'
CXXFLAGS+=' -fmodule-file=string_view.pcm'
-fmodule-outputオプションを使えばGCCのようにCMIの作成とオブジェクトファイルの作成を同時に行うことができます。
$CXX $CXXFLAGS -fmodule-output=aaa.pcm -c -o aaa.cppm.o aaa.cppm
$CXX $CXXFLAGS -fmodule-output=bbb.pcm -c -o bbb.cppm.o bbb.cppm
この場合CMIのコンパイルは必要なくなりますが、ビルド処理の並列化可能性が少しだけ悪化します。