開発環境
印刷用ページへ

Xcodeおよびコマンドライン開発ツールのインストールとアクティブ開発ディレクトリの切り替え【Mac OS X Mavericks版】

Mac OS X Mavericks上へのXcodeとコマンドライン開発ツールのインストール方法、およびアクティブ開発ディレクトリの意味と切り替え方法について紹介します。

Mavericksクリーンインストール直後の状態

Mavericks(Mac OS X 10.9)搭載Macの出荷状態、もしくはMavericksクリーンインストール直後の状態において、開発ツールは存在しないため、別途Xcodeと呼ばれる統合開発環境をインストールする必要があります。

統合開発環境に頼らず、UNIX styleでプログラムを作成する場合は、コマンドライン開発ツール(command line developer tools)が必要です。従来、コマンドライン開発ツールはXcode上に追加インストールする形で提供されていましたが、Mavericksからコマンドライン開発ツールを単独でインストールすることが可能になりました(Xcode不要)。

本稿では、アクティブ開発ディレクトリ切り替えの意味を理解するために、Xcodeとコマンドライン開発ツールの双方をインストールして解説を行います。

これから順を追いながら、Max OS X上における開発ツールのインストール過程を見てみましょう。

/usr/includeディレクトリ

UNIX環境において、システムヘッダーファイル群は/usr/inludeディレクトリ、ユーザヘッダーファイル群は/usr/local/includeに格納する習慣があります。

$ ls -F /usr/
X11/                bin/                 libexec/           share/
X11R6@              lib/                 sbin/              standalone/

しかし、クリーンインストール直後の/usrディレクトリに、/usr/includeおよび/usr/local/includeは存在しません

manファイルの検索パス

UNIX環境で重要な位置を占める、manファイル(manual entries)の検索パスは、manコマンドに-wオプションを与えると表示されます。

$ man -w
/usr/share/man

クリーンインストール直後は、/usr/share/manディレクトリのみが指定されています。

$ ls -F /usr/share/man
man1/ man3/ man4/ man5/ man6/ man7/ man8/ man9/ mann/ whatis
$ ls /usr/share/man/man3
libgmalloc.3.gz
$ ls /usr/share/man/man1/cc.*
ls: /usr/share/man/man1/cc.*: No such file or directory

/usr/share/manディレクトリには、8つのmanセクションを収めるサブディレクトリが存在しています。

printf(3)などCライブラリ関数を格納するセクション3ディレクトリ(man3)には、libgmalloc(3)のエントリしか用意されていません。このため、Cライブラリ関数のマニュアルは参照できません(man 3 printfは"No manual entry for"を出力し異常終了する)。

開発ツールのマニュアルはセクション1(man1)に格納されますが、この中にcc.1/make.1など開発ツールのマニュアルエントリは存在しないため、man ccやman makeは同様にエラーとなります。

ccコマンド

最後に、Cコンパイラのドライバツールである、ccコマンドの起動を試みてみましょう(-vはバージョン番号などの付帯情報を表示するオプション)。

$ cc -v
xcode-select: note: no developer tools were found at '/Applications/Xcode.app', requesting install. Choose an option in the dialog to download the command line developer tools.

「まだ開発ツールがインストールされていないのだから、起動はできないだろう」との予想に反し、ccコマンドが立ち上がりテキストメッセージが表示されています。"/Applications/Xcode.appディテクトリに開発ツールが存在しない"というエラーですが、同様のメッセージはasやmakeコマンドを実行しても表示されます。

テキストだけでなく、画面上にも上記のような開発ツールのインストールを促す確認画面が表示されます。

"インストール"を選択するとコマンドライン開発ツールのみが、"Xcodeを入手"を選択するとXcodeとコマンドライン開発ツールの両者がインストールされます。

今回は、開発環境の構築過程をステップ毎に追っていきますので、"今はしない"を選択してください。

$ which cc
/usr/bin/cc
$ ls -l /usr/bin/cc
lrwxr-xr-x 1 root wheel 5 12 1 19:22 /usr/bin/cc -> clang

ccコマンドの実体をwhichコマンドで確認すると、/usr/bin/ccでした。さらにlsコマンドで同ファイルを調べると、/usr/bin/clangへのシンボリックリンクとなっています。

$ ls -l /usr/bin/clang /usr/bin/as /usr/bin/otool /usr/bin/make
-rwxr-xr-x 1 root wheel 14224 12 1 19:22 /usr/bin/as
-rwxr-xr-x 1 root wheel 14224 12 1 19:22 /usr/bin/clang
-rwxr-xr-x 1 root wheel 14224 12 1 19:22 /usr/bin/make
-rwxr-xr-x 1 root wheel 14224 12 1 19:22 /usr/bin/otool

試しに、clang/as/otool/make、計4つのツールのファイルを見てみると、すべて同じファイルサイズを有しています(加えて機能に比してサイズが過小)。

なぜ4つの異なるツールのファイルサイズが同一なのか?その理由は後半で明らかになります。

Xcodeのインストール

それでは、Mac OS X統合開発環境であるXcode(無料)をインストールしてみましょう。

App Storeアプリケーションを起動し、右上の検索窓に"xcode"と入力すると、Xcodeが表示されます。

Xcodeのバージョンは執筆時点(2013.12.10)で、5.0.2です。

"無料"のボタンをクリックすると、

"Appをインストール"というボタンに変わりますので、さらにクリックすると、Apple IDを入力した後に、インストールが開始されます。

インストール完了後に、初めてXcodeを起動すると追加コンポーネントについて尋ねられますので、"Install"を選択してください。

以上でXcodeのインストールは完了です。次に、Xcodeの開発環境について見てみましょう。

/usr/includeディレクトリ

$ ls -F /usr
X11/                bin/                 libexec/           share/
X11R6@              lib/                 sbin/              standalone/

/usrディレクトリに変化はなく、/usr/includeはまだ存在していません。

manファイルの検索パス

man -w
/usr/share/man:/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/share/man:/Applications/Xcode.app/Contents/Developer/usr/share/man:/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/share/man

manファイルの探索パスに関しては、クリーンインストール直後の/usr/share/manに加え、下記の3つのディレクトリが追加されています。

① /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/share/man
② /Applications/Xcode.app/Contents/Developer/usr/share/man
③ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/share/man

それぞれのディレクトリ内容を見てみます。

$ ls -F /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/share/man
man1/ man2/ man3/ man4/ man5/ man6/ man7/ man8/ man9 mann/
$ ls -F /Applications/Xcode.app/Contents/Developer/usr/share/man
man1/ man3/ man5/ man7/ man8/
$ ls -F /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/share/man
man1/ man3/ man5/

詳細は割愛しますが、Cライブラリ関数であるprintf(3)などは、①のman3ディレクトリに多数格納されています。

$ ls -F /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/share/man/man1
ar.1                    flex++.1                otool.1
as.1                    flex.1                  pagestuff.1
asa.1                   gm4.1                   ranlib.1
bison.1                 gperf.1                 rebase.1
c++.1@                  indent.1                redo_prebinding.1
c89.1                   install_name_tool.1     rpcgen.1
c99.1                   ld.1                    segedit.1
cc.1@                   lex.1                   size.1
clang++.1@              libtool.1               strings.1
clang.1                 lipo.1                  strip.1
cmpdylib.1              lorder.1                unifdef.1
codesign_allocate.1     m4.1                    unifdefall.1
ctags.1                 mig.1                   unwinddump.1
ctf_insert.1            migcom.1                what.1
dsymutil.1              mkdep.1                 yacc.1
dwarfdump.1             nm.1
dyldinfo.1              nmedit.1

