C言語
印刷用ページへ

C言語におけるポインタ型変数の宣言

ポインタはこれまでC言語の鬼門と呼ばれてきましたが、それはC言語設計者が書いた一文が誘因となっています。本稿ではポインタを巡るC言語の問題点と、ポインタ型を意識してコーディングする当社の方針について解説します。

整数型変数の割り当てアドレス

ポインタについて検討する前に、整数型変数のメモリー領域への割り当て状況を見てみましょう。

#include <stdio.h>

int a = 0x12345678;

int main() {
    printf("a = 0x%X\n", a);
    return 0;
}

int.cは、3行目で整数型変数aを定義し(0x12345678で初期化)、その値を6行目で表示するプログラムです。

$ cc -Wall -o int int.c
$ ./int
a = 0x12345678

意図通り動作しています。次に、変数aの格納アドレスを併せて表示させるように手を加えます。

#include <stdio.h>

int a = 0x12345678;

int main() {
    printf("a = 0x%X\n", a);
    printf("a @ %p\n", &a);
    return 0;
}

int_address.cは、新たに7行目にてアドレス演算子&を用いて、変数aの格納開始アドレスを表示させます(%pはポインタの値を表示するための指定子)。

$ cc -Wall -Wl,-no_pie -o int_address int_address.c
$ ./int_address
a = 0x12345678
a @ 0x100001018

Mac OS Xでの実行例を示します。Max OS Xでは、プロセス起動時に割り当てアドレスをランダムに設定する機能がデフォルトになっているため、リンカーに-no_pieオプションを渡し、固定アドレスを使用するように指示します。変数aの格納開始アドレスは、0x10001018番地であることが分かります。

$ cc -Wall -o int_address int_address.c
$ ./int_address
a = 0x12345678
a @ 0x601040

Ubuntuでの実行例を示します。Ubuntuでは割り当てアドレスのランダム化は行われないため、オプションなしでも常に同一のアドレスが表示されます。本例では、変数aは0x601040番地に割り当てられています。

文字型変数の割り当てアドレス

次に、文字型変数の割り当てをchar_address.cで検証します。

#include <stdio.h>

char b = '*';

int main() {
    printf("b = 0x%X\n", b);
    printf("b @ %p\n", &b);
    return 0;
}

int_address.cとの大きな違いは、文字型変数bを定義している3行目のみです。

$ cc -Wl,-no_pie -o char_address char_address.c
$ ./char_address
b = 0x2A
b @ 0x100001018

Mac OS Xでの実行例ですが、文字型変数bの格納開始アドレスは、先程の整数型変数aと同一である点に注意してください。

$ cc -Wall -o char_address char_address.c
$ ./char_address
b = 0x2A
b @ 0x601040

Ubuntuでも同一アドレスに割り当てられていることが分かります。

変数の内容は整数型と文字型で異なるが、割り当てアドレスは同一」という事実は、C言語を操る上でとても大切な点であり、ポインタの本質もここにあります。

sizeof演算子

次に、変数のサイズについて検討します。変数がメモリ領域上に占めるサイズは、変数の型によって異なるため、C言語にはsizeof演算子が用意されています。これは指定された型もしくは変数のバイト長を返す演算子です。

#include <stdio.h>

int main() {
    printf("sizeof(char) = %zd\n", sizeof(char));
    printf("sizeof(int) = %zd\n", sizeof(int));
    return 0;
}

sizeof.cは、文字型変数と整数型変数のサイズを表示するプログラムです(指定子%zは警告抑制のために必要)。

$ cc -Wall -o sizeof sizeof.c
$ ./sizeof
sizeof(char) = 1
sizeof(int) = 4

文字型変数のメモリ格納サイズは1バイト、整数型変数のサイズは4バイトであることが分かります。

unionによるメモリ内容の読み換え

続いて、C言語に特徴的な概念である共用体 (union)に進みます。今回は共用体を活用した例題として、dump_union.cというプログラムを用意しました。

#include <stdio.h>

union {
    int a;
    char b;
    unsigned char c[ 4 ];
} u;

void dump(void) {
    int i;

    printf("%p : ", u.c);
    for (i = 0; i < 4; i++)
        printf("%02X ", u.c[ i ]);
    printf("\n");
}

