🐯

Watcom C/C++ で cmake を使う

2024/10/09に公開

Watcom C/C++ でのビルドに cmake を使うメモ、です。

以下、Windows(10) 環境、open watcom 1.9(2.0)、cmake 3.30 を使っています。
また dos exe の実行は dosbox-x で試しました。実機未確認。

前準備

cmake と watcom c/c++ がインストール済み、コマンドラインで使えるよう PATH を通しておきます。

cmake を使うにあたっては環境変数 WATCOM に、Watcom のディレクトリが設定されている必要あります。

都度、Watcom C/C++ の環境設定する場合は、Watcom のインストール例を参考に

set "WATCOM=c:\WATCOM"
set "PATH=%WATCOM%\BINNT;%WATCOM%\BINW;%PATH%"
set "INCLUDE=%WATCOM%\H;%WATCOM%\H\NT;%WATCOM%\H\NT\DIRECTX;%WATCOM%\H\DDK;%INCLUDE%"
set "EDPATH=%WATCOM%\EDDAT"

な感じのバッチを用意して実行でしょうか。

ビルド

CMakeLists.txt (内容は後述)、と hello.c

hello.c
#include <stdio.h>
int main(void) {
    printf("Hello World!\n");
    return 0;
}

を用意して、

project/
  CMakeLists.txt
  src/hello.c
  build/

のようなディレクトリ配置に。

cmake 生成は build/ をカレント・ディレクトリにして行うとします。

cmake の引数は

cmake -G "Watcom WMake" ../

のように WMake を指定して生成します。
できた Makefile は wmake でビルド。

デフォルトだと Debug 専用の Makefile が作られるので、Release 用を生成したい場合は、

cmake -G "Watcom WMake" -DCMAKE_BUILD_TYPE=Release ../

-DCMAKE_BUILD_TYPE=Release を引数に加えます。
( Debug を明示するなら -DCMAKE_BUILD_TYPE=Debug )

※ひとつの Makefile で Debug/Release が指定できる Makefile は作れなさそう。
vs sln(msbuild) 用や xcode 用なら出来るけど、Makefile 出力では単機能の模様。

追記: MinSizeRel

16bit DOS 向だと、速度よりプログラムサイズのほうが大事になりやすいので、
-DCMAKE_BUILD_TYPE=MinSizeRel
で、サイズ優先オプティマイズを使うのも手です。

CMakeLists.txt

まずは Windows 用で static C ランタイムライブラリを使用するビルド例。

CMakeLists.txt
cmake_minimum_required(VERSION 3.24)

project(hello)

if(CMAKE_C_COMPILER_ID MATCHES "Watcom" OR CMAKE_CXX_COMPILER_ID MATCHES "Watcom")
  set(CMAKE_WATCOM_RUNTIME_LIBRARY "SingleThreaded")
endif()

add_executable(hello "src/hello.c")

最小バージョンを 3.24 以上にするのが肝でしょうか。

昔から cmake は Watcom に対応していて DLL 版Cランタイムの exe なら作れていたのですが、ver.3.24 (2022年)の改修で static C ランタイムにも対応したようです。

MSVC その他にもある CMAKE_{コンパイラ名}_RUNTIME_LIBRARY が ver.3.24 から watcom でも使えるようになり、 CMAKE_WATCOM_RUNTIME_LIBRARY

  • SingleThreaded
  • MultiThreaded
  • SingleThreadedDLL
  • MultiThreadedDLL

のいづれかを設定できます。
DLL がついているのが DLL版ランタイム、なければ static 版ランタイムになります。
(SingleThreaded / MultiThreaded はマルチスレッド考慮かどうか)
Windows 用で無指定(デフォルト)の場合は MultiThreadedDLL になります。

ここでは、次の if 文でコンパイラが Watcom C/C++ なら(ID に Watcom の文字列があれば)、CMAKE_WATCOM_RUNTIME_LIBRARY に static 版 C ランタイムになる設定をしています。

※ この設定は project(...) の後でのみ有効。

32bit DOSエクステンダ(dos4gw) 用生成

先の Windows 用の CMakeLists.txt のまま、

cmake -G "Watcom WMake" -DCMAKE_SYSTEM_NAME=dos ../

のように引数の -DCMAKE_SYSTEM_NAME=dos でターゲット環境を dos にすれば、コンパイラが wcl386 なので DOS エクステンダ(dos4gw) 向けの生成になります。

これを CMakeLists.txt 中に書きたい場合は、

CMakeLists.txt
cmake_minimum_required(VERSION 3.24)

if("${CMAKE_GENERATOR}" STREQUAL "Watcom WMake")
  set(CMAKE_SYSTEM_NAME "dos")
endif()

project(hello)

if(CMAKE_C_COMPILER_ID MATCHES "Watcom" OR CMAKE_CXX_COMPILER_ID MATCHES "Watcom")
  set(CMAKE_WATCOM_RUNTIME_LIBRARY "SingleThreaded")
endif()

add_executable(hello "src/hello.c")