また、開発ツールのマニュアルエントリは、③のman1ディレクトリに存在します。ar、as、nmなどの基本ツールの他に、cc、clang、makeなど開発ツール一式のマニュアルが整備されています(拡張子.1はマニュアルセクション1を意味する)。ここで、cc.1は提供されているものの、gcc.1は存在しない点に注意してください。

manコマンドは、 -wオプションの後に参照したいマニュアルエントリ名を続けると、該当するmanファイルの絶対パスを表示する機能を持っています。

$ man -w cc
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/share/man/man1/cc.1
$ man -w make
/Applications/Xcode.app/Contents/Developer/usr/share/man/man1/make.1

cc.1およびmake.1のマニュアルエントリは、それぞれ異なるディレクトリに配置されています。

"man cc"、"man make"を実行すると、対応するマニュアルページが表示されるようになったことを確認しておきましょう。

Xcode/iOSライセンスへの同意

Xcodeインストール後に初めてccコマンドを実行すると、今度は下記のようなメッセージが表示されます。

$ cc -v

Agreeing to the Xcode/iOS license requires admin privileges, please re-run as root via sudo.

"Xcode/iOSライセンスに同意するためには管理者権限で実行せよ"ということなので、sudoを前置して再度ccを実行します。

$ sudo cc -v

WARNING: Improper use of the sudo command could lead to data loss
or the deletion of important system files. Please double-check your
typing when using sudo. Type "man sudo" for more information.

To proceed, enter your password, or type Ctrl-C to abort.

Password: ***********

You have not agreed to the Xcode license agreements. You must agree to both license agreements below in order to use Xcode.

Hit the Enter key to view the license agreements at '/Applications/Xcode.app/Contents/Resources/English.lproj/License.rtf'

管理者のパスワードを入力すると、License.rtfの内容を読むようにとのメッセージが表示され、リターンキーの入力を促されます。

Software License Agreements Press 'space' for more, or 'q' to quit q

By typing 'agree' you are agreeing to the terms of the software license agreements. Type 'print' to print them or anything else to cancel, [agree, print, cancel] agree

画面上でスペースキーを入力し続きを読むか、License.rtfファイルの内容を確認後に"q"を入力すると、最終確認を求められます。

ここで"agree"と応えると、

You can view the license agreements in Xcode's About Box, or at /Applications/Xcode.app/Contents/Resources/English.lproj/License.rtf

Apple LLVM version 5.0 (clang-500.2.79) (based on LLVM 3.3svn)
Target: x86_64-apple-darwin13.0.0
Thread model: posix

ccドライバが起動します。

以上で、Xcode上での開発に必要なツール類とmanファイル、ヘッダーファイル、ライブラリが利用可能な状態となりました。

hello.cのコンパイル

さて、先ほど確認したところ、/usr/includeディテクトリは存在しませんでした。UNIX styleの開発で必須となる<stdio.h>などのシステムヘッダーファイルが存在しないとなると、お決まりのhello.cは、Xcode環境ではコンパイルできないのでしょうか?実験してみましょう。

#include <stdio.h>

int main() {
    printf("Hello world!n");
    return 123;
}

定番のhello.cを用意します(return文で123を返す点が教科書とは異なる)。

$ cc -Wall -o hello hello.c
$ ./hello ;echo $?
Hello world!
123

問題なく実行可能ファイルをビルドでき、正常に実行できています。

なぜ、/usr/include/stdio.hが存在しないにもかかわらず、hello.cはコンパイルできたのでしょうか?その理由を探るためには、プリプロセッサのヘッダーファイル探索パスを調べる必要があります。

Cプリプロセッサ(cpp)によるヘッダーファイル処理の詳細については、"GNU開発ツール"をご覧ください。

Cプリプロセッサのヘッダーファイル探索パス

"GNU開発ツール"では、Cプリプロセッサのヘッダーファイル探索パスを確認する方法として、"cppコマンドの-vオプション"を紹介しています。

Xcode上でも、/usr/bin/cppコマンドは提供されているのですが、一部のヘッダーファイルを正常に処理できない問題があります。このため、cc (実体は/usr/bin/clang)コマンドにプリプロセス処理のみを実行するように指示する-Eオプション、およびccのコマンド引数をCプリプロセッサに引き渡す-Wp,オプションを活用します。

"cc -E -Wp,-v"を実行することで、"cpp -v"と同じ動作をccコマンドに行わせることが可能になります。なお、ccコマンドは入力ファイルを必要とするため、"cat /dev/null"をパイプでccコマンドに連結します(ccには標準入力からソースを読み込むことを意味する"-"を後置)。

以上をまとめると、ヘッダーファイルの探索パスを確認するためのコマンドは、次のようになります。

cat /dev/null|cc -E -Wp,-v -

それでは、実際に確認してみましょう。

$ cat /dev/null|cc -E -Wp,-v -
clang -cc1 version 5.0 based upon LLVM 3.3svn default target x86_64-apple-darwin13.0.0
ignoring nonexistent directory "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/local/include"
ignoring nonexistent directory "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/Library/Frameworks"
#include "..." search starts here:
#include <...> search starts here:
 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/5.0/include
 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include
 /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include
 /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks (framework directory)
End of search list.
# 1 "<stdin>"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 162 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "<stdin>" 2

出力結果を見ると、Xcode環境におけるシステムヘッダーファイルのデフォルト探索パスは、以下の4つのディレクトリから構成されています。

① /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/5.0/include
② /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include
③ /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include
④ /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks (framework directory)

4番目のディレクトリは Xcode用のフレームワークなので、前3者について内容を見てみましょう。

$ ls /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/5.0/include
__wmmintrin_aes.h       fmaintrin.h             stdarg.h
__wmmintrin_pclmul.h    immintrin.h             stdbool.h
aarch64_simd.h          iso646.h                stddef.h
altivec.h               limits.h                stdint.h
ammintrin.h             lzcntintrin.h           stdnoreturn.h
arm_neon.h              mm3dnow.h               tgmath.h
avx2intrin.h            mm_malloc.h             tmmintrin.h
avxintrin.h             mmintrin.h              unwind.h
bmi2intrin.h            module.map              varargs.h
bmiintrin.h             nmmintrin.h             wmmintrin.h
cpuid.h                 pmmintrin.h             x86intrin.h
emmintrin.h             popcntintrin.h          xmmintrin.h
f16cintrin.h            rtmintrin.h             xopintrin.h
float.h                 smmintrin.h
fma4intrin.h            stdalign.h

最初のディレクトリには、limits.h/stdint.h/stddef.hなど、システムに強く依存するヘッダーファイルが収められています。

$ ls /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include
FlexLexer.h

2番目のディレクトリには、FlexLexer.hというヘッダーファイルひとつだけが格納されています。

