SCons のすすめ

2024年9月8日

はじめに

Make の代替ツール SCons のメモ。

環境

  • Ubuntu 18.04 LTS
  • macOS Mojave 10.14.6 (Anaconda3)
  • Windows 11

インストール

Ubuntu なら apt-get でインストールできる。

$ sudo apt-get install scons

Python を入れているなら pip でもよい。

$ pip install scons

最小のコンパイル

C のソースファイル hello.c を用意する。SConstruct ファイルを作る。

SConstruct

Program("hello.c")

コンパイル。

$ scons

実行ファイル "hello" ができる。実行ファイル名を指定することもできる。

Program("myhello", "hello.c")

コードが C++ なら、単純に次のように書けばよい。

Program("hello.cc")

オプション "-Q" でメッセージを最低限にすることができる。

$ scons -Q

クリーン

作成されたオブジェクトファイルなどを削除するには、オプション "-c" を指定して実行する。

$ scons -c

分割コンパイル

ソースファイルが複数ある場合は、次のようにする。

SConstruct

Program("prog", ["file1.cc", "file2.cc"])

次のようにも書ける。

Program("prog", Split("file1.cc file2.cc"))

また、次のように拡張子で選択してもよい。

Program("prog", Glob("*.cc"))

SConstruct は Python スクリプトなので、変数を渡してもよい。

prog = "prog"
src = [
    "file1.cc",
    "file2.cc",
]
Program(prog, src)

scons は、make のように変更があったものだけをコンパイルする。もし、file2.cc に対応するヘッダーファイル file2.h があって、それを file1.cc がインクルードしているとする。このとき、make では Makefile で file1.cc が file2.h に依存していることを設定しておく必要があるが、scons はそのあたりを勝手にやってくれるようである。

インクルードパスの指定

インクルードファイルのパスを指定する場合は、CPPPATH を使う。

Program(prog, src, CPPPATH=["/usr/local/include"])

Frotran でモジュールのパスを指定する場合は、F90PATH を用いる。

F90PATH=libpath

ライブラリの指定

ライブラリを指定する場合は、LIBS などの引数で指定する。たとえば、"-L/usr/lib -L/usr/local/lib -lm" を指定するのと同等の設定は以下のようになる。

Program(prog, src, LIBS=["lib1"], LIBPATH=["/usr/local/lib"])

上記だけでは、プログラムがリンクしたライブラリを見失うことがある。別途 RPATH でパスを設定する。

LIBPATH=["/usr/local/lib"]
Program(prog, src, LIBS=["lib1"], LIBPATH=LIBPATH, RPATH=LIBPATH)

オブジェクトファイルに対してリンクの指定をする場合は、次のようにする。

Program("prog", ["file1.o", "file2.o"], LINKFLAGS="-lm")

コンパイルオプションの指定

コンパイルオプションを指定するには、引数 parse_flags に渡す。

Program(prog, src, parse_flags="-O2 -Wall -lm")

上記のように設定すると、オプションをちゃんと考えて設定してくれるようである。

$ scons -Q
g++ -o file1.o -c -O2 -Wall file1.cc
g++ -o file2.o -c -O2 -Wall file2.cc
g++ -o prog file1.o file2.o -lm

オプションを個別に設定したい場合は、次のように書ける。

Object("file1.cc", CCFLAGS="-O2")
Object("file2.cc", CCFLAGS="-O2")
Program("prog", ["file1.o", "file2.o"])

Fortran

Fortran 90 の場合は、フラグの指定に F90FLAGS を使う。

Program(progname, src, F90FLAGS=flags)

FORTRAN77 の場合は、代わりにFORTRANFLAGS を使う。

gprof 用の設定

Program("file.c", parse_flags=["-Wall", "-Ofast"], LINKFLAGS=["-pg"])

デバッグ用の設定

デバッグ用にオプションを変える場合は、たとえば次のようにする (Fortran の例)。

flags = ["-Wall"]

if ARGUMENTS.get("debug"):
    flags += ["-g", "-fbounds-check"]

Program(progname, src, F90FLAGS=flags)

実行時に次のようにする。

$ scons debug=1

デバッグをデフォルトにして、次のようにしてもよい。

if not ARGUMENTS.get("release"):
    flags += ["-g", "-fcheck=all"]
else:
    flags += ["-Ofast"]

リリース時に次のようにすればよい。

$ scons release=1

リンカオプション

リンカオプションを指定するには、Environment() を使う。

env = Environment()
env["LINKFLAGS"] = "-static"
env.Program(progname, src)

依存関係の指定

Fortran でインクルードファイルを使う場合など、依存関係を明示する必要がある場合は、次のようにする。

Depends("equation.o", ["eqnop.inc", "eqnop_imp.inc"])

ライブラリのコンパイル

ライブラリをコンパイルする場合は、Program() の代わりに Library() を使う。

パスを継承する

パスを実行環境から継承したい場合は、次のようにする。

import os
env = Environment(ENV=os.environ.copy())
env.Program(progname, src)

MinGW を使うときは、toolsで "mingw" を指定する。

import os
env = Environment(ENV=os.environ.copy(), tools=["mingw"])
env.Program(progname, src)