int main() {
    printf("sizeof(u) = %zd\n", sizeof(u));
    printf("u.a @ %p\n", &u.a);
    printf("u.b @ %p\n", &u.b);
    printf("u.c @ %p\n", u.c);
    printf("\n");

    dump();
    printf("u.a = 0x%X : u.b = 0x%02X\n\n", u.a, u.b);

    u.a = 0x12345678;
    dump();
    printf("u.a = 0x%X : u.b = 0x%02X\n\n", u.a, u.b);

    u.b = '*';
    dump();
    printf("u.a = 0x%X : u.b = 0x%02X\n\n", u.a, u.b);

    return 0;
}

3〜7行で共用体uを定義していますが、そのメンバとして整数型変数a、文字型変数bおよび文字型変数配列cを宣言しています。共用体では、複数メンバの中で最大のものに合わせてサイズが設定されます。今回は、文字型(1バイト)、整数型(4バイト)、文字型配列(4バイト)ですから、共用体uのサイズは最大値である4バイトとなります。

9〜16行のdumpは、共用体メンバcの内容を4バイト分出力するための関数です。

19行で共用体uのバイトサイズ、続く20〜22行ですべての共用体メンバの格納開始アドレスを表示します。

25行で初期化された共用体uの内容をダンプし、

28行では、整数型変数メンバaに0x12345678を格納し、再びdump関数を実行、

32行で、文字型変数メンバであるbにアスタリスクを格納し、3回目のdump関数を実行します。

$ cc -Wall -Wl,-no_pie -o dump_uion dump_union.c
$ ./dump_uion
sizeof(u) = 4
u.a @ 0x100001018
u.b @ 0x100001018
u.c @ 0x100001018

0x100001018 : 00 00 00 00
u.a = 0x0 : u.b = 0x00

0x100001018 : 78 56 34 12
u.a = 0x12345678 : u.b = 0x78

0x100001018 : 2A 56 34 12
u.a = 0x1234562A : u.b = 0x2A

Mac OS X上での実行結果を示します。

共用体uのサイズは予想通り4バイトでした。さらに、共用体uのメンバa、b、cの格納開始アドレスは、すべて0x100001018番地になっており、同一のメモリ開始領域に3つのメンバが割り当てられていることが分かります。

共用体uは未初期化状態で定義されているため、C言語の仕様からプロセス起動時にその中身はゼロで初期化されます。この仕様は、最初のdump関数を実行した際に、メンバcの配列内容がすべてゼロであることからも、確認できます。

次に、メンバaに0x12345678を設定した後にdump関数を実行すると、4バイトの下位から順番に0x78, 0x56, 0x34, 0x12が格納されていることが分かります。x86はlittle-endianのプロセッサであるため、このような順序になっています。ここで、メンバbの値が0x78になっている点に着目してください。

最後に、メンバbにアスタリスク(ASCIIコード 0x2A)を設定すると、共用体uの中身は先頭だけが0x2Aとなり、残りは先程の"残骸"である0x56, 0x34, 0x12が続いています。コンパイラは変数bが文字型(1バイト長)であるため、格納開始アドレス0x100001018番地の内容のみを書き換えています(続く3バイトには関与せず)。この結果、メンバaを評価すると今度は0x1234562Aとなります。

以上の実験結果から、共用体が意味するところをまとめると次のようになります。

  • 共用体のすべてのメンバの格納開始アドレスは同一である。
  • コンパイラは共用体メンバの"型に応じて"格納開始アドレスから続くバイト列の内容を解釈する。

ポインタも共用体と同様に、データの格納開始アドレスと型に応じて処理を行います。

教科書的ポインタ

それではポインタを取り扱ってみましょう。次は、ポインタ操作を含む教科書的なプログラムpointer.cです。

#include <stdio.h>

int a = 0x12345678;
int *p;

int main() {
    p = &a;
    printf("a @ %p\n", &a);
    printf("p @ %p\n", &p);
    printf("p = %p\n", p);
    return 0;
}

3行目で整数型変数a、4行目で整数を指すポインタpを定義しています。

ここではpを定義する際に、教科書的にアスタリスクを変数名に前置していますが、この表記方法の根拠はC言語設計者であるKernighan and Rithcieの著作に書かれている、次の一文に基づいています(THE C PROGRAMMING LANGUAGE, 2nd Ed, p93, 1988)。

A pointer is a variable that contains the address of a variable.

文章全体を直訳すれば、"ポインタはある変数のアドレスを保持する変数である"となりますが、明らかに意味不明瞭な記載です。本来、ポインタとポインタ型変数は全く異なる概念なのですが、両者を混同しているこの一文は、古今東西多くの人々に混乱をもたらしてきました。