$ ls /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include
AssertMacros.h          iconv.h                 readline
Availability.h          ifaddrs.h               readpassphrase.h
AvailabilityInternal.h  inttypes.h              regex.h
AvailabilityMacros.h    iso646.h                removefile.h
Block.h                 kern                    resolv.h
CommonCrypto            krb5                    rpc
ConditionalMacros.h     krb5.h                  rpcsvc
MacTypes.h              langinfo.h              rune.h
NSSystemDirectories.h   launch.h                runetype.h
TargetConditionals.h    lber.h                  sandbox.h
Xplugin.h               lber_types.h            sasl
_locale.h               ldap.h                  sched.h
_structs.h              ldap_cdefs.h            search.h
_types                  ldap_features.h         secure
_types.h                ldap_schema.h           security
_wctype.h               ldap_utf8.h             semaphore.h
_xlocale.h              ldif.h                  servers
aio.h                   libc.h                  setjmp.h
aliasdb.h               libcharset.h            sgtty.h
alloca.h                libexslt                signal.h
apache2                 libgen.h                slapi-plugin.h
apr-1                   libkern                 spawn.h
ar.h                    libproc.h               sqlite3.h
architecture            libunwind.h             sqlite3ext.h
arpa                    libxml2                 stab.h
asl.h                   libxslt                 standards.h
assert.h                limits.h                stdbool.h
bitstring.h             localcharset.h          stddef.h
bootparams.h            locale.h                stdint.h
bsm                     mach                    stdio.h
bzlib.h                 mach-o                  stdlib.h
c++                     mach_debug              strhash.h
cache.h                 machine                 string.h
cache_callbacks.h       malloc                  stringlist.h
checkint.h              math.h                  strings.h
com_err.h               membership.h            struct.h
complex.h               memcached               sys
copyfile.h              memory.h                sysexits.h
cpio.h                  menu.h                  syslog.h
crt_externs.h           miscfs                  tar.h
ctype.h                 module.map              tcl.h
cups                    monetary.h              tclDecls.h
curl                    monitor.h               tclPlatDecls.h
curses.h                mpool.h                 tclTomMath.h
cxxabi.h                nameser.h               tclTomMathDecls.h
db.h                    nc_tparm.h              term.h
default_pager           ncurses.h               term_entry.h
device                  ncurses_dll.h           termcap.h
dirent.h                ndbm.h                  termios.h
disktab.h               net                     tic.h
dispatch                net-snmp                tidy
dlfcn.h                 netdb.h                 time.h
dns.h                   netinet                 timeconv.h
dns_sd.h                netinet6                tk.h
dns_util.h              netkey                  tkDecls.h
dtrace.h                nfs                     tkIntXlibDecls.h
editline                nl_types.h              tkMacOSX.h
err.h                   nlist.h                 tkPlatDecls.h
errno.h                 notify.h                ttyent.h
eti.h                   notify_keys.h           tzfile.h
execinfo.h              ntsid.h                 ucontext.h
expat.h                 objc                    ulimit.h
expat_external.h        odmodule                unctrl.h
fcntl.h                 openssl                 unistd.h
fenv.h                  os                      unwind.h
ffi                     panel.h                 util.h
fmtmsg.h                paths.h                 utime.h
fnmatch.h               pcap                    utmp.h
form.h                  pcap-bpf.h              utmpx.h
fsproperties.h          pcap-namedb.h           uuid
fstab.h                 pcap.h                  vfs
fts.h                   pexpert                 vis.h
ftw.h                   php                     vproc.h
get_compat.h            poll.h                  wchar.h
gethostuuid.h           printerdb.h             wctype.h
getopt.h                printf.h                wordexp.h
glob.h                  profile.h               xar
grp.h                   protocols               xlocale
gssapi                  pthread.h               xlocale.h
gssapi.h                pthread_impl.h          xpc
hfs                     pthread_spis.h          zconf.h
histedit.h              pwd.h                   zlib.h
i386                    ranlib.h

3番目のディレクトリには、Cライブラリ関数でお世話になるシステムヘッダーファイルの数々が並んでいます。hello.cでインクルードした"stdio.h"も確認できます。

これで、/usr/include/stdio.hが存在しないにもかかわらず、hello.cをコンパイルできた理由が明らかになりました。

Xcodeにおける基本ツールの実体

次に、基本ツールの実体について解析してみましょう。

$ make -v
GNU Make 3.81
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.This program built for i386-apple-darwin11.3.0
$ clang -v
Apple LLVM version 5.0 (clang-500.2.79) (based on LLVM 3.3svn)
Target: x86_64-apple-darwin13.0.0
Thread model: posix

Xcodeインストール後は、makeも、ccのリンク先であるclangも正常に動作しています。

$ ls -lF /usr/bin/clang /usr/bin/make
-rwxr-xr-x 1 root wheel 14224 10 25 17:39 /usr/bin/clang*
-rwxr-xr-x 1 root wheel 14224 10 25 17:39 /usr/bin/make*

しかし、/usr/binディレクトリのコマンドファイルを確認すると、そのサイズはクリーンインストール直後と同じサイズです。Cコンパイラの機能がわずか14KiBに収まる訳はありませんから、何かしらカラクリが隠されているはずです。

恐らく、それぞれのプログラムはコンパイル処理やmake処理の呼び出しを担当する外部関数を呼び出しているはずですから、nmコマンドで実行可能ファイル中に埋め込まれている未定義シンボルを調べてみます(シンボルの意味と解析方法については、"GNU開発ツール"を参照してください)

$ nm -u /usr/bin/clang /usr/bin/make

/usr/bin/clang:
_xcselect_invoke_xcrun
dyld_stub_binder

/usr/bin/make:
_xcselect_invoke_xcrun
dyld_stub_binder

nmコマンドに-uオプションを加えると、指定されたファイル中の未定義シンボルのみを出力します。結果は上の通りで、clangおよびmakeはどちらも内部で"xcselect_invoke_xcrun"という関数を呼び出しています(Mac OS Xでは、プログラム中のシンボルに自動的にアンダースコアが前置される)。

残念ながら、Appleはxcselect_invoke_xcrunの詳細について、一般ユーザには公開していないようですが、名前から類推するに、開発ツールの本体を探し出しこれを起動するために用意されている関数のようです。つまり、14224バイトというコンパクトなサイズを持つ、/usr/bin/clangや/usr/bin/makeは、「本体プログラム呼び出しを担当するスタブプログラム(stub program)」であると考えられます。

さらに、/usr/bin/clangや/usr/bin/makeは「xcselect_invoke_xcrun関数を有する外部ライブラリに依存」していると予想されます。

$ otool -L /usr/bin/clang
/usr/bin/clang:
/usr/lib/libxcselect.dylib (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1197.1.1)

Mac OS Xにおいて、実行可能ファイルが依存している外部ライブラリは、otoolコマンドの-Lオプションで確認することができます。

libSystem.B.dylibはLinuxにおけるlibc.soに相当するCライブラリですが、clangはさらにlibxcselect.dylibという名前のライブラリにも依存していました。その名前から、「Xcode(xc)の基本ツール呼び出しを担当する(selectする)」ライブラリであることは間違いないでしょう。

$ nm -g /usr/lib/libxcselect.dylib|grep " T "
00000000000012a1 T _xcselect_find_developer_contents_from_path
000000000000157e T _xcselect_get_developer_dir_path
0000000000001d4d T _xcselect_get_manpaths
000000000000194a T _xcselect_invoke_xcrun
0000000000001d18 T _xcselect_manpaths_free
0000000000001cf7 T _xcselect_manpaths_get_num_paths
0000000000001d00 T _xcselect_manpaths_get_path
0000000000001fca T _xcselect_trigger_install_request

念のため、nmコマンドでlibxcselect.dylib内部のグローバルシンボルを検索すると、.textセグメント("T")にxcselect_invoke_xcrunが含まれていました。

さて、ここで"xcrun --find clang"というコマンドを実行してみて下さい。

$ xcrun --find clang
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang

"xcrun --find"は、指定されたスタブプログラムの本体ファイルの絶対パスを表示します。

xcrunコマンドの出力は、/usr/bin中のclangから呼び出される本体プログラムが、Xcodeディレクトリ内部のclangであることを示しています。

