ククログ

株式会社クリアコード > ククログ > AC_ARG_WITHでオプションを省略するとき、しないとき

AC_ARG_WITHでオプションを省略するとき、しないとき

はじめに

クリアコードの林です。今回はAutotools 1 にまつわる、最近遭遇したAC_ARG_WITH(に限らず)マクロの省略可能引数の落し穴の事例を紹介します。

ことの始まり

今回の記事を書くきっかけは、とあるアプリケーションのGStreamer 0.10/1.0対応をしていたときのことです。 GStreamer 0.10に対応していたアプリケーションを1.0系にも対応させることになりましたが、GStreamerは0.10系と1.0系とではAPIがかなり変更されています。 まだ0.10系を使いたいという需要もあったことから、0.10系と1.0系両方対応することになりました。

そのアプリケーションでは、ビルドシステムとしてAutotools 2 を使用していました。 そのため、configureのオプションでどちらのバージョンを使うか切り替えられるようにすることにしました。 Autoconfには --with-foo-bar という形式でオプションに指定した値を受けとることのできるマクロとして AC_ARG_WITH があるので、これを使うことにしました。

configureで実現しようとしていたこと

configureスクリプトを実行するときに実現したかったことは次の通りです。

  • 明示的にGStreamer 0.10/1.0を指定してビルドすることができる
  • 特に指定していない場合、GStreamer 1.0があればそちらを使い、なければGStreamer 0.10を使ってビルドすることができる

前者は --with-gstreamer-version というオプションを追加し、 --with-gstreamer-version=0.10 であればGStreamer 0.10系を使い、 --with-gstreamer-version=1.0 であればGStreamer 1.0系を使う指定となるように AC_ARG_WITH を追加しました。

AC_ARG_WITH([gstreamer],
            AS_HELP_STRING([--with-gstreamer-version=@<:@auto/1.0/0.10@:>@],
                           [Gstreamer version (default: auto)]),
            [with_gstreamer_version=auto])

後者は --with-gstreamer-version の値を見て PKG_CHECK_EXISTS マクロでGStreamer 1.0の有無、GStreamer 0.10の有無を順にチェックするようにしました。

case "$with_gstreamer_version" in
    "0.10")
        GSTREAMER_REQUIRED=0.10.35
        GST_MAJORMINOR=0.10
        ;;
    "1.0")
        GSTREAMER_REQUIRED=1.0.0
        GST_MAJORMINOR=1.0
        ;;
    *)
        PKG_CHECK_EXISTS(gstreamer-1.0,
                         [GSTREAMER_REQUIRED=1.0.0 GST_MAJORMINOR=1.0],
                         [GSTREAMER_REQUIRED=0.10.36 GST_MAJORMINOR=0.10])
       ;;
esac

GSTREAMER_REQUIRED というのが必須となるバージョン指定で、GST_MAJORMINORというのがGStreamer 0.10/1.0どっちを使うかという変数です。 それぞれの変数を使った具体的な0.10/1.0の対応については割愛しますが、バージョンチェック自体はこんな感じにしていました。

AC_ARG_WITHの奇妙な挙動

前述のように AC_ARG_WITH を追加した後で、警告がでることに気づきました。

% ./configure --with-gstreamer-version=0.10
configure: WARNING: unrecognized options: --with-gstreamer-version

configure実行時にオプションが認識できていません。これは、 AC_ARG_WITH の第1引数の指定ミスでした。

% diff -u just-work/configure.ac right-thing/configure.ac
--- just-work/configure.ac        2014-09-11 16:39:34.392214521 +0900
+++ right-thing/configure.ac 2014-09-11 16:39:34.392214521 +0900
@@ -13,9 +13,10 @@

 # Checks for libraries.