同文で重要な点は、C言語設計者達が"ポインタは変数である"と明記していることにあります。このため、"ポインタが通常の変数とは異なることを明示するため、変数名にアスタリスクを前置する"という解釈を取ったのです。

そしてまた、ポインタが変数であるということは、"ポインタ型変数は存在しない"ことも意味します。ポインタは通常の変数とは異なるという先程の大前提に反してしまうからです。

ひとまず、言語設計者の解釈でコーディングしたpointer.cで実験してみましょう。

7行目で&演算子を使い変数aの格納開始アドレスをポインタpにセット、8行目で変数aの格納アドレス、9行目でポインタp自身の格納アドレス、最後にポインタpの内容を出力します。

$ cc -Wall -Wl,-no_pie -o pointer pointer.c
$ ./pointer
a @ 0x100001018
p @ 0x100001020
p = 0x100001018

pointer.cは文法的に正しいプログラムですから、コンパイルは問題なく成功します。

Mac OS Xでの実行例ですが、変数aの格納アドレスは0x100001018番地、ポインタpの格納アドレスは8バイト飛んで0x100001020番地、そしてポインタpの内容は0x100001018番地でした。

後で述べますが、変数aとポインタpの格納アドレスに8バイトの差がある理由は、64ビット環境ではポインタが8バイトの長さを持っているからです。p = &aの実行により、ポインタpの内容は、確かに変数aの格納アドレスと同じ値になっています。

ポインタもしくはポインタ型変数のサイズ

続いて、ポインタのサイズを確認するpointer_size.cへと進みます。

#include <stdio.h>

int a;
int *b, c;
int* d, e;

int main() {
    printf("sizeof(a) = %zd\n", sizeof(a));
    printf("sizeof(b) = %zd\n", sizeof(b));
    printf("sizeof(c) = %zd\n", sizeof(c));
    printf("sizeof(d) = %zd\n", sizeof(d));
    printf("sizeof(e) = %zd\n", sizeof(e));
    return 0;
}

内容はsizeof演算子を用いて、様々な変数のバイトサイズを表示するものですが、このプログラムで注目すべきは4行目と5行目にあります。

4行目は、C言語設計者の解釈に基づいた"整数型変数へのポインタb"と整数型変数cを定義しています。

5行目は、教科書に反してアスタリスクを型名側へ後置しています。この表記が意味したいところは、"整数へのポインタ型変数dとe"を定義するというものですが、その背景には次のような解釈があります。

  • 通常の変数とポインタを区別せず、同じ変数と考える
  • ポインタ型変数はポインタを格納する変数である
  • ポインタは格納開始アドレスと格納データのを保持する
  • ポインタの型には文字ポインタ型、整数ポインタ型、構造体ポインタ型、関数ポインタ型などがある

この内容であれば表現と解釈の間に矛盾は生じません。さて、結果はどうなるでしょうか?

$ cc -Wall -o pointer_size pointer_size.c
$ ./pointer_size
sizeof(a) = 4
sizeof(b) = 8
sizeof(c) = 4
sizeof(d) = 8
sizeof(e) = 4

ポインタbのサイズが8バイト(64ビット環境の場合)、整数型変数aおよびcのサイズが4バイトであるのは、予想通りです。

問題は、"int*を整数ポインタ型"と解釈して宣言した変数dとeのサイズです。変数dは8バイト長ですが、変数eは4バイト長と整数型のサイズしかありません。コンパイラは言語設計者の意図通り、"int* d, e"を"int *d, e"と強制的に解釈したことになります。

それではC言語には、ポインタ型は存在しないのでしょうか?

typedefによるポインタ型の誕生

実は、ポインタ型は存在します。ただし、typedefという小技を使う必要があるのですが。

#include <stdio.h>

typedef int* INT_POINTER;
INT_POINTER a, b;

int main() {
    printf("sizeof(a) = %zd\n", sizeof(a));
    printf("sizeof(b) = %zd\n", sizeof(b));
    return 0;
}

ここに、typedef_pointer.cというプログラムを用意しました。3行目でtypedef機能を用いて、整数へのポインタ型であるINT_POINTER型を新たに定義しています。

4行目でINT_POINTER型変数aとbを定義していますが、それぞれのサイズは果たしていくらになるでしょうか?

$ cc -Wall -o typedef_pointer typedef_pointer.c
$ ./typedef_pointer
sizeof(a) = 8
sizeof(b) = 8

