2009年10月9日金曜日

第8回 開発環境の構築

    ディレクトリ/usr (UnixSystem Resources)以下には、一般に使用するユーザコマンド、共有ライブラリ、そして開発環境が入っている。開発環境はさらに系統別に分類され下記のディレクトリに置かれる。

    /usr/ccs (C Compiler Support)   …   SystemV系
    /usr/sfw (Sun Free Ware)   …   GNU系
    /usr/ucb (University ofCalifornia Berkeley)   …   BSD系

    開発環境の構築といっても、Oracleユーザのプロファイルには、パスと環境変数が設定済みであるので、それをそのまま使用するか、ユーザを新規に作成する場合はプロファイルをコピーすれば、すでにパスが通っているので、下記のSolaris 10 にバンドルされている、コンパイラなどは直接起動できる。

    GNU Cコンパイラ、リンカ(3.4.3)   …   /usr/sfw/bin/gcc
    Solarisリンカ(5.10-1.493)   …   /usr/ccs/bin/ld
    GNU make(3.80)   …   /usr/sfw/bin/gmake
    Solaris make   …   /usr/ccs/bin/make

    加えてOracle 10gをインストールしたのでPro*C/C++、すなわち埋め込みSQL(Embedded SQL)をコンパイル前に処理するプリコンパイラ(precompilerまたはプリプロセッサpreprocessorと呼ぶ)と、SQL処理のライブラリが装備されている。なおかつ、本回までの作業を終えているならば、オラクルとの接続が確立されているはずで、C 言語によるOracle データベースへアクセスするプログラムの開発条件が満たされているので、それの開発を試みる。

  1. ソースプログラムの準備
  2. Oracleインストールの事前準備の設定で、必要な環境条件が調っている「oracle」ユーザーでログインして、下記の静的SQL(staticSQL.pc)と動的SQL(dynamicSQL.pc)のソースファイルを2種類作成する。置く場所は任意である。プログラムの内容は、Oracleに「scott」ユーザーで接続して、自分のテーブルの数をカウントして表示するという、ごく単純で短いものだ。

    両者は同じSQL文を使用しているが、静的SQLと動的SQLの違いは、静的SQLがSQL文をリテラルで保持しているのに対して、動的SQLは文字列変数内に保持していることである。プリコンパイラはさすがに文字列の中身までは解析しないし、いつ変わるとの知れない、文字通り「変数」の中身を解析するのは無意味なことなのだ。

    静的SQL(staticSQL.pc)

    #include <sqlca.h>
    int main () {
        EXEC SQL BEGIN DECLARE SECTION; 
            char *username = "scott/tiger"; 
            int rows=0;
        EXEC SQL END DECLARE SECTION; 
        EXEC SQL CONNECT: username;
        EXEC SQL select count (*) into:rows from user_tables;
        printf ("%d rows\n", rows);
        exit (0);
    }

    動的SQL(dynamicSQL.pc)

    #include <sqlca.h>
    int main() {
        EXEC SQL BEGIN DECLARE SECTION;
            char *username="scott/tiger";
            char statement[256];
            int rows;
        EXEC SQL END DECLARE SECTION;
        EXEC SQL CONNECT: username;
        sprintf(
            statement, 
            "begin "
            "select count(*) into:rows from user_tables;"
            "end;"
        );
        EXEC SQL PREPARE statement_id FROM :statement;
        EXEC SQL EXECUTE statement_id USING :rows;
        printf ("%d rows\n", rows);
        exit(0);
    }

  3. Pro*Cによるプリコンパイル
  4. Pro*CによるプリコンパイルでPro*Cのソースプログラム(*.pc)からCコンパイラが理解できるCのソースプログラム(*.c)に変換する。変換する内容は、「EXECSQL」から「;(セミコロン)」までの実行ステートメントをSQLLIB(ランタイムライブラリ)の関数をコールするソースコードに置き換え、「EXEC SQL BEGIN DECLARESECTION;」から「EXEC SQL END DECLARESECTION;」までに入っている、C言語のプログラムとOracleが共有する変数、これをホスト変数と呼ぶが、両者が使用できるようにする。

    プリコンパイラは下記のように、拡張子なしでプログラム名を指定して実行する。また、オプションに、sqlcheck=fullに設定することで埋込みSQL構文の解析を完全に実行しておく、プリコンパイルでSQL構文の不備を撥ねるのだ。静的SQLでuseridオプションを指定しているのは、プリコンパイルの段階で、実際にこの接続先からSQL文中にあるテーブル構造を読み取りに行くので、必須のオプションである。

    静的SQL

    $ proc staticSQL sqlcheck=full userid=scott/tiger;

    動的SQL

    $ proc dynamicSQL sqlcheck=full;

    以降のコマンドライン・オプションは静的SQLと動的SQLで同一であるので、静的SQLのファイル名であるstaticSQLによって例示する。動的SQLの場合はそれをdynamicSQLに読み替えて使用されたい。

  5. GCCによるコンパイル
  6. GCC はコンパイラのみならず、プリプロセス、コンパイル、アセンブル、リンクの各機能を呼び出す親プログラムである。ここではコンパイルまでを行うので「-c」オプションを指定して、コンパイルの対象になるソースプログラムファイルを指定すると、同名で拡張子を「.o」に変えたオブジェクトコードを作成する。

    続いて「-I」オプションによってヘッダファイルを探すディレクトリを指定する。プログラム中でsqlca.hをincludeしている。これは「$ORACLE_HOME/precomp/public」の配下に有るのでその場所を指定する。

    静的SQL

    $ gcc -c staticSQL.c -I $ORACLE_HOME/precomp/public

    動的SQL

    $ gcc -c dynamicSQL.c -I $ORACLE_HOME/precomp/public

  7. リンク
  8. どちらが良いとはいえないので、リンカにGNUのコマンドを使った「gcc -o」と、Sunのコマンドを使った「ld」の、2種類のコマンドを例示する。

    先に下記のように環境変数を設定しておいたので、オプションの設定を簡略することが出来る。LD_LIBRARY_PATHを設定して置かないと、リンカは標準ライブラリ(「/usr/ccs/lib/」、「/usr/lib」)の中のみを検索するので、「-L」オプションを用いて、ライブラリを検索するパスを示してから、Oracleのライブラリを指定しなければならない。さらに、「-R」オプションによって、実行時にロードするライブラリへの検索パスを、実行プログラムに組み込む必要がある。したがって、この環境変数を設定しておくことによって、「-L」、「-R」オプションの設定を省略することが出来る。

    LD_LIBRARY_PATH=$ORACLE_HOME/lib

    標準ライブラリではない、または環境変数に定義していない任意のライブラリは、「-L」と「-l(小文字のL)」オプションと組み合わせて使用する。リンカは、「-L」で指示されたパスを起点にして、そこから「-l」で指定したライブラリ名に接頭辞「lib」を加えたものを、共有ライブラリ(shared library)、スタティックライブラリ(static library)の順に検索する。すなわち拡張子を「.so」、「.a」の順に加えて検索したライブラリを、リンクリストに加えるオプションである。

    例えば、「-lclntsh」と指定した場合は、LD_LIBRARY_PATH で指定してあるので、「-L」オプションは不要であり、$ORACLE_HOME/lib/以下の共有ライブラリ「libclntsh.so」を検索して、そこからシンボリックリンクを張った「libclntsh.so.10.1」をリンクリストに加え、実行時リンカは、実行時にこの共有ライブラリを結合する。

    • gcc -o
    • リンクにGCCを使用する場合は下記のように行う。すなわち「-o」オプションでリンクを指定して、実行ファイル名、リンクするオブジェクトコード、そしてライブラリを指定して、実行ファイルを作成する。

      静的SQL

      $ gcc -o staticSQL staticSQL.o -lclntsh

      動的SQL

      $ gcc -o dynamicSQL dynamicSQL.o -lclntsh

    • ld
    • 下記は「gcc -o」では暗黙でリンクされるが「ld」ではリンクされないライブラリである。これが両者の違いであるので「ld」では明示する。

      • -lc  …  Cの標準関数を含むライブラリ(libc.so)
      • crt1.o、crti.o、crtn.o  …  Cラインタイムオブジェクト(C RunTime startup)

      したがって、「ld」ではそれらを追加して下記のように行う。なお、Cラインタイムオブジェクトは「gcc -o」は/usr/lib以下のオブジェクトを使用しているが、ここではOracleライブラリのものを使用した。

      静的SQL

      $ ld -o staticSQL staticSQL.o $ORACLE_HOME/lib/prod/lib/crti.o $ORACLE_HOME/lib/prod/lib/crt1.o $ORACLE_HOME/lib/prod/lib/crtn.o -lc -lclntsh

      動的SQL

      $ ld -o dynamicSQL dynamicSQL.o $ORACLE_HOME/lib/prod/lib/crti.o $ORACLE_HOME/lib/prod/lib/crt1.o $ORACLE_HOME/lib/prod/lib/crtn.o -lc -lclntsh

  9. プログラムの実行
  10. 出来上がった実行ファイルを実行すると、scottがオーナーであるテーブル数、すなわち「DEPT」、「EMP」、「BONUS」、「SALGRADE」の4個のテーブルがカウントされ、下記のように表示される。

    静的SQL

    $ staticSQL
    4 rows

    動的SQL

    $ dynamicSQL
    4 rows

  11. makeの使用
  12. 「make」というコマンドを実行すると、カレントディレクトの中のファイルをmakefile、Makefile (以降makefileで統一)の順に探し、その記述を読み込んで実行する。記述には実行可能ファイル作成までの手順が書かれている。後で示すmakefileが既に作成済みであるとすると、「-n」オプションを加えて実行した結果は下記のとおりであり、プリコンパイル、コンパイル、リンクが順次実行される様子が分かる。

    $ make -n staticSQL
    proc sqlcheck=full iname=staticSQL.pc userid="scott/tiger"
    gcc -c staticSQL.c -I. -I$ORACLE_HOME/precomp/public -I$ORACLE_HOME/rdbms/public
    gcc -o staticSQL staticSQL.o -lclntsh `cat $ORACLE_HOME/lib/sysliblist`

    これだけではシェルスクリプトにコマンドをならべたことと同じであるが、make は依存関係の検査という仕組みを使用して、生成するファイルが、そのファイルの生成に影響するファイルよりも新しい場合は、該当するコマンドは実行しない。これにより、更新されたファイルに関連性のあるものだけを処理するため、不要な処理を抑制することが出来る。

    makefileはターゲット行、シェルコマンド行、マクロ定義 インクルード行によって構成される。ターゲット行は下記のように、対になるコマンド行と構成される場合は、これをルール(rule)と呼ぶ。

    ターゲット(targets) : 前提条件ファイルのリスト(dependents)
    [tab]コマンド(command)

    「ルール」の記法には、サフィックスルール(suffix rule)とパターンルール(patternrule)がある。パターンルールを用いて、プリコンパイルを行う場合は、下記のように記述する。すなわち下記は、%.pcから%.cをproc…コマンドによって作成するという意味になり、「%」はmakeを「make [options] [target] 」のように起動したときの[target]が置き換わる。「$<」は自動変数と呼ばれ、ここでは「%.pc」、要するにPro*Cのソースプログラムが該当する。

    %.c: %.pc
        proc sqlcheck=full iname=$< userid="scott/tiger"

    一連の処理を下記のmakefileにまとめた、これをソースプログラムと同じディレクトリに保存する。

    CFLAGS = -I. -I$$ORACLE_HOME/precomp/public -I$$ORACLE_HOME/rdbms/public
    LIBS = $$ORACLE_HOME/lib/prod/lib/crti.o $$ORACLE_HOME/lib/prod/lib/crt1.o $$ORACLE_HOME/lib/prod/lib/crtn.o -lc
    ORA_LIBS = -lclntsh `cat $$ORACLE_HOME/lib/sysliblist`
    %.c: %.pc
        proc sqlcheck=full iname=$< userid="scott/tiger"
    %.o: %.c
        gcc -c $< $(CFLAGS)
    %: %.o
    #    ld -o $@ $< $(LIBS) $(ORA_LIBS)
        gcc -o $@ $< $(ORA_LIBS)

    • コマンド行のインデントはタブでなくてはならない
    • viでこのページからコピー&ペーストする場合には、次のように置換する。([tab]はタブを入力)
      :%s/[スペース4文字]/[tab]/g

    • 「$@」は自動変数であり[target]と同じになる。
    • 汎用的に使えるように、よく使われるヘッダー、ライブラリを追加してある。
    • 行頭が「#」の場合はコメントである、したがって、この例ではgccのリンカが有効になっている。Solarisリンカを使う場合はgcc …をコメントアウトして、ld …をコメントインする。

    下記のとおりmakeを起動すると、実行可能ファイルが作成される。

    静的SQL

    $ make staticSQL

    動的SQL

    $ make dynamicSQL

    あるいは、下記のようにすると指定したターゲットまでの中間処理までをおこなう。

    静的SQL

    $ make staticSQL.o

    動的SQL

    $ make dynamicSQL.o

    • GNU makeを使用するときには、コマンドを「make」から「gmake」に変更する。
    • 依存関係を無視して、無条件にmake する場合は「-u」オプションを使用する。gmakeの場合は依存するファイルにtouchして日時を生成するファイルよりも新しくする。

0 件のコメント:

コメントを投稿