$ ls -F /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
ar*                     dyldinfo*               otool*
as*                     flex*                   pagestuff*
asa*                    flex++*                 ranlib@
bison*                  gm4*                    rebase*
c++@                    gperf*                  redo_prebinding*
c89*                    indent*                 rpcgen*
c99*                    install_name_tool*      segedit*
cc@                     ld*                     size*
clang*                  lex*                    strings*
clang++@                libtool*                strip*
cmpdylib*               lipo*                   unifdef*
codesign_allocate*      lorder*                 unifdefall*
cpp*                    m4*                     unwinddump*
ctags*                  mig*                    what*
ctf_insert*             mkdep*                  yacc*
dsymutil*               nm*
dwarfdump*              nmedit*

/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/binディレクトリを検索すると、基本開発ツール一式が収められていることが分かります。

$ ls -l /usr/bin/ar /usr/bin/as /usr/bin/clang /usr/bin/nm
-rwxr-xr-x 1 root wheel  14224 10 25 17:39 /usr/bin/ar*
-rwxr-xr-x 1 root wheel  14224 10 25 17:39 /usr/bin/as*
-rwxr-xr-x 1 root wheel  14224 10 25 17:39 /usr/bin/clang*
-rwxr-xr-x 1 root wheel  14224 10 25 17:39 /usr/bin/nm*
$ cd /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin $ ls -lF ar as nm clang
-rwxr-xr-x 1 root wheel     29600 10 25 22:33 ar*
-rwxr-xr-x 1 root wheel     27824 10 25 22:33 as*
-rwxr-xr-x 1 root wheel  29381728 10 25 22:33 clang*
-rwxr-xr-x 1 root wheel    131632 10 25 22:33 nm*

スタブプログラムである/usr/binディレクトリのar/as/clang/nmは全て同一ファイルサイズでしたが、/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/binディレクトリのファイルは、すべてサイズが異なっています。clangに至っては、ファイルサイズが29MiBにも及ぶ巨大プログラムです。

ここまでのシーケンスをまとめたものが、上図です。赤字で示したディレクトリ(/Applications/Xcode.app/Contents/Developer)は、アクティブ開発ディレクトリ(active developer directory)と呼ばれます(後述)。

  1. シェル上でccコマンドを起動
  2. PATH環境変数を通じて、/usr/bin/ccを実行
  3. シンボリックリンクにより、/usr/bin/clangが起動
  4. clang内部のxcselect_invoke_xcrun関数を実行
  5. /Applications/Xcode.app/Contents/Developer
    /Toolchain/XcodeDefault.xctoolchain/usr/bin/clang
    を起動

Mac OS X上で開発ツールを使いこなす場合は、この流れを把握しておく必要があります。

コマンドライン開発ツールのインストール

それでは最後に、コマンドライン開発ツールをXcode上にインストールしてみましょう。Mavericks版からコマンドライン開発ツールのインストール方法が変更されているので、ご注意ください。

Xcodeメニューから、"Open Developer Tool"を選び、サブメニューの中から"More Developer Tools..."を選択します。

途中、上記画面のようにApple IDによるサインインを要求されます。

自動的にSafariが起動し、Apple Developer向けのダウンロードページが表示されます。この中に、"Command Line Tools (OS X Mavericks) for Xcode"と名付けられた、dmg (Disk iMaGe)パッケージが用意されているため、ダウンロードします(執筆時点で2013年10月23日版、ファイルサイズ97.4MB)。

Xcode経由ではなく、下記のページから直接ダウンロードすることも可能です。

リンクをクリックすると、command_line_tools_os_x_mavericks_for_xcode__late_october_2013.dmgというファイルがダウンロードされますので、ダブルクリックにてマウントします。

マウントされたボリュームCommand Line Developer Toolsの中に、Command Line Tools (OS X 10.9).pkgというパッケージファイルが格納されています。名前が両者で異なっていますが、コマンドライン開発ツール(Command Line Developer Tools)が正しい名称と言えるでしょう。

Command Line Tools (OS X 10.9).pkgをダブルクリックすると、インストールが開始されますので指示に従ってください。

コマンドライン開発ツールのインストールにより変化した開発環境について検討する前に、先ほどのCommand Line Tools (OS X 10.9).pkgの内容を見てみます。

パッケージ内容の確認

まず、マウントされたボリュームCommand Line Development Toolsの内容ですが、

$ ls -F1 /Volumes/Command Line Developer Tools/
Command Line Tools (OS X 10.9).pkg
Packages/
$ ls -F1 /Volumes/Command Line Developer Tools/Packages/
CLTools_Executables.pkg
MacOSX10_9_SDK.pkg

Command Line Tools (OS X 10.9).pkgというパッケージファイルに加え、Packagesサブディレクトリの中に、CLTools_Executables.pkgおよびMacOSX10_9_SDK.pkgが存在しています。

  • CLTools_Executables.pkg --- 開発ツールプログラム、開発ツールmanファイル(man1)
  • MacOSX10_9_SDK.pkg --- システムヘッダーファイル、システムコールmanファイル(man2)、Cライブラリ関数manファイル(man3)

開発ツールのプログラムとマニュアルエントリを含むファイルが前者、システムヘッダーファイルとシステムコール/ライブラリ関数のマニュアルエントリを含むファイルが後者になります。

なお、PackagesディレクトリがFinder上で表示されず、"hidden"扱いになっているのは、Mac OS X特有のextended attributesが設定されているためです。

$ ls -alF@ /Volumes/Command Line Developer Tools/
total 1304
drwxr-xr-x 5 wataru staff 238 10 19 08:20 ./
drwxrwxrwt@ 6 root admin 204 11 26 13:39 ../
com.apple.FinderInfo 32
-rw-r--r--@ 1 wataru staff 6148 10 19 06:34 .DS_Store
com.apple.FinderInfo 32
-rw-r--r-- 1 wataru staff 657793 10 19 08:20 Command Line Tools (OS X 10.9).pkg
drwxr-xr-x@ 4 wataru staff 136 10 19 08:18 Packages/
com.apple.FinderInfo 32

extended attributesは、lsコマンドに@オプションを添えると上記例のように可視化できます。上位ディレクトリ(..)、.DS_Storeディレクトリ、Packagesディレクトリには、それぞれcom.apple.FinderInfoアトリビュートに32が設定されており、この結果非表示となります(アトリビュート値の詳細については非公開)。