結果はご覧の通り、どちらの変数もサイズは8バイト。つまり、a/bは共に"ポインタ型変数"として意図通りにコンパイルされています。

ポインタ型はC言語仕様上存在しないが、typedefを使えば定義できる

ポインタ型はないとも言えるし、あるとも言える。つまり、ポインタに関してC言語は論理的に破綻しているのです。

オーバーシー・パブリッシング著作物でのポインタ型の取扱い

ポインタを巡る混乱の最大の原因は"A pointer is a variable."という一文にあり、この混迷を打開するためにはポインタ型の導入が不可欠であると、筆者は考えています。

残念ながら、現状ではポインタ型は許されておらず、ポインタ型を意識したコーディングを行うためには、typedefの力を借りなければなりません。

大規模なプログラム中で繰り返し登場する場合はtypedefが有効ですが、小さな実例程度のプログラムでは、かえって可読性が下がることにもつながります。このような場合、当社の記事中では"単一の変数/メンバに対してポインタ型を意識した定義/宣言"を行います。

例えば、符号無し文字型変数へのポインタを保持する変数は、

unsigned char* p;

と定義し、同じ型を持つ複数の変数を続けて定義する場合は、

unsigned char* p;
unsigned char* q;

のように複数行で記載します。

unsigned char* p, q;

とすれば、既に述べた通りプログラマの意図を外れたコードが生成されてしまいますので、この点はくれぐれもご注意ください。

当社は教育的視点に配慮し、上記のリスクよりも、ポインタ解釈の整合性を優先しています。

posted by Wataru Nishida.
開発環境
印刷用ページへ

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.
開発環境
印刷用ページへ

Mac OS X MavericksにおけるFTDIシリアルポートドライバの取扱い

Mac OS X Mavericks上で、FTDI社USB-シリアル変換IC用のシリアルポートドライバをアンロードする方法について解説します。

ブレッドボードI/Oプログラミングで使用する、FTDI社USB-FIFO変換IC用の、D2XXライブラリはUSB-シリアル変換IC用ドライバが存在すると正常に動作しません

このため、USB-FIFO変換ICを接続しD2XXライブラリ経由でプログラム制御する場合は、あらかじめUSB-シリアル変換IC用ドライバをアンロードしておく必要があります。

FTDI社 仮想シリアルポート (Virtual COM Port: VCP)ドライバ

FTDI社は自社が販売しているUSB-シリアル変換IC向けに、仮想シリアルポート(Virtual Communication Port: VCP)ドライバを提供しています。

VCPドライバの実体は、USB-シリアル変換ICをOSにシリアルポートとして認識させるための、カーネル拡張モジュール(KEXT: Kernel EXTernal module)です。

USB-シリアル変換ICが接続されると、自動的に対応するカーネル拡張モジュールがロードされ、アプリケーションソフトウェアから"本物のシリアルポート"としてアクセスできるようになります。

Mac OS X上では、従来FTDI製VCPドライバ(別途インストールが必要)が利用されてきましたが、MavericksではApple純正のUSB-FTDIドライバが最初から用意されています。

本稿では、両者を統一して"FTDIシリアルポートドライバ"と表記することとします。

Mac OS Xにおけるカーネル拡張モジュールの確認

Mac OS Xでは、カーネル拡張モジュールのロード状況は、kextstatコマンドでモニターできます。

kextstat|wc -l
128
kextstat|grep FTDI
$

上の実行例は、USB-シリアル変換IC未接続の状態で実行した結果です。ロードされているカーネル拡張モジュールは全部で128個ありますが、この中に"FTDI"と言う名前をもつモジュールはありません。

次に、FTDI製USB-シリアル変換ICを接続してみます(Mac OS XにはあらかじめVCPドライバインストール済み)。

kextstat|grep FTDI
140 0 0xffffff7f81660000 0x8000 0x8000 com.FTDI.driver.FTDIUSBSerialDriver (2.2.18) <127 36 5 4 3 1>
141 0 0xffffff7f80f1f000 0x7000 0x7000 com.apple.driver.AppleUSBFTDI (1.0.1b1) <127 36 5 4 3>

今度は、ふたつの拡張モジュールが見つかりました。

  • com.FTDI.driver.FTDIUSBSerialDriver --- FTDI社製のVCPドライバ
  • com.apple.driver.AppleUSBFTDI --- Apple社製のUSB-FTDIドライバ