のように、project(...) の前に、CMAKE_SYSTEM_NAME を設定。

コンパイラの種類(CMAKE_?_COMPILER_ID) は project(...) の後でしか使えないので、判定は CMAKE_GENERATOR で代用しています。

なお dos ターゲットの場合、CMAKE_WATCOM_RUNTIME_LIBRARY は SingleThreaded のみ設定できます(もちろんデフォルト)。
MultiThreaded 等他を設定すると中途半端に Windows の exe になったりするので注意、と。

16 bit MSDOS 用生成

16 bit MSDOS 用に作る場合は、

  • CMAKE_SYSTEM_NAME=dos でターゲットをDOSに.
  • CMAKE_SYSTEM_PROCESSOR=I86 で 16bit 86 CPUを使う指定(80186,80286 もコレ)
  • CMAKE_C_COMPILER=wclCMAKE_CXX_COMPILER=wcl で、コンパイラとして wcl386 でなく wcl を利用.

を指定します。
コマンドライン引数だと長過ぎるので、CMakeLists.txt 内に記述。

CMakeLists.txt
cmake_minimum_required(VERSION 3.24)

if("${CMAKE_GENERATOR}" STREQUAL "Watcom WMake")
  set(CMAKE_SYSTEM_NAME "dos")
  set(CMAKE_SYSTEM_PROCESSOR "I86")
  set(CMAKE_C_COMPILER "wcl")
  set(CMAKE_CXX_COMPILER "wcl")
endif()

project(hello)

if(CMAKE_C_COMPILER_ID MATCHES "Watcom" OR CMAKE_CXX_COMPILER_ID MATCHES "Watcom")
  set(CMAKE_WATCOM_RUNTIME_LIBRARY "SingleThreaded")
  add_compile_options(-ms -1)  # small モデル. 186 用に生成.
endif()

add_executable(hello "src/hello.c")

メモリモデル指定とか、ターゲットCPU指定とかは、コンパイラのオプションで指定して、と。

ちょっとだけ Toolchain ファイル

Watcomに限らず、実際的に使うときは、ひとつの CMakeLists.txt で、複数のターゲット環境(Win32、16bit DOS、DOSエクステンダ) に対応することも多いと思います。

cmake でカスタムなターゲット設定は toolchain.cmake に分けるほうがお作法なのかも?という気もしたので、DOS コンパイラ設定を別ファイルにして CMAKE_TOOLCHAIN_FILE で指定するようにしてみました。

watcom-dos16-toolchain.cmake
set(CMAKE_SYSTEM_NAME "dos")
set(CMAKE_SYSTEM_PROCESSOR "I86")
set(CMAKE_C_COMPILER "wcl")
set(CMAKE_CXX_COMPILER "wcl")
watcom-dos32-toolchain.cmake
set(CMAKE_SYSTEM_NAME "dos")
set(CMAKE_SYSTEM_PROCESSOR "X86")
set(CMAKE_C_COMPILER "wcl386")
set(CMAKE_CXX_COMPILER "wcl386")
CMakeLists.txt
cmake_minimum_required(VERSION 3.24)

project(hello)

if(CMAKE_C_COMPILER_ID MATCHES "Watcom" OR CMAKE_CXX_COMPILER_ID MATCHES "Watcom")
  if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "I86")
    # 16 bit DOS.
    add_compile_options(-ms -0)  # small モデル. 8086 以降用で生成.
  elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "dos")
    # 32 bit DOS エクステンダ.
    add_compile_options(-4)      # 486 以降用に生成.
  else()
    # Windows.
    set(CMAKE_WATCOM_RUNTIME_LIBRARY "MultiThreaded")
  endif()
endif()

add_executable(hello "src/hello.c")

のようなファイルを用意して。(オプションは適当)

Windows用
cmake -G "Watcom WMake" ../
16bit DOS用
cmake -G "Watcom WMake" -DCMAKE_TOOLCHAIN_FILE=../watcom-dos16-toolchain.cmake ../
32bit DOS エクステンダ用
cmake -G "Watcom WMake" -DCMAKE_TOOLCHAIN_FILE=../watcom-dos32-toolchain.cmake ../

で Makefile 生成。

-DCMAKE_TOOLCHAIN_FILE 指定するなら -G 無しにしたいところだけれど、己には無理ゲーでした。

32bit dos 用は、CMAKE_SYSTEM_NAME=dos だけでいいかもしれませんが、他も一応設定。
CMAKE_SYSTEM_PROCESSOR は I86 でなければ 32bit扱いなので何でもよさそうですが、生成された情報では X86 になっていたので、それに合わせました。

おわりに

未だ cmake はよくわからずだけど 16bit dos 生成対応しようと思って。

CMake/share/cmake-?.??/Modules/*.cmake で "watcom" を検索しながら調べてみたら実はすでに DOS 対応していた、と。

で、どう使うんだ?と試したのが今回。

正直、何か間違いや遠回りをしてそうな気もするけれど、とりあえず、dos 用の 16bit 32bit exe を cmake で生成できたので、吉とします。

Discussion