$ file /Volumes/Command Line Developer Tools/Command Line Tools (OS X 10.9).pkg
/Volumes/Command Line Developer Tools/Command Line Tools (OS X 10.9).pkg: xar archive - version 1
$ file /Volumes/Command Line Developer Tools/Packages/*
/Volumes/Command Line Developer Tools/Packages/CLTools_Executables.pkg: xar archive - version 1
/Volumes/Command Line Developer Tools/Packages/MacOSX10_9_SDK.pkg: xar archive - version 1

fileコマンドで確認すると3つのpkgファイルは、すべてxarアーカイバによる圧縮ファイルであることが分かります。

本ファイルは、xarアーカイバ/gzipアーカイバ/cpioユーティリティーにより、何重にも処理されているため、解析に手間がかかりますが、Mac OS X固有のpkgutilコマンドを用いると、内部のファイル一覧を簡単に参照できます。

$ pkgutil --payload-files /Volumes/Command Line Developer Tools/Packages/CLTools_Executables.pkg|head -n 35
.
./Library
./Library/Developer
./Library/Developer/CommandLineTools
./Library/Developer/CommandLineTools/usr
./Library/Developer/CommandLineTools/usr/bin
./Library/Developer/CommandLineTools/usr/bin/BuildStrings
./Library/Developer/CommandLineTools/usr/bin/CpMac
./Library/Developer/CommandLineTools/usr/bin/DeRez
./Library/Developer/CommandLineTools/usr/bin/GetFileInfo
./Library/Developer/CommandLineTools/usr/bin/MergePef
./Library/Developer/CommandLineTools/usr/bin/MvMac
./Library/Developer/CommandLineTools/usr/bin/ResMerger
./Library/Developer/CommandLineTools/usr/bin/Rez
./Library/Developer/CommandLineTools/usr/bin/RezDet
./Library/Developer/CommandLineTools/usr/bin/RezWack
./Library/Developer/CommandLineTools/usr/bin/SetFile
./Library/Developer/CommandLineTools/usr/bin/SplitForks
./Library/Developer/CommandLineTools/usr/bin/UnRezWack
./Library/Developer/CommandLineTools/usr/share
./Library/Developer/CommandLineTools/usr/share/man
./Library/Developer/CommandLineTools/usr/share/man/man1
./Library/Developer/CommandLineTools/usr/share/man/man1/BuildStrings.1
./Library/Developer/CommandLineTools/usr/share/man/man1/CpMac.1
./Library/Developer/CommandLineTools/usr/share/man/man1/DeRez.1
./Library/Developer/CommandLineTools/usr/share/man/man1/GetFileInfo.1
./Library/Developer/CommandLineTools/usr/share/man/man1/MergePef.1
./Library/Developer/CommandLineTools/usr/share/man/man1/MvMac.1
./Library/Developer/CommandLineTools/usr/share/man/man1/ResMerger.1
./Library/Developer/CommandLineTools/usr/share/man/man1/Rez.1
./Library/Developer/CommandLineTools/usr/share/man/man1/RezDet.1
./Library/Developer/CommandLineTools/usr/share/man/man1/RezWack.1
./Library/Developer/CommandLineTools/usr/share/man/man1/SetFile.1
./Library/Developer/CommandLineTools/usr/share/man/man1/SplitForks.1
./Library/Developer/CommandLineTools/usr/share/man/man1/UnRezWack.1

pkgutilコマンドに--payload-filesオプションを付与すると、内部のファイル一覧が表示されます。上記例では、先頭の35行のみを出力していますが、コマンドファイルは/Library/Developer/CommandLineTools/usr/binディレクトリ、manファイルは/Library/Developer/CommandLineTools/usr/share/manディレクトリに格納されていることが分かります。

$ pkgutil --payload-files /Volumes/Command Line Developer Tools/Packages/CLTools_Executables.pkg|grep "include/"
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/__wmmintrin_aes.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/__wmmintrin_pclmul.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/aarch64_simd.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/altivec.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/ammintrin.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/arm_neon.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/avx2intrin.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/avxintrin.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/bmi2intrin.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/bmiintrin.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/cpuid.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/emmintrin.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/f16cintrin.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/float.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/fma4intrin.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/fmaintrin.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/immintrin.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/iso646.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/limits.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/lzcntintrin.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/mm3dnow.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/mm_malloc.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/mmintrin.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/module.map
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/nmmintrin.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/pmmintrin.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/popcntintrin.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/rtmintrin.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/smmintrin.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/stdalign.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/stdarg.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/stdbool.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/stddef.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/stdint.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/stdnoreturn.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/tgmath.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/tmmintrin.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/unwind.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/varargs.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/wmmintrin.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/x86intrin.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/xmmintrin.h
./Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/xopintrin.h
./Library/Developer/CommandLineTools/usr/include/FlexLexer.h
./Library/Developer/CommandLineTools/usr/lib/llvm-gcc/4.2.1/include/mm_malloc.h
./Library/Developer/CommandLineTools/usr/lib/llvm-gcc/4.2.1/include/stdarg.h
./Library/Developer/CommandLineTools/usr/lib/llvm-gcc/4.2.1/include/varargs.h

システムヘッダーファイルの一覧は、pkgutilコマンド出力の中から、grepで"include/"を検索すれば得られます。

ヘッダーファイルを格納するディレクトリは、下記の3ディレクトリです。

① /Library/Developer/CommandLineTools/usr/lib/clang/5.0/include
② /Library/Developer/CommandLineTools/usr/include
③ /Library/Developer/CommandLineTools/usr/lib/llvm-gcc/4.2.1/include

それぞれのディレクトリ内容は、Xcodeインストール後と同じような構成になっています。

$ pkgutil --payload-files /Volumes/Command Line Developer Tools/Packages/MacOSX10_9_SDK.pkg

詳細は割愛しますが、MacOSX10_9_SDK.pkg内部には、/usr/include/usr/share/man/man2/usr/share/man/man3ディレクトリに格納される、膨大な数のファイル群が存在します。

コマンドライン開発ツールインストール後の環境

コマンドライン開発ツールをインストールすることで、開発環境がどのように変わったのか検討します。

/Library/Developer/CommandLineToolsディレクトリの内容

最初に、コマンドライン開発ツールのホームディレクトリである/Library/Developer/CommandLineToolsの内容を見てみましょう。

$ ls -F /Library/Developer/CommandLineTools/usr/bin
BuildStrings*           dyldinfo*               nasm*
CpMac*                  flex*                   ndisasm*
DeRez*                  flex++*                 nm*
GetFileInfo*            g++@                    nmedit*
MergePef*               gatherheaderdoc*        otool*
MvMac*                  gcc*                    pagestuff*
ResMerger*              gcov*                   projectInfo*
Rez*                    git*                    ranlib@
RezDet*                 git-cvsserver*          rebase*
RezWack*                git-receive-pack@       redo_prebinding*
SetFile*                git-shell*              resolveLinks*
SplitForks*             git-upload-archive@     rpcgen*
UnRezWack*              git-upload-pack*        segedit*
ar*                     gm4*                    size*
as*                     gnumake*                strings*
asa*                    gperf*                  strip*
bison*                  hdxml2manxml*           svn*
c++@                    headerdoc2html*         svnadmin*
c89*                    indent*                 svndumpfilter*
c99*                    install_name_tool*      svnlook*
cc@                     ld*                     svnrdump*
clang*                  lex*                    svnserve*
clang++@                libtool*                svnsync*
cmpdylib*               lipo*                   svnversion*
codesign_allocate*      lldb*                   unifdef*
cpp*                    lorder*                 unifdefall*
ctags*                  m4*                     unwinddump*
ctf_insert*             make*                   what*
dsymutil*               mig*                    xml2man*
dwarfdump*              mkdep*                  yacc*

/Library/Developer/CommandLineTools/usr/binディレクトリには、開発ツールが格納されていますが、Xcodeとは異なり、GitやSubversion (SVN)関連のプログラムも含まれています。

$ ls /Library/Developer/CommandLineTools/usr/share/man/man1
BuildStrings.1                  git-p4.1
CpMac.1                         git-pack-objects.1
DeRez.1                         git-pack-redundant.1
GetFileInfo.1                   git-pack-refs.1
MergePef.1                      git-parse-remote.1
MvMac.1                         git-patch-id.1
ResMerger.1                     git-peek-remote.1
Rez.1                           git-prune-packed.1
RezDet.1                        git-prune.1
RezWack.1                       git-pull.1
SetFile.1                       git-push.1
SplitForks.1                    git-quiltimport.1
UnRezWack.1                     git-read-tree.1
ar.1                            git-rebase.1
as.1                            git-receive-pack.1
asa.1                           git-reflog.1
bison.1                         git-relink.1
c++.1@                          git-remote-ext.1
c89.1                           git-remote-fd.1
c99.1                           git-remote-testgit.1
cc.1@                           git-remote.1
clang++.1@                      git-repack.1
clang.1                         git-replace.1
cmpdylib.1                      git-repo-config.1
codesign_allocate.1             git-request-pull.1
ctags.1                         git-rerere.1
ctf_insert.1                    git-reset.1
dsymutil.1                      git-rev-list.1
dwarfdump.1                     git-rev-parse.1
dyldinfo.1                      git-revert.1
flex++.1                        git-rm.1
flex.1                          git-send-email.1
gatherheaderdoc.1               git-send-pack.1
gcov.1.gz                       git-sh-i18n--envsubst.1
git-add.1                       git-sh-i18n.1
git-am.1                        git-sh-setup.1
git-annotate.1                  git-shell.1
git-apply.1                     git-shortlog.1
git-archimport.1                git-show-branch.1
git-archive.1                   git-show-index.1
git-bisect.1                    git-show-ref.1
git-blame.1                     git-show.1
git-branch.1                    git-stage.1
git-bundle.1                    git-stash.1
git-cat-file.1                  git-status.1
git-check-attr.1                git-stripspace.1
git-check-ignore.1              git-submodule.1
git-check-ref-format.1          git-svn.1
git-checkout-index.1            git-symbolic-ref.1
git-checkout.1                  git-tag.1
git-cherry-pick.1               git-tar-tree.1
git-cherry.1                    git-unpack-file.1
git-citool.1                    git-unpack-objects.1
git-clean.1                     git-update-index.1
git-clone.1                     git-update-ref.1
git-column.1                    git-update-server-info.1
git-commit-tree.1               git-upload-archive.1
git-commit.1                    git-upload-pack.1
git-config.1                    git-var.1
git-count-objects.1             git-verify-pack.1
git-credential-cache--daemon.1  git-verify-tag.1
git-credential-cache.1          git-web--browse.1
git-credential-store.1          git-whatchanged.1
git-credential.1                git-write-tree.1
git-cvsexportcommit.1           git.1
git-cvsimport.1                 gitremote-helpers.1
git-cvsserver.1                 gitweb.1
git-daemon.1                    gm4.1
git-describe.1                  gnumake.1
git-diff-files.1                gperf.1
git-diff-index.1                hdxml2manxml.1
git-diff-tree.1                 headerdoc.1
git-diff.1                      headerdoc2html.1
git-difftool.1                  indent.1
git-fast-export.1               install_name_tool.1
git-fast-import.1               ld.1
git-fetch-pack.1                lex.1
git-fetch.1                     libtool.1
git-filter-branch.1             lipo.1
git-fmt-merge-msg.1             lldb.1
git-for-each-ref.1              lorder.1
git-format-patch.1              m4.1
git-fsck-objects.1              make.1
git-fsck.1                      mig.1
git-gc.1                        migcom.1
git-get-tar-commit-id.1         mkdep.1
git-grep.1                      mpgl.1
git-hash-object.1               nasm.1
git-help.1                      ndisasm.1
git-http-backend.1              nm.1
git-http-fetch.1                nmedit.1
git-http-push.1                 otool.1
git-imap-send.1                 pagestuff.1
git-index-pack.1                projectInfo.1
git-init-db.1                   ranlib.1
git-init.1                      rebase.1
git-instaweb.1                  redo_prebinding.1
git-log.1                       resolveLinks.1
git-lost-found.1                rpcgen.1
git-ls-files.1                  segedit.1
git-ls-remote.1                 size.1
git-ls-tree.1                   strings.1
git-mailinfo.1                  strip.1
git-mailsplit.1                 svn.1
git-merge-base.1                svnadmin.1
git-merge-file.1                svndumpfilter.1
git-merge-index.1               svnlook.1
git-merge-one-file.1            svnrdump.1
git-merge-tree.1                svnsync.1
git-merge.1                     svnversion.1
git-mergetool--lib.1            unifdef.1
git-mergetool.1                 unifdefall.1
git-mktag.1                     unwinddump.1
git-mktree.1                    what.1
git-mv.1                        xcrun.1
git-name-rev.1                  xml2man.1
git-notes.1                     yacc.1

/Library/Developer/CommandLineTools/usr/share/man/man1ディレクトリも同様に、開発ツール以外に、Git/Subversion関連のマニュアルエントリが多数追加されています。

先程、CLTools_Executables.pkgパッケージ中で確認した、下記のヘッダーファイル格納ディレクトリも確認しておきましょう。

  • /Library/Developer/CommandLineTools/usr/lib/clang/5.0/include
  • /Library/Developer/CommandLineTools/usr/include
  • /Library/Developer/CommandLineTools/usr/lib/llvm-gcc/4.2.1/include
$ ls -F /Library/Developer/CommandLineTools/usr/lib/clang/5.0/include
__wmmintrin_aes.h       fmaintrin.h             stdarg.h
__wmmintrin_pclmul.h    immintrin.h             stdbool.h
aarch64_simd.h          iso646.h                stddef.h
altivec.h               limits.h                stdint.h
ammintrin.h             lzcntintrin.h           stdnoreturn.h
arm_neon.h              mm3dnow.h               tgmath.h
avx2intrin.h            mm_malloc.h             tmmintrin.h
avxintrin.h             mmintrin.h              unwind.h
bmi2intrin.h            module.map              varargs.h
bmiintrin.h             nmmintrin.h             wmmintrin.h
cpuid.h                 pmmintrin.h             x86intrin.h
emmintrin.h             popcntintrin.h          xmmintrin.h
f16cintrin.h            rtmintrin.h             xopintrin.h
float.h                 smmintrin.h
fma4intrin.h            stdalign.h
$ ls -F /Library/Developer/CommandLineTools/usr/include
FlexLexer.h
$ ls -F /Library/Developer/CommandLineTools/usr/lib/llvm-gcc/4.2.1/include
mm_malloc.h stdarg.h varargs.h

システムヘッダーファイル群の配置は、上記の通りとなっています。

/usr/includeディレクトリの登場

X11/        bin/        lib/        sbin/        standalone/
X11R6@      include/    libexec/    share/

コマンドライン開発ツールで最も大きな変化は、MacOSX10_9_SDK.pkgパッケージから展開された、/usr/includeディレクトリです。

$ ls -C /usr/include
AssertMacros.h           ifaddrs.h               ranlib.h
Availability.h          inttypes.h              readline/
AvailabilityInternal.h  iso646.h                readpassphrase.h
AvailabilityMacros.h    kern/                   regex.h
Block.h                 krb5/                   removefile.h
CommonCrypto/           krb5.h                  resolv.h
ConditionalMacros.h     langinfo.h              rpc/
MacTypes.h              launch.h                rpcsvc/
NSSystemDirectories.h   lber.h                  rune.h
TargetConditionals.h    lber_types.h            runetype.h
Xplugin.h               ldap.h                  sandbox.h
_locale.h               ldap_cdefs.h            sasl/
_structs.h              ldap_features.h         sched.h
_types/                 ldap_schema.h           search.h
_types.h                ldap_utf8.h             secure/
_wctype.h               ldif.h                  security/
_xlocale.h              libc.h                  semaphore.h
aio.h                   libcharset.h            servers/
aliasdb.h               libexslt/               setjmp.h
alloca.h                libgen.h                sgtty.h
apache2/                libkern/                signal.h
apr-1/                  libproc.h               slapi-plugin.h
ar.h                    libunwind.h             spawn.h
architecture/           libxml2/                sqlite3.h
arpa/                   libxslt/                sqlite3ext.h
asl.h                   limits.h                stab.h
assert.h                localcharset.h          standards.h
bitstring.h             locale.h                stdbool.h
bootparams.h            mach/                   stddef.h
bsm/                    mach-o/                 stdint.h
bzlib.h                 mach_debug/             stdio.h
c++/                    machine/                stdlib.h
cache.h                 malloc/                 strhash.h
cache_callbacks.h       math.h                  string.h
checkint.h              membership.h            stringlist.h
com_err.h               memcached/              strings.h
complex.h               memory.h                struct.h
copyfile.h              menu.h                  sys/
cpio.h                  miscfs/                 sysexits.h
crt_externs.h           module.map              syslog.h
ctype.h                 monetary.h              tar.h
cups/                   monitor.h               tcl.h@
curl/                   mpool.h                 tclDecls.h@
curses.h                nameser.h               tclPlatDecls.h@
cxxabi.h                nc_tparm.h              tclTomMath.h@
db.h                    ncurses.h@              tclTomMathDecls.h@
default_pager/          ncurses_dll.h           term.h
device/                 ndbm.h                  term_entry.h
dirent.h                net/                    termcap.h
disktab.h               net-snmp/               termios.h
dispatch/               netdb.h                 tic.h
dlfcn.h                 netinet/                tidy/
dns.h                   netinet6/               time.h
dns_sd.h                netkey/                 timeconv.h
dns_util.h              nfs/                    tk.h@
dtrace.h                nl_types.h              tkDecls.h@
editline/               nlist.h                 tkIntXlibDecls.h@
err.h                   notify.h                tkMacOSX.h@
errno.h                 notify_keys.h           tkPlatDecls.h@
eti.h                   ntsid.h                 ttyent.h
execinfo.h              objc/                   tzfile.h
expat.h                 odmodule/               ucontext.h
expat_external.h        openssl/                ulimit.h
fcntl.h                 os/                     unctrl.h
fenv.h                  panel.h                 unistd.h
ffi/                    paths.h                 unwind.h
fmtmsg.h                pcap/                   util.h
fnmatch.h               pcap-bpf.h              utime.h
form.h                  pcap-namedb.h           utmp.h
fsproperties.h          pcap.h                  utmpx.h
fstab.h                 pexpert/                uuid/
fts.h                   php/                    vfs/
ftw.h                   poll.h                  vis.h
get_compat.h            printerdb.h             vproc.h
gethostuuid.h           printf.h                wchar.h
getopt.h                profile.h               wctype.h
glob.h                  protocols/              wordexp.h
grp.h                   pthread.h               xar/
gssapi/                 pthread_impl.h          xlocale/
gssapi.h                pthread_spis.h          xlocale.h
hfs/                    pwd.h                   xpc/
histedit.h              python2.5@              zconf.h
i386/                   python2.6@              zlib.h
iconv.h                 python2.7@

システムヘッダーファイル一式が/usr/include下に用意されています。Xcodeのシステムヘッダーファイルは、/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include内に用意されていましたが、両者の間に違いはあるのでしょうか?

$ diff -r /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include /usr/include
Only in /usr/include/: python2.5
Only in /usr/include/: python2.6
Only in /usr/include/: python2.7
$

diffコマンドに-rオプションを添えると、ふたつのディレクトリ内にあるファイル群を再帰的に比較することができます。

結果を見ると、python関連のシンボリックリンク3つが/usr/includeディレクトリに存在する以外、両者のファイル群に違いはありませんでした。

マニュアルエントリ検索パス

$ man -w
/usr/share/man:/Applications/Xcode.app/Contents/Developer/usr/share/man:/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/share/man

マニュアルエントリの検索パスは、Xcodeインストール後に良く似ており、新しくインストールされた、/Library/Developer/CommandLineTools/usr/share/manディレクトリは含まれていません。

しかし、/usr/share/man/man3ディレクトリにはCライブラリ関数のマニュアルエントリ一式が新しく格納されています。

$ diff -r /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/share/man/man3 /usr/share/man/man3
$

先程と同様に、/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/share/man/man3と/usr/share/man/man3ディレクトリの内容を比較すると、両者は完全に一致していました。

ヘッダーファイル探索パス

$ cat /dev/null|cc -E -Wp,-v -
clang -cc1 version 5.0 based upon LLVM 3.3svn default target x86_64-apple-darwin13.0.0
ignoring nonexistent directory "/usr/local/include"
#include "..." search starts here:
#include search starts here:
 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/5.0/include
 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include
 /usr/include
 /System/Library/Frameworks (framework directory)
 /Library/Frameworks (framework directory)
End of search list.
# 1 ""
# 1 "" 1
# 1 "" 3
# 162 "" 3
# 1 "" 1
# 1 "" 2
# 1 "" 2

ヘッダーファイル探索パスも同様であり、新しくインストールされたディレクトリが含まれていません。

clangを実行するとどちらの実体プログラムが起動されるのか?

ここまで見てきたように、コマンドライン開発ツールをインストールしたにもかかわらず、開発環境はXcodeの支配を強く受けているようです。

となると、シェル上でclangを実行した際には、下記どちらの本体プログラムが起動されるのでしょうか?

● /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang
● /Library/Developer/CommandLineTools/usr/bin/clang

その答えを知るためには、高機能のトレーサーdtraceを利用する必要があります。解析結果は次のようになります。

$ sudo dtrace -n "syscall::execve:entry / execname == "clang" / { trace(copyinstr(arg0)) }" > clang.log &
[1] 8057
$ dtrace: description 'syscall::execve:entry ' matched 1 probe

$ clang -v
Apple LLVM version 5.0 (clang-500.2.79) (based on LLVM 3.3svn)
Target: x86_64-apple-darwin13.0.0
Thread model: posix
$ fg
sudo dtrace -n "syscall::execve:entry / execname == "clang" / { trace(copyinstr(arg0)) }" > clang.log
^C
$ cat clang.log 
CPU     ID                    FUNCTION:NAME
  2    281                     execve:entry   /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang

dtrace利用にあたっては、まずトレースする条件を指定する必要があります。

dtrace -n "syscall::execve:entry / execname == "clang" / { trace(copyinstr(arg0)) }" > clang.log &

dtraceコマンドの詳細は省きますが、"clangプロセス中でシステムコールexecveが実行された時だけ、記録する"よう指示しています。最後に&を後置することで、バックグラウンドジョブとして稼働させ、標準出力へのメッセージはclang.logというファイルに記録します。

dtraceを起動した後にclangを実行し、次にfgコマンドでバックグラウンドプロセスをフォアグラウンドプロセスに戻し、^Cで強制終了させます。

記録されたclang.logを参照すると、起動されたプログラムはXcodeのディレクトリに含まれているclangであり、期待された/Library/Developer/CommandLineTools/usr/bin/clangではありませんでした。

xcode-selectによるアクティブ開発ディレクトリの切り替え

なぜ、このような"齟齬"が起きるのでしょうか?

実は、Mac OS Xにはアクティブ開発ディレクトリ(active developer directory)という概念があり、目的に応じて開発環境を切り替えることができるのですが、Xcodeに続いてコマンドライン開発ツールをインストールした場合、なぜか正しく切り替わっていないのです。

切り替えの対象には、下記の4項目が含まれます(ライブラリについては本稿では割愛)。

  1. スタブプログラムから起動される本体プログラム
  2. システムヘッダーファイル群
  3. 基本ライブラリ
  4. マニュアルエントリ

アクティブ開発ディレクトリの切り替え

現在のアクティブ開発ディレクトリを確認するには、"xcode-select -p"という命令を実行します。

$ xcode-select -p
/Applications/Xcode.app/Contents/Developer

Xcodeに続いてコマンドライン開発ツールをインストールした直後は、/Applications/Xcode.app/Contents/Developerが設定されています(Xcodeインストール後の状態)。

UNIX styleの開発を行う場合は、Xcode環境のままでは困りますので、本来のアクティブ開発ディレクトリは、/Library/Developer/CommandLineToolsでなければなりません。

アクティブ開発ディレクトリを変更する場合は、xcode-selectコマンドの-s (Switch)オプションを用います。

$ sudo xcode-select -s /Library/Developer/CommandLineTools
Password:
$ xcode-select -p
/Library/Developer/CommandLineTools

切り替えには管理者権限が必要なので、sudoを前置してください。

切り替え直後に、再度アクティブ開発ディレクトリを確認すると、指定通り/Library/Developer/CommandLineToolsへ変化しています。

この状態で、xcrunコマンドを使い、clangの実体プログラムを探してみます。

$ xcrun --find clang
/Library/Developer/CommandLineTools/usr/bin/clang

/usr/bin/clangから起動される実体プログラムは、/Library/Developer/CommandLineTools/usr/bin/clangに切り替わっています。

念のため、dtraceでclangの内部動作を見ておきましょう。

$ sudo dtrace -n "syscall::execve:entry / execname == "clang" / { trace(copyinstr(arg0)) }" > clang.log &
[1] 8082
$ dtrace: description 'syscall::execve:entry ' matched 1 probe

$ clang -v
Apple LLVM version 5.0 (clang-500.2.79) (based on LLVM 3.3svn)
Target: x86_64-apple-darwin13.0.0
Thread model: posix
$ fg
sudo dtrace -n "syscall::execve:entry / execname == "clang" / { trace(copyinstr(arg0)) }" > clang.log
^C
$ cat clang.log 
CPU     ID                    FUNCTION:NAME
  0    281                     execve:entry   /Library/Developer/CommandLineTools/usr/bin/clang

execveシステムコールを通じて、コマンドライン開発ツールディレクトリのclangが起動されていることを確認できました。

アクティブ開発ディレクトリ切り替えの効果について、clangプログラムでの実例を上図にまとめておきます。

xcode-selectにより、この他、マニュアルエントリやインクルードヘッダーファイルの探索パスも切り替わっています。

Mavericksにおける最終的なUNIX styleの開発環境

アクティブ開発ディレクトリを/Library/Developer/CommandLineToolsに切り替えた状態で、最終の開発環境を確認しておきましょう。

マニュアルエントリ探索パス

$ man -w
/usr/share/man:/Library/Developer/CommandLineTools/usr/share/man

マニュアルエントリの探索パスは、/usr/share/manディレクトリが最初に検索され、/Library/Developer/CommandLineTools/usr/share/manディレクトリが最後の検索対象となります。

  1. /usr/share/man
  2. /Library/Developer/CommandLineTools/usr/share/man

ヘッダーファイル探索パス

ヘッダーファイルの探索パスについて調べる前に、ユーザヘッダーファイルを格納するための/usr/local/includeディレクトリを作成しておきます。

$ sudo mkdir  -p /usr/local/include

/usrディレクトリへの書き込みには、管理者権限が必要なためsudoを前置する必要があります。

$ cat /dev/null|cc -E -Wp,-v -
clang -cc1 version 5.0 based upon LLVM 3.3svn default target x86_64-apple-darwin13.0.0
#include "..." search starts here:
#include <...> search starts here:
 /usr/local/include
 /Library/Developer/CommandLineTools/usr/bin/../lib/clang/5.0/include
 /Library/Developer/CommandLineTools/usr/include
 /usr/include
 /System/Library/Frameworks (framework directory)
 /Library/Frameworks (framework directory)
End of search list.
# 1 "<stdin>"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 162 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "<stdin>" 2

Cプリプロセッサは最初に/usr/local/includeディレクトリを検索し、見つかれなければ下記の順番で検索を続けます(フレームワークディレクトリはUNIX styleでは無関係のため割愛)。

  1. /usr/local/include
  2. /Library/Developer/CommandLineTools/usr/lib/clang/5.0/include
  3. /Library/Developer/CommandLineTools/usr/include
  4. /usr/include

探索対象の最初が/usr/local/includeディレクトリ、最後が/usr/includeディレクトリである点に留意してください。

この仕組みにより、オリジナルのstdio.hを/usr/local/includeディレクトリに置いておけば、/usr/include/stdio.hに代わり読み込ませることなどが可能になります。

また、stdint.hやstddef.hなどシステムに強く依存するシステムヘッダーファイルは、/usr/includeディレクトリにも用意されてはいますが、実際には探索順序に従い、/Library/Developer/CommandLineTools/usr/lib/clang/5.0/includeディレクトリ内のファイルが読み込まれます。

$ ls -F /Library/Developer/CommandLineTools/usr/lib/clang/5.0/include
__wmmintrin_aes.h       fmaintrin.h             stdarg.h
__wmmintrin_pclmul.h    immintrin.h             stdbool.h
aarch64_simd.h          iso646.h                stddef.h
altivec.h               limits.h                stdint.h
ammintrin.h             lzcntintrin.h           stdnoreturn.h
arm_neon.h              mm3dnow.h               tgmath.h
avx2intrin.h            mm_malloc.h             tmmintrin.h
avxintrin.h             mmintrin.h              unwind.h
bmi2intrin.h            module.map              varargs.h
bmiintrin.h             nmmintrin.h             wmmintrin.h
cpuid.h                 pmmintrin.h             x86intrin.h
emmintrin.h             popcntintrin.h          xmmintrin.h
f16cintrin.h            rtmintrin.h             xopintrin.h
float.h                 smmintrin.h
fma4intrin.h            stdalign.h

同ディレクトリの中には、43個のヘッダーファイルが格納されています。

$ diff -rq /Library/Developer/CommandLineTools/usr/lib/clang/5.0/include /usr/include|grep -v Only
Files /Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/iso646.h and /usr/include/iso646.h differ
Files /Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/limits.h and /usr/include/limits.h differ
Files /Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/module.map and /usr/include/module.map differ
Files /Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/stdbool.h and /usr/include/stdbool.h differ
Files /Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/stddef.h and /usr/include/stddef.h differ
Files /Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/stdint.h and /usr/include/stdint.h differ
Files /Library/Developer/CommandLineTools/usr/lib/clang/5.0/include/unwind.h and /usr/include/unwind.h differ

43個のファイルについて、/usr/includeディレクトリの同名ファイルと比較してみると、一部内容が異なるファイルが存在することが分かります。

  • iso646.h
  • limits.h
  • stdbool.h
  • stddef.h
  • stdint.h
  • unwind.h

システムヘッダーファイルの内容を参照する場合、上記6個のファイルについては、/usr/includeディレクトリではなく、/Library/Developer/CommandLineTools/usr/lib/clang/5.0/includeディレクトリ内のファイルにアクセスする必要があります。

本体コマンドの探索パス

先程の繰り返しになりますが、再度紹介しておきます。

$ xcrun --find clang
/Library/Developer/CommandLineTools/usr/bin/clang

/usr/binディレクトリに存在するスタブプログラムから呼び出される本体プログラムの探索は、/Library/Developer/CommandLineTools/usr/binディレクトリで実施されます。

最後に

Mavericks上でプログラム開発を行う際には、Xcode統合開発環境上で行うのか、UNIX styleの開発を行うのか、その目的に応じて、適切にアクティブ開発ディレクトリを切り替える必要があります。

開発ツール(特にGit, SVN)やマニュアルエントリ、システムヘッダーファイル回りで不具合が生じた際は、本稿で紹介したテクニックを活用し、原因究明に役立てて頂ければ幸いです。

posted by Wataru Nishida.