FTDI社とApple社、ふたつのドライバがロードされていますが、それぞれのドライバ名をバンドルID (bundle ID)と呼びます。

FTDI VCPドライバがインストールされていない場合は、

$ kextstat|grep FTDI
140 0 0xffffff7f80f1f000 0x7000 0x7000 com.apple.driver.AppleUSBFTDI (1.0.1b1) <127 36 5 4 3>

Apple製ドライバのみがロードされます。

cu/ttyキャラクタ型デバイスの登録

ドライバがロードされると/devディレクトリ上には、接続されたUSB-シリアル変換ICに対応するキャラクタ型デバイスファイルが出現します。

$ ls -l /dev/*serial*
crw-rw-rw- 1 root wheel 18, 23 12 19 23:09 /dev/cu.usbserial-FTUG3YAF
crw-rw-rw- 1 root wheel 18, 22 12 19 23:09 /dev/tty.usbserial-FTUG3YAF

上記例は、UM232H (FT232H: シリアル1チャンネル搭載)を接続した場合ですが、シリアルポートひとつに対して、ふたつのデバイスファイルが登録されています。

UNIXでは歴史的に、TTY (TeleTYpe)デバイスは受信、cu (Call Up)デバイスはモデムなどの発信に利用されてきた経緯があるため、このように2種類のデバイスが用意されています(通常はcuを使用)。

$ screen /dev/cu.usbserial-FTUG3YAF 38400

この状態で、screenコマンドにcuデバイスファイル名を指定すると、38400ボーの通信速度で接続されたターミナルエミュレータが起動します。FTDIボードとLinux組み込みマイコンボードなどをRS-232-Cケーブルで接続すれば、シリアル通信が可能になります。

カーネル拡張モジュールのアンロード

Mac OS Xでは、カーネル拡張モジュールをアンロードするためには、kextunloadコマンドを用います。-bundleオプションで、アンロードする拡張モジュールのバンドルIDを指定します。実行には管理者権限が必要なので、sudoの前置が必要です。

$ sudo kextunload -bundle com.FTDI.driver.FTDIUSBSerialDriver
Password:
$ kextstat|grep FTDI
$

上記例では、FTDI製のVCPドライバをアンロードしています。

$ sudo kextunload -bundle com.apple.driver.AppleUSBFTDI
Password:
$ kextstat|grep FTDI
$

次は、Apple製のUSB-FTDIドライバのアンロード例です。ふたつのドライバが存在する場合は、双方をアンロードしてください。

ドライバをアンロードするためにはバンドルIDを使用する以外に、カーネル拡張モジュールを格納するディレクトリ名を指定する方法もあります(オプションは不要)。

$ tree /System/Library/Extensions/FTDIUSBSerialDriver.kext
/System/Library/Extensions/FTDIUSBSerialDriver.kext
└── Contents
    ├── Info.plist
    ├── MacOS
    │   └── FTDIUSBSerialDriver
    └── Resources
        └── English.lproj
            └── InfoPlist.strings

4 directories, 3 files

FTDI製VCPドライバの本体は、/System/Library/Extensions/FTDIUSBSerialDriver.kextディレクトリに格納されており、上記のような構造になっています。

$ tree /System/Library/Extensions/IOUSBFamily.kext/Contents/PlugIns/AppleUSBFTDI.kext
/System/Library/Extensions/IOUSBFamily.kext/Contents/PlugIns/AppleUSBFTDI.kext
└── Contents
    ├── Info.plist
    ├── MacOS
    │   └── AppleUSBFTDI
    ├── Resources
    │   └── English.lproj
    │       └── InfoPlist.strings
    ├── _CodeSignature
    │   └── CodeResources
    └── version.plist

5 directories, 5 files

Apple製のUSB-FTDIドライバは、/System/Library/Extensions/IOUSBFamily.kext/Contents/PlugIns/AppleUSBFTDI.kextディレクトリに存在します。

拡張モジュールディレクトリを指定するアンロードの実行は、次のようになります。

$ sudo kextunload /System/Library/Extensions/FTDIUSBSerialDriver.kext

FTDI製VCPドライバの場合。

$ sudo kextunload /System/Library/Extensions/IOUSBFamily.kext/Contents/PlugIns/AppleUSBFTDI.kext

Apple製USB-FTDIドライバの場合。

最後に

D2XXライブラリで作成したUSB-FIFO制御プログラムが正常に動作しない場合は、kextstat|grep FTDIを実行し、シリアルポートドライバが存在する場合は対応モジュールのアンロードを適宜実行してください。

posted by Wataru Nishida.
開発環境
印刷用ページへ

VirtualBox UbuntuへのホストUSBデバイスの接続

VirtualBox上で稼働するUbuntuに、ホストのUSBデバイスを認識させる方法について解説します。

VirtualBoxへのUSBデバイスの接続

ホスト(VirtualBoxを起動するマシン)に接続されたUSBデバイスをVirtualBoxの仮想環境上で利用するためには、接続作業が必要になります。

VirtualBox最下段のインジケータ、左から3番目にUSBデバイスのアイコンが位置していますが、クリックするとホストに接続されているUSBデバイスの一覧が表示されます(MacBook ProにFTDI社のUM232Hを接続した状態での実行例)。

目的のデバイスをマウスで選択しクリックすると、

UM232HがVirtualBoxに接続され、チェックマークが入ります。

Ubuntuでの認識

Ubuntuには、デフォルトでFTDIシリアルポートドライバがインストールされているため、UM232Hが接続されると、仮想シリアルポートへアクセスするためのデバイスファイルが出現します。

oversea@Ubuntu64:~$ ls -l /dev/*USB*
crw-rw---- 1 root dialout 188, 0 12月 21 11:11 /dev/ttyUSB0

/dev/ttyUSB0はキャラクタ型デバイスファイルであり、グループオーナーはdialout(発信)となっています。

Ubuntuでは、デバイスファイル以外に/dev/serialディレクトリも新たに登録されます。

$ tree /dev/serial
/dev/serial
├── by-id
│   └── usb-FTDI_UM232H_FTUG3YAF-if00-port0 -> ../../ttyUSB0
└── by-path
    └── pci-0000:00:06.0-usb-0:2:1.0-port0 -> ../../ttyUSB0

ホスト環境からの切断

UM232HがUbuntu上で認識されると同時に、ホスト環境では切断されます。

MacBookPro:~ $ ls /dev/*serial*
ls: /dev/*serial*: No such file or directory
MacBookPro:~ $ kextstat |grep FTDI
MacBookPro:~ $

/devディレクトリのデバイスファイルエントリは消失し、FTDIシリアルポート対応拡張モジュールもアンロードされています。この時点で、ホスト上のD2XXライブラリを使用したプログラムはFTDIデバイスを認識できなくなりますので、注意してください。

VirtualBoxステータスラインのUSBアイコン上で切断を行うと、再びホスト環境に拡張モジュールがロードされ、デバイスファイルも復帰します(Ubuntu上のデバイスファイルは消失)。

posted by Wataru Nishida.
開発環境
印刷用ページへ

VirtualBoxへのUbuntuインストール

VirtualBox上にUbuntu Desktop 64ビット版およびGuest Additionsをインストールする方法を解説します。

Ubuntu 64bit/Desktopのダウンロード

Ubuntu Desktop版は下記のページからダウンロード可能です。

最新版は13.10であり(2013/12/7時点)、64ビット版を選択します。

寄付の確認画面が表示されますので、その意思がある場合は金額を入力し、PayPalで決済します。寄付を辞退する場合は、左側の"Not now, take me to the download"をクリックしてください。

以後は自動的に進行し、"ubuntu-13.10-desktop-amd64.iso"というISOイメージファイル(926MB)がダウンロードされます。

copyiso

ダウンロードしたISOイメージファイルは、仮想マシンのファイルを格納するディレクトリに移動しておきます(今回の例では、~/VirtualBox VMs/Ubuntu 64bit/)。

ISOイメージのマウント

次に、ISOイメージファイルを仮想マシンUbuntu 64bit上の光学ドライブにマウントさせる作業を行います。

デフォルトでは、CD/DVDドライブは未接続の状態()になっていますので、マネージャー画面上の"ストレージ"をクリックし、設定を行います。

ストレージツリー内の""と表示されたCD/DVDアイコンをクリックし、"IDE セカンダリーマスター"の右にある、CD/DVDアイコンをクリックすると、

サブメニューが表示されるため、最上段の"仮想CD/DVDディスクファイルの選択"を実行します。

ダウンロードした"ubuntu-13.10-desktop-amd64.iso"を選択し、オープンします。

storage-final

CD/DVDアイコンの右に、マウントされたISOイメージファイル名が表示されています。

ISOイメージのマウント状況は、マネージャー画面にも反映されています。

これで、仮想マシンのCD/DVDドライブに、"Ubuntu 13.0 Desktop 64ビット版インストーラCD"を挿入した状態を設定できました。

Ubuntu起動および初期設定

マネージャー画面上の"起動"ボタンを押すと、仮想マシンが立ち上がります。

bios

最初に、仮想マシンのBIOS起動画面が表示されますが、最下段のステータスバー左から2番目のCD/DVDアイコンに注目してください。メディア未挿入時はグレー表示となりますが、先ほどISOイメージを"仮想マウント"したため、アイコンがブルーで表示されています。

このため、Ubuntu 64bit仮想マシンはハードディスクからではなく、CD-ROM(実体はISOイメージ)から起動します。

CD-ROMからUbuntuが起動し、Welcomeメッセージが表示されます。画面上の"Install Ubuntu"をクリックし、インストールに進みます。

prepare

そのまま"Continue"で続行してください。

installation

デフォルトの"Erase disk and install Ubuntu"を実行します。

タイムゾーンの選択を行います(IPアドレスに応じてTokyoを自動選択)。

キーボードレイアウトを指定しますが、これもデフォルト(Japanese-Japanese)のままで構いません(括弧付け表記が付いたものは旧式のキーボード)。

管理ユーザ(sudoの実行許可)の名前、Ubuntuホストの名称、アカウント名、パスワードを入力します。

オンラインストレージサービスである"Ubuntu One"を利用する場合は、アカウントを入力します。Ubuntu Oneを利用しない場合は、"Log in later"を選択します。

インストール中の画面がしばらく表示された後、

インストール完了のメッセージ表示されます。"Restart Now"を押し、システムを再起動してください。

press_enter

再起動前に、Ubuntu上で各種の終了処理が行われます。グラフィック画面は消え、テキストメッセージがコンソール上に表示されます。

最後に、"Please remove installation media and close the tray (if any) then press ENTER:"と表示され、ユーザーの指示を待ちます。ステータスバーのCD/DVDアイコンはグレー表示になっており、"イジェクト処理"は完了しているので、そのままリターンキーを押してください。

仮想マシンは再起動し、今度はハードディスクからUbuntuが起動します。

Desktop画面が現れ、Ubuntuのインストールは完了です。

Guest Additionsの追加インストール

VirtualBoxは、ゲストOS環境向けにGuest Additionsと呼ばれる拡張パッケージを用意しています。

主要機能は以下の通りです。

  1. マウスポインタをホスト/ゲスト間でシームレスに共有
  2. ホスト/ゲスト間の共有フォルダを提供
  3. より高度なビデオサポート(大解像度、3D/2Dアクセラレーション対応)
  4. ホスト/ゲスト間でのクリップボード共有
  5. ホスト/ゲスト間の時刻同期

insert

Guest Addionsのインストールは、ゲストOSが稼働している状態で、トップメニューの"Devices"から"Insert Guest Additions CD image"を選択します。

Guest AdditionsインストーラCDがマウントされ、実行の確認を求めてきますので、"Run"を選択してください。

実行にあたり、管理者のパスワードを求められます。

複数のカーネルモジュールが自動的にビルドされ、その後インストールされます。

最後に、"Press Return to close this window..."というメッセージが表示されるので、リターンキーを押すと、Guest Additionsのインストールは完了です。

Guest Additions CDのイジェクト

上図のように、Guest AdditionsインストーラCD(VBoxGuestAdditions.iso)がマウント状態になっているため、仮想的な"イジェクト処理"を行います。

CDアイコンのサブメニューを表示させ、最下段の"Remove disk from virtual drive"を実行してください。Guest AdditionsインストーラCDはイジェクトされ、CDアイコンはグレー表示に変わります。

Terminalの起動

UNIX環境での作業には、端末が必須となります。

Ubuntuでは、Desktop上で"Ctrl-Alt-T"のキーボードショートカット(MacではCtrl-option-T)を入力すると、Terminalプログラムが起動します。

oversea@Ubuntu64:~$ uname -a
Linux Ubuntu64 3.11.0-12-generic #19-Ubuntu SMP Wed Oct 9 16:20:46 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux

手始めに、Terminal上でuname -aを実行すると、ゲスト環境の実行プロセッサは確かにx86-64 (AMD64命令セット)であることが分かります。

先ほどビルド&インストールされた、Guest Additions用のカーネルモジュールも確認してみましょう。

Guest Additions用のカーネルモジュール名はvboxで始まるため、lsmodコマンドの出力をgrepで検索すると、vboxvideo/vboxsf/vboxguest 計3種類のモジュールがインストールされていることが分かります。

OpenSSHサーバーのインストール

インストール直後のUbuntuには、sshコマンドは用意されていますが、sshサーバーは搭載されていません。ホスト環境からSSH接続で作業する場合は、SSHサーバーが必要になるため、追加インストールを行います。

Ubuntuの基盤パッケージ群を構成しているディストリビューションはDebianですが、その内容は下記ページで確認できます。

SSHサーバーはopenssh-serverという名前のパッケージに含まれています。

Debianでは、パッケージインストールに際して、apt-getコマンドを用います。オプションにinstallを指定し、その後にインストールするパッケージ名を続けます。インストールの実行には管理者権限が必要であるため、sudoを前置してください。

oversea@Ubuntu64:~$ sudo apt-get install openssh-server
[sudo] password for oversea:
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following extra packages will be installed:
libck-connector0 ncurses-term openssh-client python-requests python-urllib3
ssh-import-id
Suggested packages:
libpam-ssh keychain monkeysphere openssh-blacklist openssh-blacklist-extra
rssh molly-guard
The following NEW packages will be installed:
 libck-connector0 ncurses-term openssh-server python-requests python-urllib3
 ssh-import-id
The following packages will be upgraded:
 openssh-client
1 upgraded, 6 newly installed, 0 to remove and 156 not upgraded.
Need to get 1,278 kB of archives.
After this operation, 3,507 kB of additional disk space will be used.
Do you want to continue [Y/n]?

メッセージから、openssh-serverに関連する6つのパッケージがインストールされ、openssh-clientはアップデートされることが分かります。

Debianでは、パッケージの依存関係が厳密に定義されると共に、データベース化されているため、あるパッケージが他パッケージに依存している場合は、これらも自動的にインストールされる仕組みになっています。

oversea@Ubuntu64:~$ ps ax|grep sshd
3841 ? Ss 0:00 /usr/sbin/sshd -D
3985 pts/0 S+ 0:00 grep --color=auto sash

psコマンドの出力から、sshdを含む行を抽出すると、SSHサーバーが起動していることを確認できます(/usr/sbin/sshd -D)。

oversea@Ubuntu64:~$ ifconfig
eth0      Link encap:Ethernet  HWaddr 08:00:27:fa:af:50  
          inet addr:192.168.1.195  Bcast:192.168.193.255  Mask:255.255.255.0
          inet6 addr: fe80::a00:27ff:fefa:af50/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:5914 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1805 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:2269422 (2.2 MB)  TX bytes:172391 (172.3 KB)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:1546 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1546 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:126238 (126.2 KB)  TX bytes:126238 (126.2 KB)

ifconfigコマンドで、ネットワークインターフェースの設定状態を確認すると、UbuntuホストのIPアドレスは192.168.1.195が割り振られています(環境によって異なる)。

MacBookPro $ ssh oversea@192.168.1.195
oversea@192.168.1.195's password:
Welcome to Ubuntu 13.10 (GNU/Linux 3.11.0-14-generic x86_64)* Documentation: https://help.ubuntu.com/

0 packages can be updated.
0 updates are security updates.

Last login: Tue Dec 10 13:08:48 2013 from macbook-pro-17.local

ホスト環境(MacBoor Pro)からsshコマンドを使い、192.168.1.195へ接続すると(ユーザー名@の後にIPアドレスを指定)、パスワードの入力を求められ、Ubuntuへのリモートログインが完了します。

再起動

以上でUbuntuの基本設定は完了しました。インストールされたGuest Additionsを有効化するためには、再起動が必要です。

Desktop画面の右上に電源アイコンがありますので、これをクリックするとサブメニューが現れます。"Restart..."を選択すると、

最終確認画面が表示されるので、"左側のRestart"を選択してください。"右側はShutdown"であり、こちらを選択すると仮想マシンの電源がオフとなります。

再度Ubuntuが起動すると、ホスト環境に合わせてDesktopは大画面化しています。

インストール直後のUbuntuには、vimやemacsは用意されておらず、エディターはnanoもしくは旧式のedしか利用できません。必要に応じてパッケージをインストールし、自分好みの環境に育てあげてください。パッケージ選定にあたっては、Debianのパッケージ一覧が参考になるでしょう。

posted by Wataru Nishida.