-AC_ARG_WITH([gstreamer],
+AC_ARG_WITH([gstreamer-version],
             AS_HELP_STRING([--with-gstreamer-version=@<:@auto/1.0/0.10@:>@],
                            [Gstreamer version (default: auto)]),

ただ、修正前の状態で奇妙な挙動を示していることに気づきました。 オプション指定が間違っているのに、明示的に指定した0.10を使えています。

明示的に0.10を指定した場合、次のようになりました。

% ./configure --with-gstreamer-version=0.10
configure: WARNING: unrecognized options: --with-gstreamer-version
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /bin/mkdir -p
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking whether make supports nested variables... yes
checking for gcc... gcc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables... 
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ISO C89... none needed
checking whether gcc understands -c and -o together... yes
checking for style of include used by make... GNU
checking dependency style of gcc... gcc3
0.10
checking for GST... yes
checking that generated files are newer than configure... done
configure: creating ./config.status
config.status: creating Makefile
config.status: creating config.h
config.status: config.h is unchanged
config.status: executing depfiles commands
configure: WARNING: unrecognized options: --with-gstreamer-version
GStreamer: 0.10.36

明示的に1.0を指定した場合、次のようになりました。

% ./configure --with-gstreamer-version=1.0
configure: WARNING: unrecognized options: --with-gstreamer-version
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /bin/mkdir -p
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking whether make supports nested variables... yes
checking for gcc... gcc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables... 
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ISO C89... none needed
checking whether gcc understands -c and -o together... yes
checking for style of include used by make... GNU
checking dependency style of gcc... gcc3
1.0
checking for GST... yes
checking that generated files are newer than configure... done
configure: creating ./config.status
config.status: creating Makefile
config.status: creating config.h
config.status: config.h is unchanged
config.status: executing depfiles commands
configure: WARNING: unrecognized options: --with-gstreamer-version
GStreamer: 1.2.4

AC_ARG_WITH の定義が間違っているので、元々意図していた --with-gstreamer-version= が機能しないように思えますが、実際にはうまく動いています。 どういうことでしょうか。3

AC_ARG_WITHとフォールバック

おかしいなと感じたら、まずは公式ドキュメントを確認してみましょう。 AC_ARG_WITH()のドキュメント のシグネチャは次の通りです。

Macro: AC_ARG_WITH (package, help-string, [action-if-given], [action-if-not-given])

間違っていた定義では、 packagegstreamer-version ではなく gstreamer なので --with-gstreamer-versionunrecognized option 扱いになっています。 そのため、以下の警告が出ていました。

configure: WARNING: unrecognized options: --with-gstreamer-version

このとき、Autoconfはフォールバックとして with_gstreamer_version に指定した値をいれます。

そこで、以下のようにAC_ARG_WITHそのものを削除してみました。

% diff -u just-work/configure.ac fallback/configure.ac
--- just-work/configure.ac      2014-09-11 16:39:34.392214521 +0900
+++ fallback/configure.ac       2014-09-11 16:39:34.392214521 +0900
@@ -13,10 +13,6 @@

 # Checks for libraries.

-AC_ARG_WITH([gstreamer],
-            AS_HELP_STRING([--with-gstreamer-version=@<:@auto/1.0/0.10@:>@],
-                           [Gstreamer version (default: auto)]),
-            [with_gstreamer_version=auto])
 echo $with_gstreamer_version

結果からいうと上記の AC_ARG_WITH を削除しても挙動がまったく一緒でした。 つまり、Autoconfがフォールバックとして $with_gstreamer_version に指定した値を設定してくれているため、たまたまうまく動作していたのです。

AC_ARG_WITH の正しい指定

まずは、 AC_ARG_WITHの第1引数である packagegstreamer ではなく、 gstreamer-version にします。

AC_ARG_WITH([gstreamer-version],
            AS_HELP_STRING([--with-gstreamer-version=@<:@auto/1.0/0.10@:>@],
                           [Gstreamer version (default: auto)]),
            [with_gstreamer_version=auto])

次に [with_gstreamer_version=auto] は、何も指定していなかったらautoにするためのもの 4 なので、 action-if-not-given に対応していないといけません。 action-if-not-given は第4引数として指定することになっています。 よく見てみましょう。「,」 不足で第4引数ではなく、第3引数になってしまっていますね。

したがって、正しい定義は次の通りです。

AC_ARG_WITH([gstreamer-version],
            AS_HELP_STRING([--with-gstreamer-version=@<:@auto/1.0/0.10@:>@],
                           [Gstreamer version (default: auto)]),,
            [with_gstreamer_version=auto])

でも、,, で第3引数が省略されているのはとてもわかりにくいですね。明示的に書くことにしましょう。 最終的な定義はこうなりました。

AC_ARG_WITH([gstreamer-version],
            AS_HELP_STRING([--with-gstreamer-version=@<:@auto/1.0/0.10@:>@],
                           [Gstreamer version (default: auto)]),
            [with_gstreamer_version=$withval],
            [with_gstreamer_version=auto])

まとめ

今回はAutotoolsの AC_ARG_WITH マクロでオプションを省略するとき、しないときの挙動にまつわる事例を紹介しました。 必要最小限の記述をするために、省略可能な引数であれば省略するというのは正しいやりかたです。ただし、省略のしかたによっては、(ミスをしたときに)意図しない挙動にめんくらうことにもなります。

もし意図しない挙動に遭遇したら、まずは公式のドキュメントを参照してみましょう。うっかり勘違いして使っていないか確認できます。 途中に出ているログも注意深く見ておきましょう。今回のように明らかな警告がでているかもしれません。

GitHubに今回使用した サンプルのリポジトリ を置いてあるので、git cloneして手元でも動かして試すことができます。

git clone https://github.com/kenhys/ac-arg-with-sample.git

上記のリポジトリには以下の3つのサンプルが含まれています。

  • fallback AC_ARG_WITH を削除したバージョン
  • just-work 今回の記事のきっかけとなった奇妙な挙動を示すバージョン
  • right-thing 修正済みの AC_ARG_WITH バージョン

動作を試すには、それぞれのディレクトリに移動した後、最初に ../autogen.sh 、 次に ./configure を実行してみてください。

% ../autogen.sh
% ./configure --with-gstreamer-version=0.10
% ./configure --with-gstreamer-version=1.0
  1. Autotoolsに関しては Autotools事始め という過去記事もあるのでAutotoolsをまだよく知らない人は参考にしてみてください。

  2. Autotoolsを使用していると、./configure; make; make installでインストールまで行える。

  3. 途中で出力している0.10とか1.0というのはデバッグのためにあえて出力しているもの。

  4. autoだったらcase文で分岐してPKG_CHECK_EXISTSで再度チェックするようにした部分が実行される。