2011年12月03日

SConsでTeX文書をコンパイルする

SConsでTeX文書をコンパイルする
SConsはデフォルトでTeX文書をコンパイルするDVI()ビルダを持っている。
日本語platex関連(pLaTeX2e)は事前にインストールしておく。

以下は、test.texファイルをコンパイルして*.dviファイルを出力し、
それをdvipdfmコマンドでPDFに変換するSConstructファイル。
bibtexやmakeindexも呼ばれ、必要な回数、platexコンマンドも呼び出される。
import os
env = Environment( ENV = os.environ,
                   LATEX  = 'platex',
                   BIBTEX = 'jbibtex' )
dvi = env.DVI('test','test.tex')
name , ext = os.path.splitext( dvi[0].path )
env.Command( name + '.pdf', dvi, 'dvipdfm -o $TARGET $SOURCE')
日本語文書なので「LATEX変数」に「platex」を指定する。 これで、うまく行くと思ったものの、「文献参照生成」機能がうまく動かないことがわかった。 orz 4年前に以下のスレッドで問題になっていて、直っている気がするんだけど。 http://scons.tigris.org/ds/viewMessage.do orz orz 他にpdflatexを使って*.texから直接PDFファイルを出力するPDF()というビルダもあるが、 「pdflatex」が日本語スタイルを処理できないので使えなかった。
posted by Copyright (C) avrin All Rights Reserved. at 22:45| SCons | このブログの読者になる | 更新情報をチェックする

2010年12月11日

ARMマイコンでSConsを使う(3) マイコン編

ARMマイコンでSConsを使う(3) マイコン編
前回の ARMマイコンでSConsを使う(2) のつづき。
    ARMマイコンでSConsを使う(1) さらに前。

make/Makefile より簡単に書けるSConsを試してみます。インストールはここ。

前回までで、一通りのことはできるようになったので各種マイコン用の
SConsプロジェクトを作ってみます。
マイコン用の無料のコンパイラはgcc系が多いのでマイコンが変わっても
違いは少ないと思われます。

  * AVRでSConsを使う
    基本編:(AVR)
    完成編:(AVR)
    まとめ編:(AVR) toolを使う

  * LPC2388(ARM7)でSConsを使う
  * LPC1768(Cortex-m3)でSConsを使う
  * toolファイル :「armgcc.py」


* AVRでSConsを使う
SConsのバージョンは「Ver.2.0.1」です。
このファイルにまとめました。
scons-note3-20110327.zip

基本編:(AVR)
ARMの前にAVRマイコンをSConsでコンパイルしてみます。

まず、パソコン上のコンパイラ「gcc」用の
「超簡単なプロジェクト」(SConstructファイル)をベースにします。
# SConscript.gcca00
# Cygwin/MSys(MinGW)
import os
env = Environment( tools = ['mingw'] , ENV=os.environ )
env['CCFLAGS'] =        \
      '-Os              \
      -Wall -std=gnu99  \
      -fsigned-char -funsigned-bitfields -fpack-struct -fshort-enums \
      -ffunction-sections -fdata-sections'
env['LINKFLAGS'] =      '-Wl,--gc-sections'
prog_n = env.Program( 'main', Glob('*.c') )
上は実質4行程度の記述ですが、 C言語ファイルがいくつあってもコンパイル可能なスグレモノです。 コンパイルしてみます。
$ scons -Q -f SConstruct.gcca00
gcc -o main.o -c -Os -Wall -std=gnu99 -fsigned-char -funsigned-bitfields 
-fpack-struct -fshort-enums -ffunction-sections -fdata-sections main.c
gcc -o main.exe -Wl,--gc-sections main.o

$ scons -Q -f SConstruct.gcca00 -c
Removed main.o
Removed main.exe
上は以下のように「envの定義に含めて書く」こともできます。
# SConscript.gcca00.1
# Cygwin/MSys(MinGW)
import os
env = Environment(                     
      tools    = ['mingw']             
    , ENV      = os.environ            
    , CCFLAGS  = '-Os                  \
                 -Wall -std=gnu99      \
                 -fsigned-char -funsigned-bitfields -fpack-struct -fshort-enums \
                 -ffunction-sections -fdata-sections '
    ,LINKFLAGS = ' -Wl,--gc-sections ' 
)
prog_n = env.Program( 'main', 'main.c' )
見やすいように、コメントと改行などを追加しておきます。
# SConscript.gcca0
# Cygwin/MSys(MinGW)
import os

TARGET = 'main'
########################
# Create env object
########################
env = Environment( tools = ['mingw'] , ENV=os.environ )
env['CCFLAGS'] =        \
      '-Os              \
      -Wall -std=gnu99  \
      -fsigned-char -funsigned-bitfields -fpack-struct -fshort-enums \
      -ffunction-sections -fdata-sections'

env['LINKFLAGS'] =      \
      '-Wl,--gc-sections'

########################
# Build project
########################
prog_n = env.Program( TARGET, Glob('*.c') )
これを「AVRマイコン」用に修正すると以下のようになります。
# SConscript.gcca1
# MSDOS/Cygwin/MSys(MinGW)

import os

TARGET = 'main'

########################
# MCU type
########################
MCU = 'atmega328p'

########################
# Create env object
########################
env = Environment( tools = ['mingw'] , ENV=os.environ )
env['CC']         = 'avr-gcc'
env['PROGSUFFIX'] = '.elf'
env['CCFLAGS']=  \
      '-Os       \
      -Wall -std=gnu99 \
      -fsigned-char -funsigned-bitfields -fpack-struct -fshort-enums \
      -ffunction-sections -fdata-sections'                           \
      + ' -mmcu='+ MCU

env['LINKFLAGS']= \
      '-Wl,--gc-sections'

########################
# Build project
########################
prog_n = env.Program( TARGET, [ 'main_avr.c' ] )
上でわかるようにパソコン用の「gcc」との違いは 1,MCU指定:「atmega328p」 2,コンパイラ指定:「avr-gcc」 3,出力ファイル拡張子名:「.elf」 (実は.exeのままでも問題ない) とほんの少しです。 それに、もう「Cygwin/MinGW環境」も必要なく、 「MS-DOS窓」と「avr-gcc」があればコンパイルできます。(注1) ただし、コンパイルには「AVR Tool Chain」をインストールして使ってください。 「WinAVR」ではうまく動作しませんでした。 (「msvcirt.dll」がらみが新しいとダメになる症状のようでした。) 完成編:(AVR) 上で、「*.elf」ファイルを出力したのでこのファイルから 「*.hex, *.eep, *.lst, *.bin」を生成します。 これは前回学んだ内容そのままです。一気に追加します。
# SConscript.gcca2
# MSDOS/Cygwin/MSys(MinGW)

import os

TARGET = 'main'

########################
# MCU type
########################
MCU = 'atmega328p'

########################
# Create env object
########################
env = Environment( tools = ['mingw'] , ENV=os.environ )
env['CC']         = 'avr-gcc'
env['PROGSUFFIX'] = '.elf'
env['CCFLAGS']=  \
      '-Os       \
      -Wall -std=gnu99 \
      -fsigned-char -funsigned-bitfields -fpack-struct -fshort-enums  \
      -fno-inline-small-functions -fno-split-wide-types -mshort-calls \
      -ffunction-sections -fdata-sections'                            \
      + ' -mmcu='+ MCU

MAP_NAME = TARGET + '.map'
env['LINKFLAGS']= \
      '-Wl,--gc-sections \
       -Wl,--cref -Wl,-M -Wl,-Map=' + MAP_NAME

########################
# Build project
########################
prog_n = env.Program( TARGET, [ 'main_avr.c' ] )

########################
# Generate hex,bin etc.
########################
import os.path
base_name, ext = os.path.splitext( prog_n[0].path )
env.Command( base_name + '.bin', prog_n, '@avr-objcopy -O binary $SOURCE $TARGET' )
env.Command( base_name + '.hex', prog_n, '@avr-objcopy -O ihex $SOURCE $TARGET'   )
env.Command( base_name + '.lst', prog_n, '@avr-objdump -h $SOURCE -d  > $TARGET'  )
env.Command( None              , prog_n, '@avr-size $SOURCE'                      )
AVR_EEP_CMD = '@avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" \
	--change-section-lma .eeprom=0 --no-change-warnings -O ihex $SOURCE $TARGET'
env.Command( base_name + '.eep', prog_n, AVR_EEP_CMD )

env.Clean( prog_n, MAP_NAME )
「*.eep, *.map」ファイルも出力するようにしました。フル装備です。 コンパイルしてみます。
>scons -Q -f SConstruct.gcca2
avr-gcc -o main_avr.o -c -Os -Wall -std=gnu99 -fsigned-char -funsigned-bitfields
 -fpack-struct -fshort-enums -ffunction-sections -fdata-sections -mmcu=atmega328p main_avr.c
avr-gcc -o main.elf -Wl,--gc-sections -Wl,--cref -Wl,-M -Wl,-Map=main.map main_avr.o
   text    data     bss     dec     hex filename
   1668      14       6    1688     698 main.elf

>scons -Q -f SConstruct.gcca2 -c
Removed main_avr.o
Removed main.elf
Removed main.map
Removed main.bin
Removed main.eep
Removed main.hex
Removed main.lst
Perfect ! まとめ編:(AVR) toolを使う SConsには「tool」という考え方があって、固有の設定をまとめて 行うことが出来ます。すでに「'mingw'」というツールを使っています。 新しいツールを定義して「armgcc.py」という名前でカレントフォルダに保存します。 使うときはこうです。
env = Environment( tools = ['armgcc'], toolspath=['.'] )
...
...
マイコン間で共通/固有なものをこのファイルに記述します。 (要するになんでも可 :-) 例えば「*.hex, *.bin, *.lst」の生成やコンパイルオプションなどです。 AVRのコンパイルを「armgcc.py」というツールを使って記述してみます。(注2)
# SConscript.gcca3
# MSDOS/Cygwin/MSys(MinGW)

import os

TARGET = 'main'

########################
# MCU type
########################
MCU = 'atmega328p'

########################
# Create env object
########################
attrib = ['avr', 'AVR']  # or 'ARM7' or 'CORTEX-M3'
env = Environment( TOOLCHAIN = attrib, tools = ['armgcc'], toolpath  = ['.'] )

env['CCFLAGS'] +=      \
      '-Os             \
      -Wall -std=gnu99 \
      -fsigned-char -funsigned-bitfields -fpack-struct -fshort-enums   \
      -fno-inline-small-functions -fno-split-wide-types -mshort-calls  \
      -ffunction-sections -fdata-sections'                             \
      + ' -mmcu='+ MCU

MAP_NAME = TARGET + '.map'
env['LINKFLAGS'] +=      \
      '-Wl,--gc-sections \
       -Wl,--cref -Wl,-M -Wl,-Map=' + MAP_NAME

########################
# Build project
########################
prog_n = env.Program( TARGET, [ 'main_avr.c' ] )

########################
# Generate hex,bin etc.
########################
env.MakeBin(  prog_n )
env.MakeHex(  prog_n )
env.MakeList( prog_n )
env.MakeEep(  prog_n )
env.MakeSize( prog_n )

env.Clean( prog_n, MAP_NAME )
16,17行目でツールチェインとして「avr」を設定しています。 ARMなら「arm-none-eabi」等になります。 コンパイル時に「-gcc」、「-objdump」等がサフィックスとして補完されます。 ツールチェインを指定したので40行目〜44行目を簡略化することができました。 (armgcc.py 参照) * LPC2388(ARM7)でSConsを使う IF誌付録のLPC2388基板用のSConsファイルを書いてみます。 サンプルソースはirukaさんの、このページの http://hp.vector.co.jp/authors/VA000177/html/2009-04.html 「CQ出版:LPC2388 ledtestの問題発覚」という項目にある "ledtest.zip"/「LEDチカチカ」サンプルプロジェクトです。 コンパイルするファイルは「asmfunc.S, main.c, cpu_init.c」の3つです。 asmfunc.S, main.c: ARMコードでコンパイルします。 cpu_init.c    : Thumbコードでコンパイルします。 以下が最小限に絞ったSConstructファイルです。
# SConscript.arm7-g0
# MSDOS/Cygwin/MSys(MinGW)

import os

TARGET = 'ledtest'
attrib = ['arm-none-eabi', 'ARM7']  # or 'AVR' or 'CORTEX-M3'
CPU    = 'LPC2388'
arm    = Environment( TOOLCHAIN = attrib, tools = ['armgcc'], toolpath  = ['.'] )
arm['CCFLAGS']    += '-Os  -gdwarf-2                \
                      -Wall -std=gnu99 -msoft-float \
                      -fsigned-char -funsigned-bitfields -fpack-struct -fshort-enums \
                      -ffunction-sections -fdata-sections '
arm['CPPPATH']     = ['.']
arm['ASFLAGS']    += ' -c -x assembler-with-cpp '

thumb              = arm.Clone()
thumb['CCFLAGS']  += ' -mthumb '
thumb['LINKFLAGS']+= ' -Wl,--gc-sections -nostartfiles -Wl,-T%s-ROM.ld ' % ( CPU )

arm_obj_n          =   arm.Object( [ 'asmfunc.S', 'main.c'] )
thumb_obj_n        = thumb.Object( ['cpu_init.c'] ) 
prog_n             = thumb.Program( TARGET, arm_obj_n + thumb_obj_n )

thumb.MakeHex(  prog_n )
thumb.MakeSize( prog_n )
と、実質20行程度で記述できます。 (ただし、「armgcc.py」が別ファイルになっているのでアレですが。) 17行目: 「arm環境」の中身をClone()でコピーして「thumb環境」を作っています。       違いは「-mthumb オプション」だけです。(その他は「armgcc.py」の中) 21〜23行目:「arm環境」、「thumb環境」でコンパイル後、         「thumb環境」を使ってオブジェクトをリンクしています。         (リンカ設定をすれば「arm環境」を使ってリンク可能です。) 以下は上に加えて「*.map, *.lst」出力やコメント、オプションを増やしてみたもの。
# SConscript.arm7-g1
# MSDOS/Cygwin/MSys(MinGW)

import os

TARGET = 'ledtest'

########################
# MCU type
########################
attrib = ['arm-none-eabi', 'ARM7']  # or 'AVR' or 'CORTEX-M3'
CPU    = 'LPC2388'

########################
# Create env object
########################
arm   = Environment( TOOLCHAIN = attrib, tools = ['armgcc'], toolpath  = ['.'] )
arm['CCFLAGS']    +=                \
      '-Os  -gdwarf-2               \
      -Wall -std=gnu99 -msoft-float \
      -fsigned-char -funsigned-bitfields -fpack-struct -fshort-enums   \
      -ffunction-sections -fdata-sections '
arm['CPPPATH']     = ['.']
arm['CPPDEFINES']  = ['__WinARM__', ( '__WINARMSUBMDL_%s__' % CPU, 10) ]
arm['ASFLAGS']    += ' -c -x assembler-with-cpp '

thumb              = arm.Clone()
thumb['CCFLAGS']  += ' -mthumb '
MAP_NAME = TARGET + '.map'
thumb['LINKFLAGS']+=                           \
      '-Wl,--gc-sections                       \
       -Wl,--cref -Wl,-M -Wl,-Map=%s           \
       -nostartfiles -Wl,-T%s-ROM.ld ' % ( MAP_NAME, CPU )
thumb['LIBS']     = ['m']

########################
# Build project
########################
arm_obj_n   =   arm.Object( [ 'asmfunc.S', 'main.c'] )
thumb_obj_n = thumb.Object( ['cpu_init.c'] ) 
prog_n      = thumb.Program( TARGET, arm_obj_n + thumb_obj_n )

########################
# Generate hex,bin etc.
########################
thumb.MakeHex(  prog_n )
thumb.MakeList( prog_n )
thumb.MakeSize( prog_n )

thumb.Clean( prog_n, MAP_NAME )
実行してみます。
>scons -j2 -Q -f SConstruct.arm7-g1
arm-none-eabi-gcc -mcpu=arm7tdmi-s -mthumb-interwork -c -x assembler-with-cpp -o
 asmfunc.o asmfunc.S
arm-none-eabi-gcc -o cpu_init.o -c -mcpu=arm7tdmi-s -mthumb-interwork -Os -gdwarf-2 
-Wall -std=gnu99 -msoft-float -fsigned-char -funsigned-bitfields -fpack-struct 
-fshort-enums -ffunction-sections -fdata-sections -mthumb -D__WinARM__ 
-D__WINARMSUBMDL_LPC2388__=10 -I. cpu_init.c
arm-none-eabi-gcc -o main.o -c -mcpu=arm7tdmi-s -mthumb-interwork -Os -gdwarf-2
-Wall -std=gnu99 -msoft-float -fsigned-char -funsigned-bitfields -fpack-struct 
-fshort-enums -ffunction-sections -fdata-sections -D__WinARM__ 
-D__WINARMSUBMDL_LPC2388__=10 -I. main.c
arm-none-eabi-gcc -o ledtest.elf -mcpu=arm7tdmi-s -mthumb-interwork -Wl,--gc-sections 
-Wl,--cref -Wl,-M -Wl,-Map=ledtest.map -nostartfiles -Wl,-TLPC2388-ROM.ld
asmfunc.o main.o cpu_init.o -lm
   text    data     bss     dec     hex filename
   1416       0      12    1428     594 ledtest.elf

>scons -j2 -Q -f SConstruct.arm7-g1 -c
Removed asmfunc.o
Removed cpu_init.o
Removed main.o
Removed ledtest.elf
Removed ledtest.map
Removed ledtest.lst
Removed ledtest.hex
Good job ! 注:「LPC2388-ROM.ld」のテキストセクションは以下のように「KEEP」を追加します。
/* Section Definitions */
SECTIONS
{
  /* The first section which is used for code */
   .text :
  {
    KEEP(*(.VECTOR))    /* Exception vector table */
    KEEP(*(.text .text.*))    /* Program code */
    *(.gnu.linkonce.t.*)
    *(.glue_7)
    *(.glue_7t)
    *(.gcc_except_table)
    *(.rodata)        /* Read-only data (constants) */
    *(.rodata*)
    *(.gnu.linkonce.r.*)
    . = ALIGN(4);
  } > ROM
* LPC1768(Cortex-m3)でSConsを使う Cortex-m3の場合、CMSISドライバ・ライブラリがあった方が良いので、 フォルダの指定が少し冗長になります。 simple-cmsis-20110327.zip 当然SConsが動作する前提です。 「Cygwin/MSys/MinGW」は不要(のはず)です。 「MS-DOSコマンド・プロンプト窓」でコンパイルします。 gcc: 「Codesourcery G++ Lite for ARM」 専用です。 サンプル:LEDチカチカと「19200bps」でUART1に文字を出力します。 コンパイルは「simple-cmsis_xx」フォルダで $ scons とすればOKですが 以下のバッチファイルを使った方が良いです。 m.bat : 普通にコンパイルする。 mc.bat: ライブラリ以外をクリーン。 mm.bat: クリーン後にコンパイル。(ライブラリは除く) mcc.bat: オールクリーン(ライブラリもクリーンする) コンパイル結果は「build/src」フォルダに出力されます。 「複数フォルダのソースをコンパイル、ビルド結果は別フォルダ」というサンプルも 兼ねています。 Separating Source and Build Directories http://www.scons.org/doc/production/HTML/scons-user/c3340.html * toolファイル :「armgcc.py」 「armgcc.py」の中身については実験中です。 SConstructファイルに全部書いても同じです。 注意点は「CCFLAGS, LINKFLAGS, ASFLAGS」の中身を事前設定にしたため、使うときは
env['CCFLAGS']  += ' -Os '
env['LINKLAGS'] += ' --gc-sections '
env['ASFLAGS']  += ' -c '
のように「加算」形式にします。 たいした中身じゃので「=」で上書きしたほうがスッキリするかもです。 注意点: gccで「*.c, *.S, *.s」をコンパイルするときに 1,「C言語ファイル、ASMファイル、リンク」を全て「gcc」の引数指定で    行う方法、 2,個別に「gcc, as, ld」を起動する方法。 があります。 現在は「1.」を前提として 「CCFLAGS、」「LINKFLAGS」、「ASFLAGS」を設定しています。 特にアセンブラファイル「*.S」と「*.s」は意味が違うので微妙。 参考: http://stackoverflow.com/questions/4093843/running-avrdude-commands-as-scons-targets SCons ユーザーズガイド http://www.scons.org/doc/production/HTML/scons-user/index.html http://www.scons.org/wiki/UsingCodeGenerators BuildNumberProcessing http://www.scons.org/wiki/BuildNumberProcessing Copyright (C) 2010, audin All rights reserved.
posted by Copyright (C) avrin All Rights Reserved. at 11:14| Comment(0) | SCons | このブログの読者になる | 更新情報をチェックする

2010年12月02日

ARMマイコンでSConsを使う(2)

ARMマイコンでSConsを使う(2)
前回の ARMマイコンでSConsを使う(1) のつづき。
    ARMマイコンでSConsを使う(3) が次。

make/Makefile より簡単なSConsを試してみます。

このページで出てくるファイルは、
scons-note2-20101212.zip

SConsの開発コードも2010/08月に「Mercurial」に移行が決定したようで、
今まさに移行中な感じです。
http://www.scons.org/wiki/DVCSMigration

上で、「gitはWindows対応がまだ甘い、bazzarはこれといって突出したメリット
が見あたらない」と判断されています。(ざっくり訳です :-)
http://scons.tigris.org/ds/viewMessage.do?dsForumId=1268&dsMessageId=2684154

Mercurial自体がPython系なのでPythonの比重が高いプロジェクトは
Mercurial化される確率が高い感じです。(良いよい)

ちなみにPyMiteは2010/10月に「Mercurial」に移行しました。

* How to編 (2)
  * Hex, Bin, Listファイルを出力する / Builder
    表示を抑制する:「@」
  * Hex, Bin, Listファイルを出力する(2)/ Alias()
    ターゲットをまとめて実行する
  * Hex, Bin, Listファイルを出力する(3)/ command() 簡単
    ターゲットを選択して実行する
  *環境変数「SCONSFLAGS」を設定する
  *プラットフォームに依存しない"rm,cp,mv,mkdir,chmod"
  *プリビルドでC言語ファイルを生成する
    Command()ビルダで生成する:
    専用ビルダ(Builder)を作って生成する:
  *便利そうな関数
  * make で scons 一発! (爆)

Makefileにありがちな機能をSConsで実現してみるHow toです。
やり方は多数あると思いますが、自分で試して動作したものを記述します。

実行環境はWindows XPのMS-DOS窓、Cygwin/MSys/MinGWコンソールの
どれかです。コンパイラは特に書かない限り「gcc」。
「gcc」は仮の姿で、題名にもあるようにこれはそのうち「arm-none-eabi-gcc」に
変わっていきます。最終的には「Cygwin/MSys/MinGW」も排除して
「MS-DOS窓」+「arm-none-eabi-gcc.exe」になります。(微妙か)

* How to編 (2)

* Hex, Bin, Listファイルを出力する
例えば実行ファイル、「main.exe や main.elf」があればバッチファイルで
「*.hex,*.bin,*.lst」ファイルを作ることが出来るので、
無理にアレすることはないものの、
Cleanで削除できることや依存関系に組み込まれるのがメリットでしょうか。
@echo off
rem gen.bat
rem for Windows only
set target=main
set src=%target%.exe

set tc=
set OBJCOPY=%tc%objcopy
set OBJDUMP=%tc%objdump

%OBJCOPY% -O binary %src% %target%.bin
%OBJCOPY% -O ihex %src%  %target%.hex
%OBJDUMP% -h %src% -d  > %target%.lst
echo done !
上のバッチファイルでOKといえばOKか。 これをSConsに組み込んでみます。やり方はいくつかありますが、 「Builderを作る」という"大げさな"やり方にしてみます。(オイ 「hello.c,func.c」をコンパイルして「main」を出力する簡単なプロジェクトに追加してみます。 以前と同様、実行可能環境は2行目にコメントで書きます。 SConsのバージョンは今のところVer.2.0.1です。
# SConstruct.gcc10
# cygwin/MSys(MinGW)
import os

env = Environment( tools=[ 'mingw' ])
env['ENV'] = os.environ

env.Program( 'main', [ 'hello.c', 'func.c' ] )
最後の行(8行目)はワイルドカードを使って env.Program( 'main', Glob( '*.c' ) ) としてもOKです。 これに「*.hex,*.bin,*.lst」を出力するBuilderを追加すると、
# SConstruct.gcc11.1
# Cygwin/MSys(MinGW)
import os

env = Environment( tools=['mingw'] )
env['ENV'] = os.environ

prog_n = env.Program( 'main', [ 'hello.c', 'func.c' ] )

env['OBJCOPY']='objcopy'
env['OBJDUMP']='objdump'
env['SIZE']   ='size'
ext = '.elf'

# output .bin/.hex/.lst files
#                    suffix, src_sfix,  action
bld_dic ={ 'MakeBin':[ 'bin', ext,    '$OBJCOPY -O binary $SOURCE $TARGET']
          ,'MakeHex':[ 'hex', ext,    '$OBJCOPY -O ihex $SOURCE $TARGET']  
          ,'MakeLst':[ 'lst', ext,    '$OBJDUMP -h $SOURCE -d  > $TARGET'] 
          ,'MakeSize':[ None, ext,    '$SIZE $SOURCE']                     
}
for builder, cmd in bld_dic.iteritems():
    bld = Builder( action     = cmd[2]
                  ,suffix     = cmd[0]
                  ,src_suffix = cmd[1]  )
    env.Append( BUILDERS =  { builder:  bld } )

# output bin,hex,lst file
env.MakeBin( prog_n )
env.MakeHex( prog_n )
env.MakeLst( prog_n )

# display size information
env.MakeSize( prog_n )
となります。10行目以降を追加しました。 この例は、ついでに「size」情報も出力するようになっています。 実行してみます。
$ scons -Q -f SConstruct.gcc11.1
gcc -o func.o -c func.c
gcc -o hello.o -c hello.c
gcc -o main.exe hello.o func.o
size main.exe
   text    data     bss     dec     hex filename
   2564     684     192    3440     d70 main.exe
objcopy -O binary main.exe main.bin
objcopy -O ihex main.exe main.hex
objdump -h main.exe -d > main.lst
実行順序が意図通りではないですがOKとします。(^^; 再度ビルドコマンドを発行した後、Cleanしてみます。
$ scons -Q -f SConstruct.gcc11.1
size main.exe
   text    data     bss     dec     hex filename
   2564     684     192    3440     d70 main.exe

$ scons -Q -f SConstruct.gcc11.1 -c
Removed func.o
Removed hello.o
Removed main.exe
Removed main.bin
Removed main.hex
Removed main.lst
Good job ! ただし、Cygwin上ではsizeコマンドがエラーになりうまく行きません。orz Cygwin上では最後の行を無効にしてください。 MinGWなら大丈夫です。
# display size information
if env['PLATFORM'] != 'cygwin':
    env.MakeSize( prog_n )
最後の部分を上のようにすればcygwin判定が出来ます。 表示を抑制する: makeと同じようにコマンド実行の表示を「@」で抑制出来ます。(SConstruct.gcc11.2) (同様に「-」を付ければエラーを無視します。) コマンド定義部分を以下のように変更します。
bld_dic ={ 'MakeBin': [ 'bin', ext,    '@$OBJCOPY -O binary $SOURCE $TARGET']
          ,'MakeHex': [ 'hex', ext,    '@$OBJCOPY -O ihex $SOURCE $TARGET']  
          ,'MakeLst': [ 'lst', ext,    '@$OBJDUMP -h $SOURCE -d  > $TARGET'] 
          ,'MakeSize':[  None, ext,    '@$SIZE $SOURCE']                     
}
実行してみます。
$ scons -Q -f SConstruct.gcc11.2
gcc -o func.o -c func.c
gcc -o hello.o -c hello.c
gcc -o main.exe hello.o func.o
   text    data     bss     dec     hex filename
   3788    1044     244    5076    13d4 main.exe
Nice ! これで意図通りの表示になりました。 * Hex, Bin, Listファイルを出力する(2) 別なやり方です。例えばmakeで $ make hex とした時にHexファイルが生成される様にします。 Makefileだと以下のような感じです。
.PHONY: hex
hex: $(TARGET).hex

$(TARGET).hex: $(TARGET).elf
    $(OBJCOPY) -O ihex $< $@
clean:
    ...
    $(TARGET).hex
$ make flash でFlashライトするときも同じ感じだと思います。
# SConstruct.gcc12
# cygwin/MSys(MinGW)
import os

env = Environment( tools=[ 'mingw' ] )
env['ENV'] = os.environ

prog_n = env.Program( 'main', [ 'hello.c', 'func.c' ] )

env['OBJCOPY']='objcopy'
env['OBJDUMP']='objdump'

import os.path
exe_name = prog_n[0].path
base_name, ext = os.path.splitext( exe_name )
phony_dic = {                        
     'hex':'$OBJCOPY -O ihex   %s %s'
    ,'bin':'$OBJCOPY -O binary %s %s'
    ,'lst':'$OBJDUMP -h %s -d >   %s'
}
for ptarget, cmd in phony_dic.iteritems():
    out_name = base_name +'.' + ptarget
    a = env.Alias( ptarget, prog_n, cmd % ( exe_name, out_name ) )
    AlwaysBuild( a )
    Clean( prog_n, out_name )
上で追加したのは10行目以降ですが、最後の3行が実現部です。 Alias()の部分は2番目の引数に「prog_n」オブジェクトを指定することで 依存関係を構築しています。 (prog_nが未生成なら生成後に"コマンド文字列"が実行されます) Alias('hex', prog_n, コマンド文字列) AlwaysBuild( a )で「"コマンド文字列"」が「常」に実行される様にします。 Clean()でクリーンターゲットに追加しています。(prog_n 依存) 「"コマンド文字列"」が「常」に実行される点でmake hex と微妙に違いますが 十分OKです。
$ scons -Q -f SConstruct.gcc12
gcc -o func.o -c func.c
gcc -o hello.o -c hello.c
gcc -o main.exe hello.o func.o

$ scons -Q -f SConstruct.gcc12 hex
objcopy -O ihex main.exe main.hex

$ scons -Q -f SConstruct.gcc12 hex   2回目
objcopy -O ihex main.exe main.hex

$ scons -Q -f SConstruct.gcc12 -c
Removed func.o
Removed hello.o
Removed main.exe
Removed main.hex

$ scons -Q -f SConstruct.gcc12 hex
gcc -o hello.o -c hello.c
gcc -o func.o -c func.c
gcc -o main.exe hello.o func.o
objcopy -O ihex main.exe main.hex
Good job ! ターゲットをまとめて実行する 上の「'hex','bin','lst'」ターゲットをまとめて 'gen'ターゲットとして一度に実行する方法。
gen_a = env.Alias( 'gen', ['hex','bin','lst'])
AlwaysBuild( gen_a )
上の2行を「SConstruct.gcc12」の最後(forループの外側)に付け加えます。 (SConstruct.gcc13) Alias()の2番目の引数、['hex','bin','lst']配列は 直前のforループで生成する手もあります。
$ scons -Q -f SConstruct.gcc13 gen
objcopy -O ihex main.exe main.hex
objcopy -O binary main.exe main.bin
objdump -h main.exe -d > main.lst

$ scons -Q -f SConstruct.gcc13 -c
Removed func.o
Removed hello.o
Removed main.exe
Removed main.bin
Removed main.lst
Removed main.hex
Nice ! 当然、「'hex','bin','lst'」の個別実行も有効です。 * Hex, Bin, Listファイルを出力する(3)/ command() 簡単 Not Writing a Builder: the Command Builder http://www.scons.org/doc/production/HTML/scons-user/c3762.html 単発コマンドを実行するだけなら「Builder」を作るのは大げさなので 「Command()」関数(これもBuilder)を使った方が良いというやり方です。
# SConstruct.gcc14
# Cygwin/MSys(MinGW)
import os

env = Environment( tools=['mingw'] )
env['ENV'] = os.environ

prog_n = env.Program( 'main', [ 'hello.c', 'func.c' ] )

env['OBJCOPY']='objcopy'
env['OBJDUMP']='objdump'
env['SIZE']   ='size'

# output .bin/.hex/.lst files
import os.path
base_name, ext = os.path.splitext( prog_n[0].path )
env.Command( base_name + '.bin', prog_n, '$OBJCOPY -O binary $SOURCE $TARGET' )
env.Command( base_name + '.hex', prog_n, '$OBJCOPY -O ihex $SOURCE $TARGET'   )
env.Command( base_name + '.lst', prog_n, '$OBJDUMP -h $SOURCE -d  > $TARGET'  )
env.Command( None, prog_n, '$SIZE $SOURCE'                      )
確かに簡単(^^; 依存関係にも、クリーンターゲットにも対応してます。 実行ファイル名を直書きすればばもっと簡単になります。
# SConstruct.gcc14.1
# Cygwin/MSys(MinGW)
import os

env = Environment( tools=['mingw'] )
env['ENV'] = os.environ

prog_n = env.Program( 'main', [ 'hello.c', 'func.c' ] )

# output .bin/.hex/.lst files
import os.path
base_name, ext = os.path.splitext( prog_n[0].path )
env.Command( base_name + '.bin', prog_n, 'objcopy -O binary $SOURCE $TARGET' )
env.Command( base_name + '.hex', prog_n, 'objcopy -O ihex $SOURCE $TARGET'   )
env.Command( base_name + '.lst', prog_n, 'objdump -h $SOURCE -d  > $TARGET'  )
env.Command( None,               prog_n, 'size $SOURCE'                      )
実行してみます。
$ scons -Q -f SConstruct.gcc14
gcc -o func.o -c func.c
gcc -o hello.o -c hello.c
gcc -o main.exe hello.o func.o
size main.exe
   text    data     bss     dec     hex filename
   2564     684     192    3440     d70 main.exe
objcopy -O binary main.exe main.bin
objcopy -O ihex main.exe main.hex
objdump -h main.exe -d > main.lst

$ scons -Q -f SConstruct.gcc14 -c
Removed func.o
Removed hello.o
Removed main.exe
Removed main.bin
Removed main.hex
Removed main.lst
Good job ! ターゲットを選択して実行する: 常に必要なのは「size情報」と「*.hex」ファイルとし、 他のファイル(*.bin,*.lst)はコマンドラインから指定('gen')したときだけ 生成するようにしてみます。
# SConstruct.gcc15
# Cygwin/MSys(MinGW)
import os

env = Environment( tools=['mingw'] )
env['ENV'] = os.environ

prog_n = env.Program( 'main', [ 'hello.c', 'func.c' ] )

env['OBJCOPY']='objcopy'
env['OBJDUMP']='objdump'
env['SIZE']   ='size'

# output .bin/.hex/.lst files
import os.path
base_name, ext = os.path.splitext( prog_n[0].path )
env.Command( base_name + '.hex', prog_n, '$OBJCOPY -O ihex $SOURCE $TARGET'   )
env.Command( None, prog_n, '$SIZE $SOURCE'                      )
if 'gen' in COMMAND_LINE_TARGETS or env.GetOption('clean'):
    a = env.Command( base_name + '.bin', prog_n, '$OBJCOPY -O binary $SOURCE $TARGET' )
    b = env.Command( base_name + '.lst', prog_n, '$OBJDUMP -h $SOURCE -d  > $TARGET'  )
    Alias( 'gen', [a, b] )
これを実行してみます。
$ scons -Q -f SConstruct.gcc15
gcc -o hello.o -c hello.c
gcc -o func.o -c func.c
gcc -o main.exe hello.o func.o
size main.exe
   text    data     bss     dec     hex filename
   3788    1044     244    5076    13d4 main.exe
objcopy -O ihex main.exe main.hex

$ scons -Q -f SConstruct.gcc15 gen
objcopy -O binary main.exe main.bin
objdump -h main.exe -d > main.lst
'gen'を指定したとき「*.bin,*.lst」は生成されるものの「size情報」と「*.hex」は 生成されないという微妙な結果になりました。 以下の様に'gen'のAlias()に追加すれば意図通りになります。
c = env.Command( base_name + '.hex', prog_n, '$OBJCOPY -O ihex $SOURCE $TARGET'   )
d = env.Command( None, prog_n, '$SIZE $SOURCE'                      )
if 'gen' in COMMAND_LINE_TARGETS or env.GetOption('clean'):
    a = env.Command( base_name + '.bin', prog_n, '$OBJCOPY -O binary $SOURCE $TARGET' )
    b = env.Command( base_name + '.lst', prog_n, '$OBJDUMP -h $SOURCE -d  > $TARGET'  )
    Alias( 'gen', [a, b, c, d] )
なんか意地になってます。(爆 こうなる理由は「SConstruct」ファイルはPythonスクリプトながらSConsの解析順序が Pyhtonとは異なることに起因していると思われます。 2.5.2. SCons Functions Are Order-Independent http://www.scons.org/doc/production/HTML/scons-user/x355.html#AEN377 *環境変数「SCONSFLAGS」を設定する。 $ scons -Q のようなオプションを毎回付けるのを避けたい場合、 環境変数「SCONSFLAGS」に設定します。 ('-Q'は出力表示を一部抑制します。) Windowsの場合、環境変数「SCONSFLAGS」を追加して値に「-Q」を設定。 bash系は「.bashrc」とかに「export SCONSFLAGS='-Q'」等。 「-Q」を付けると「configure」時の出力も抑制されます。 *プラットフォームに依存しない"rm,cp,mv,mkdir,chmod" プリビルドやポストビルドで使う場合がある「rm,cp,mv,mkdir,chmod」というコマンド群。 SConsはOSに依存しない相当関数(ファクトリ)を用意しています。 Copy() Delete() Move() Touch() Mkdir() Chmod() これらの関数は「Actionオブジェクト」を返す「ファクトリ」と呼ばれます。 定義時には実行されず、Actionオブジェクトだけを返します。 実行するにはCommand()ビルダやExecute()関数の引数に渡します。 Actionオブジェクト:   上のCopy()ファクトリなどの返り値。Action()関数の返り値。 Command()ビルダ:   Actionオブジェクトや「文字列で指定されたコマンド」を実行します。   $SOURCE、$TARGET情報を基に依存関係システムに組み込まれます。 Execute()関数:   指定された引数を実行します。返り値でエラー判定が出来ます。 注意点はCommand()関数が「ビルダ」である点で、クリーンターゲットにも 組み込まれます。Copy()で作成した「$TARGETファイル」は クリーン時に削除されます。(Mkdir()で作成したフォルダは削除されない) 14.5. Creating a Directory: The Mkdir Factory http://www.scons.org/doc/production/HTML/scons-user/x3056.html *プリビルドでC言語ファイルを生成する 「main()関数がビルド(env.Program())される前に、プリビルドで C言語ソースファイルを生成し、依存関係に組み込む」 という、要求があったとします。 やり方は、 1,単発処理としてCommand()ビルダで生成する。 2,複数ファイルの場合、専用ビルダを作って生成する。 という方法があります。 Command()ビルダで生成する: 例として「option.txt」から「option.c」を生成してみます。 コンバータを「opt2c.exe」とすれば $ opt2c option.txt option.c でC言語ファイルを生成します。 「opt2c.exe」を作るのはめんどくさいので、便宜的に「cpコマンド」で代用します。
# SConstruct.gcc18
# Cygwin/MSys(MinGW)
import os

env = Environment( tools=['mingw'] )
env['ENV'] = os.environ

# pre-build (1)
pre1_n = env.Command( 'option.c', 'option.txt', 'cp $SOURCE $TARGET' )

prog_n = env.Program( 'main', [ 'hello.c', 'func.c', pre1_n ] )
9行目を追加しました。 Command( target, source, command )という順序です。 生成された「'option.c'」はNodeオブジェクトとして「pre1_n」に得られるので これをProgram()の引数に追加してコンパイルします。 依存関係が明確なので記述順序に影響されませんが、 実行順に記述した方が分かり易いです。 (-:
$ scons -Q -f SConstruct.gcc18
gcc -o func.o -c func.c
gcc -o hello.o -c hello.c
cp option.txt option.c
gcc -o option.o -c option.c
gcc -o main.exe hello.o func.o option.o

$ scons -Q -f SConstruct.gcc18 -c
Removed func.o
Removed hello.o
Removed option.c
Removed option.o
Removed main.exe
クリーン時には、依存関係により 「生成されたC言語ファイル」も正しく削除されています。 専用ビルダを作って生成する: Commnad()ビルダは拡張子ベースのワイルドカードが使えないので 複数のファイルをコンバートする場合には専用ビルダを作ります。 例えば「*.conf」から「*.c」を生成するコンバータを「conf2c.exe」とすると、 Makefileなら %.c: %.conf conf2c.exe @< $@ となります。 (以下はcpコマンドで代用します。)
# SConstruct.gcc19
# Cygwin/MSys(MinGW)
import os

env = Environment( tools=['mingw'] )
env['ENV'] = os.environ

# pre-build (1)
pre1_n = env.Command( 'option.c', 'option.txt', 'cp $SOURCE $TARGET' )

# pre-build (2) 
# convert *.conf to *.c
bld = Builder( action = 'cp $SOURCE $TARGET'
              ,suffix     = '.c'             
              ,src_suffix = '.conf'           
              ,single_source = True )
env.Append( BUILDERS =  { 'Conf2c':  bld } )
pre2_n = env.Conf2c( Glob( '*.conf' ) )

prog_n = env.Program( 'main', [ 'hello.c', 'func.c', pre1_n, pre2_n ] )
13〜18行目が追加した部分です。この「形」を覚えるしかないと思います。 13行目:「Builder」を定義します。   「キモ」は16行目で「single_source = True」の記述がないと意図通りに   動作しませんでした。 orz 17行目:定義したBuilderを「Conf2c」という名前で「env環境」に登録します。 18行目:登録した「Conf2c Builder」を使って複数のC言語ファイルを生成しています。 20行目:生成された「pre2_n」オブジェクトをProgram()に追加してコンパイルさせます。 (「pre2_n」オブジェクト」の中身はC言語ファイル名のNodeオブジェクト配列。) 実行してみます。
$ scons -Q -f SConstruct.gcc19
gcc -o func.o -c func.c
gcc -o hello.o -c hello.c
cp option.txt option.c
gcc -o option.o -c option.c
cp opt1.conf opt1.c
gcc -o opt1.o -c opt1.c
cp opt2.conf opt2.c
gcc -o opt2.o -c opt2.c
gcc -o main.exe hello.o func.o option.o opt1.o opt2.o

$ scons -Q -f SConstruct.gcc19 -c
Removed func.o
Removed hello.o
Removed option.c
Removed option.o
Removed opt1.c
Removed opt1.o
Removed opt2.c
Removed opt2.o
Removed main.exe
Perfect ! Re: [scons-users] How to do this in scons? http://scons.tigris.org/ds/viewMessage.do?dsForumId=1272&dsMessageId=2677881 *便利そうな関数 WhereIs()関数:        もし存在するなら実行ファイルのフルパスを返します。        存在しなければ「None」を返します。        WhereIs('objcopy')とすると「objcopy」のフルパスが得られます。 これを使えばツールチェインの自動判別ができます。 Depends()関数:        Depends('src.c', 'src.h')で依存関係を設定します。 print env.Dump():        envに設定されている環境構築変数を表示します。        独自のコンパイラや変数の状況確認に使える。        env[]の中身が全部表示される。 * make で scons 一発! (爆) よく、「make 一発でコンパイルできる」という言い方をします。 SCons用の「SConstruct」ファイルしかないのに、思わず $ make と打ってしまった、あなたに。て、自分か。
# Makefile
# for SCons
all:
	@scons -Q
clean:
	@scons -Q -c
という「Makefile」をひそませておけば誰にも気づかれずに SConsに移行できます。 (「 slが走る 」手もある。← 気づかれる) これが 「make で scons 一発!」ワザです。(爆) 当然、逆の 「scons で make 一発!」ワザもあります。(オイ これです。
# SConstruct
# for make
import os

env = Environment( )
env['ENV'] = os.environ

if not env.GetOption( 'clean' ):
	env.Execute( 'make' )
else:
	env.Execute( 'make clean' )
Nice joke ! (ちなみに、どちらも意図通りに動きます。) http://stackoverflow.com/questions/4093843/running-avrdude-commands-as-scons-targets 参考: SCons ユーザーズガイド http://www.scons.org/doc/production/HTML/scons-user/index.html http://www.scons.org/wiki/UsingCodeGenerators BuildNumberProcessing http://www.scons.org/wiki/BuildNumberProcessing Copyright (C) 2010, audin All rights reserved.
posted by Copyright (C) avrin All Rights Reserved. at 21:15| Comment(0) | SCons | このブログの読者になる | 更新情報をチェックする

2010年11月27日

ARMマイコンでSConsを使う(1)

ARMマイコンでSConsを使う(1)
* Synopsis(概要) SCons
* VC++で「hello.c 」を1行でコンパイル!?
* VC++でdllを作る。2行
*「bcc32.exeならどうなる?」にハマる!
* SConsのキモ!?
* コンパイル・オプションを指定する
  gccでコンパイル:
  別な書き方:
* SConsは賢い!?

* How to編(1)
  *ソースフォルダ構成
  *最初のコンパイルスクリプトSConstruct.gcc5
  *ライブラリのCleanを阻止する。
  *特定のファイルをCleanしたい
  *ライブラリを再構築する
  *./configure機能を使う

*Synopsis(概要) SCons
SConsはmake/Makefileの代替になるビルドツール(コマンドライン型)です。
PyMite(Python)eLua(Lua)でも採用されています。

makeの代用ツールは他にもたくさんあるようですが、
(CMake,Rake,Ant,OMake, etc.)
ここでは既にインストール済みのSConsを試してみます。

このページで出てくるファイル群です。
scons-note1-20101212.zip

SConsは「依存関係を書く必要がない」のでかなり簡単に書けます。
ぶっちゃけな話、SConsの記述ファイルは「Makefile」から
「依存関係の記述をカットしたような」雰囲気になると
思って良いと思います。
そうすると「残る」のはざっくり
1,コンパイルオプションの指定、
2,ビルドするファイルの指定。
3,その他
になります。
依存関係を考える必要がないので、
「ひたすら○○を指定する」という単純なお仕事に変わります。(爆

で、「hello.c」をコンパイルする記述は以下のように1行で済むという触れ込みです。
Program('hello.c')
ファイルを1つ指定しているだけですね。
Program('my_target', Glob( '*.c') )
と、書けばカレントフォルダのC言語ファイルを全部コンパイルして 「my_target.exe」が出来ます。依存関係もcleanターゲットも不要です。 全てSConsが面倒を見てくれます。 以下の基本条件はeLua(Lua)のページでインストールした「Windows版 SCons」を 「Windows XPのMS-DOS窓」から使います。 最終目標は、 ARM/Cortex-M3/CMSISのLPC1768/STM32用の 「SConsビルドスクリプト」を作ることです*VC++で「hello.c 」を1行でコンパイル!? ARMマイコンの前に、お手軽なWindows用のコンパイラで小手調べしてみます。 (Visual Studio 2010 C++ (VC++)はインストール済みとします。 ) (当然、無料版のやつです。) 3つのソースで構成されるファイル群をコンパイルしてみます。 「hello.c」「func.c」「func.h」です。 ビルド用の「SConstruct」ファイルも同じフォルダに作ります。 「hello.c」は「func.h」の「NUMBER定数」に依存しています。
/* hello.c */
#include "func.h"
int main(){
   print_hello( NUMBER );
   return 0;
}
/* func.c */
#include <stdio.h>
void print_hello( int num ){
   printf( "\n  Hello world ! %d", num );
}
/* func.h */
#define NUMBER 999
void print_hello( int num );
これらをコンパイルするSConsスクリプトファイル「SConstruct」は、
# SConsruct.vc1
# vc++
Program('main', [ 'hello.c', 'func.c' ] )
1行で書けるんです。(#の行はコメントです。) コンパイルするファイル(*.c)とターゲット名「'main'」を指定するだけです。 (main.exeが生成されます) この1行ポッキリの記述で 1,ソースファイルの更新確認      →コンパイル 2,ヘッダファイルの依存関係、更新確認 →コンパイル 3,クリーンターゲットの機能 と、Makefileだと煩雑になりがちな内容をSConsが全部やってくれるのです。
# SConstruct
c_src   = Glob( 'c_dir/*.c' )
cpp_src = Glob( 'cpp_dir/*.cpp' )
Program( 'out/main', c_src + cpp_src )
例えば上のようにワイルドカードで「根こそぎ」指定することも出来ます。 フォルダの指定も可能です。上の場合「out/main.exe」が出来ます。 makeの「Makefile」に相当するファイルが「SConstruct」ファイルになります。 (「SConstruct」ファイルは「Pythonスクリプトファイル」そのものです。)
>scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
cl /Fofunc.obj /c func.c /nologo
func.c
cl /Fohello.obj /c hello.c /nologo
hello.c
link /nologo /OUT:main.exe hello.obj func.obj
scons: done building targets.
上のようにSConsコマンドでコンパイルすると「main.exe」が生成されます。 「func.c」、「hello.c」がVC++(cl.exe)でコンパイル、link.exeでリンクされます。 ここで、「func.h」の「NUMBER定数」を「100」に変更して再度SConsを実行してみます。
>scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
cl /Fohello.obj /c hello.c /nologo
hello.c
link /nologo /OUT:main.exe hello.obj func.obj
scons: done building targets.
すると、 「func.h」に依存している「hello.c」だけが再コンパイル・リンクされます。 お見事! クールかな? (make clean に相当するのは "scons -c"です。 "scons -f SConstruct.vc -c" 等) *VC++でdllを作る。2行 今度は「hello_dll.c」「func_dll.c」の2つのファイルを用意します。 「func_dll.c」 : このファイルをコンパイルして「myfunc.dll」を生成します。 「hello_dll.c」: myfunc.dll内の関数を呼び出します。 「main.exe」が生成される実行ファイル名です。
/* func_dll.c */
/* for vc++ */
#include <stdio.h>

__declspec(dllexport) void print_hello( int num ){
	printf("Hello, world! %d\n", num);
}
/* hello_dll.c */
/* for vc++ */
#include <windows.h>
#include <stdio.h>

typedef void (*TPRINT_HELLO)( int num );
HINSTANCE hdll;
TPRINT_HELLO print_hello;

int main(){
  hdll = LoadLibrary("myfunc.dll");
  if (hdll == NULL){
	puts("[ dll not found ]");
	return 1;
  }
  print_hello = (TPRINT_HELLO)GetProcAddress( hdll, "print_hello");
  if( NULL == print_hello ){
	puts("[ dll not found ]");
  }
  else{
	print_hello( 6 );
  }
  FreeLibrary( hdll );
  return 0;
}
SConstructは以下のように2行ポッキリです。
# SConsruct.vc2

SharedLibrary( target='myfunc' ,source=['func_dll.c'] )
Program( 'main', [ 'hello_dll.c'] )
コンパイルしてみます。
>scons -f SConstruct.vc2
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
cl /Fofunc_dll.obj /c func_dll.c /nologo
func_dll.c
cl /Fohello_dll.obj /c hello_dll.c /nologo
hello_dll.c
link /nologo /OUT:main.exe hello_dll.obj
link /nologo /dll /out:myfunc.dll /implib:myfunc.lib func_dll.obj
   ライブラリ myfunc.lib とオブジェクト myfunc.exp を作成中
scons: done building targets.
お見事! *「bcc32.exeならどうなる?」にハマる! 上のように 1行、2行で書ける話は、うますぎる気がします。 (オイ VC++の例は、ある意味デフォルトでうまく行くように「しくまれていた」のです。 (「仕込み」の問題ともいう。) Windows用に「Borland の bcc32.exe」という無料のC/C++コンパイラがあるので、 最初に出てきた「hello.c,func.c,func.h」を同様にコンパイルしてみます。 (ソースコードの内容は全く同じです。) 以下が「bcc32.exe」用のSConsスクリプトです。
# SConstruct.bcc1
import os

env = Environment( tools=[ 'bcc32', 'ilink32'] )
env['ENV'] = os.environ

env.Program( 'main', [ 'hello.c', 'func.c' ] )
少し違いますが実質2行程度増えているだけです。 上を「SConstruct.bcc1」に保存してコンパイルしてみます。 bcc32.exeへのPATHは通っているとします。
>scons -f SConstruct.bcc1
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
bcc32 -q -c -ofunc.obj func.c
func.c:
bcc32 -q -c -ohello.obj hello.c
hello.c:
bcc32 -q -emain.exe hello.obj func.obj
scons: done building targets.
お見事! と、書いてますが、実質2行追加するのに「かなり大変でした」orz 以下のように書いても同じです。
# SConsruct.bcc1.1
# bcc32
import os

env = Environment( tools=[ 'bcc32', 'ilink32'] , ENV = os.environ )
env.Program( 'main', [ 'hello.c', 'func.c' ] )
* SConsのキモ!? もう一度「SConstruct.bcc1」ファイルを見てみます、
# SConstruct.bcc1
import os

env = Environment( tools=['bcc32', 'ilink32'] )
env['ENV'] = os.environ

env.Program( 'main', [ 'hello.c', 'func.c' ] )
4行目がある意味「キモ」で、5行目は「FAQ」になります。 4行目: 'bcc32, ilink32'は実行ファイル'そのもの'を 指定しているのではなく「'bcc32.py','ilink.py'」という設定ファイル を指定しています。この「*.py」ファイルは、 C:\Python26\Lib\site-packages\scons-2.0.1\SCons\Tool というフォルダに存在します。(インストール場所は各自読み替えてください) このフォルダの「*.py」ファイル群をみれば、これらが かなりの「キモ」だということが分かると思います。(爆 「'bcc32.py','ilink.py'」というファイルが 'bcc32.exe','ilink32.exe'に関するエトセトラな設定をやっています。 従って、ARM用のgccコンパイラの場合、 例えば「armgcc.py」というファイルがあるかというと「ない」ので 自分で作るか、相当する内容を「SConstruct」内に記述することになります。 あるいは、動作内容の近いものをtools=[]で読み込んで、違う部分を 一部オーバーライドするような感じでも良いかもしれません。 5行目: env['ENV'] = os.environ これはWindowsの「環境変数」を取り込む指定です。 SConsは「実行パスPATHを自動では取り込まない」ので、この記述が必要です。 これがないと「bcc32.exe」が見えないのです。(この話はFAQです) ただしこの場合「PATH変数」だけでなく 全ての環境変数を取り込んでいます。 PATH変数に限定するときは env['ENV'] = {'PATH' : os.environ['PATH']} とします。 エッセンス: 上の高々数行の記述にSConsのエッセンスが凝縮されていて、 後は上で書いたように 「コンパイルオプションの指定」 「ビルドファイル群の指定」 を必要に応じて追加するだけです。 ちなみに「'bcc32.py','ilink.py'」の出来は今ひとつの感があります。(-: * コンパイル・オプションを指定する ここまででSConsの基本形が分かったと思います。 実質的なビルド記述はあくまで env.Program( 'main', [ 'hello.c', 'func.c' ] ) の1行です。 あとは「次々とコンパイル・オプション」を指定していくだけです。 具体的には以下の方(かた)のページが非常に参考になりました。 SCons/Makefileを書くのに疲れた人のためのSCons 以下、「ありがちな」オプションを指定してみます。 コンパイラの種類に依存しないように書けるのがミソです。 指定の仕方はいろいろあって、ここでは env['CPPPATH'] = ['dir1','dir2'] の形式を使います。上はインクルードパスを指定しています。
env['CCFLAGS']   = '-Os -Wall -ffunction-sections'
env['LINKFLAGS'] = '-Wl,--gc-sections -Wl,-mcpu=cortex-m3'
env['LIBS']      = ['m', 'opengl', 'cmsis']
env['LIBPATH']   = ['lib_dir','cmsis_dir']
env['CPPPATH']   = ['inc_dir','dir2','include']
この中で特に重要なのが「LIBPATH」、「CPPPATH」で、 ライブラリパスを「-Lxx」、 インクルードパスを「-Ixx」の形で 「LINKFLAGS」や「CCFLAGS」に書いてはいけません。 書いてしまうと「依存関係の解決が正しく行われません」 これ思いっきりハマりました。(爆 例えばライブラリのコンパイルが全くされなかったりします。 上の env['LIBS'] = ['m', 'opengl', 'cmsis'] の右辺の記述は、 env['LIBS'] = Split('m opengl cmsis') とシンプルに記述することも出来ます。
env['CPPDEFINES']=['_DEBUG_','__USE_CMSIS',('FREQ',8000)]
上の記述はgccなら「-D」オプションに相当し、
-D_DEBUG_
-D__USE_CMSIS
-DFREQ=8000
に展開されます。 gccでコンパイル: ここで、コンパイル環境を一時的に「Cygwin/Msys(Mingw)」のLinux like環境に変更します。 コンパイル・オプション指定に「VC++」、「bcc32.exe」を使っても 自分的にあまり役立たないからです。 ソースコードは今までの「hello.c,func.c,func.h」がそのまま使えます。
# SConstruct.gcc0
import os

env = Environment( tools=['mingw'] )
env['ENV'] = os.environ

env.Program( 'main', [ 'hello.c', 'func.c' ] )
上が「Cygwin」、「Msys(Mingw)」の両方で動作するSConstructスクリプトです。 「Cygwin」だけなら4行目の「tools=['mingw']」は必要ありません。 今までと同様にコンパイル可能です。
$ scons -Q -f SConstruct.gcc0
gcc -o func.o -c func.c
gcc -o hello.o -c hello.c
gcc -o main.exe hello.o func.o
上は「-Qオプション」を付けて表示出力を簡略化しています。 これに「コンパイル・オプション」を追加してみます。
# SConstruct.gcc1
import os

env = Environment( tools=['mingw'] )
env['ENV'] = os.environ

env['CCFLAGS']   = '-Os -Wall -ffunction-sections -fdata-sections'
env['LINKFLAGS'] = '-Wl,--gc-sections -Wl,--warn-once'
env['LIBS']      = ['m', 'bfd']
env['LIBPATH']   = ['libdir', 'libdir2']
env['CPPPATH']   = ['.', 'inc/dir', 'include']
env['CPPDEFINES']= ['_DEBUG_','__USE_CMSIS',('FREQ',8000)]

env.Program( 'main', [ 'hello.c', 'func.c' ] )
7〜12行目にオプションを追記しただけです。 コンパイルしてみます。
$ scons -Q -f SConstruct.gcc1
gcc -o func.o -c -Os -Wall -ffunction-sections -fdata-sections -D_DEBUG_ -D__USE
_CMSIS -DFREQ=8000 -I. -Iinc/dir -Iinclude func.c
gcc -o hello.o -c -Os -Wall -ffunction-sections -fdata-sections -D_DEBUG_ -D__US
E_CMSIS -DFREQ=8000 -I. -Iinc/dir -Iinclude hello.c
gcc -o main.exe --gc-sections --warn-once hello.o func.o -Llibdir -Llibdir2 -lm
-lbfd
お見事! どこにも「gcc」を使えと書いてないのに、 環境を察知してデフォルトで「gcc」でコンパイルされる点が好印象です。 別な書き方: オプション指定に限らず、いろんな書き方ができます。 以下のオプション指定は上と同じ意味です。
# SConstruct.gcc2
import os

env = Environment( tools=['mingw'] )
env['ENV'] = os.environ

env.MergeFlags( '-Os -Wall -ffunction-sections -fdata-sections \
 -Wl,--gc-sections -Wl,--warn-once            \
 -lm -lbfd                                    \
 -Llibdir -Llibdir2                           \
 -I. -Iinc/dir -Iinclude                      \
 -D_DEBUG_ -D__USE_CMSIS -DFREQ=8000          \
')

env.Program( main, [ hello.c, func.c ] )
素(す)の「CFLAGS」にリンカオプションも含めて指定する形です。(MergeFlags) この書き方の場合コンパイルする前にオプション文字列がパースされて env['LINKFLAGS'] = '-Wl,--gc-sections -Wl,--warn-once' env['LIBS'] = ['m', 'bfd'] 等と等価な変換が行われるので、上で述べた「-Lxx,-Ixx」を含めても 問題ありません。 表記を区切る時は env.MergeFlags( [ '-Os, '-Wall', '-ffunction-sections'] ) こんな感じに書きます。 別な書き方(2): さらに以下のような書き方もできます。
# SConsruct.gcc1.1
import os

env = Environment(
     tools=[ 'mingw']
    ,ENV        = os.environ
    ,CCFLAGS    = '-Os -Wall -ffunction-sections -fdata-sections'
    ,LINKFLAGS  = '-Wl,--gc-sections -Wl,--warn-once'
    ,LIBS       = ['m', 'bfd']
    ,LIBPATH    = ['libdir', 'libdir2']
    ,CPPPATH    = ['.', 'inc/dir', 'include']
    ,CPPDEFINES = ['_DEBUG_','__USE_CMSIS',('FREQ',8000)]
)
env.Program( 'main', [ 'hello.c','func.c'] )
上のように簡潔に書ける。 * SConsは賢い!? SConsは後発なだけあってmakeで「?」だった部分が改良されていて おもしろい。 Makefileでフルビルド!?:  Makefileを書いていると 「Makefileを修正したら勝手にフルビルドしてほしい」 と当然のように思うわけですが、これをやるには依存関係の底辺の数カ所に 「Makefile」を依存させるような記述が必要になる。 これをやってしまうと 「Makefile中のコメントを修正しただけでフルビルドってしまいます。」 めんどくさいのでバッチファイルで「make clean ; make」とか書いてるけど。 (make -B も使います) SCons: やるなぁ〜〜!  SConsはあちこちで書かれているようにオブジェクトファイルのハッシュ値の変化を  見ているのでC言語のソースコードのコメントが変わるとコンパイルはかかるものの  オブジェクトファイルに変化がないので「リンクはかからない」のだ。  リンクファイルが多数あって長時間かかるときは非常に助かる機能だ。  じゃあ、「SConstructファイルが変更された時」はどうなるかというと、  コメント修正では当然無反応。    コンパイルオプションの部分を変えてみると、  な、なんと、なんと、フルビルドがかかります。   参りました。 m(__)m じゃあ、何をどこまで見ているのか見てみた。 コンパイルオプションも「スペースを除いたハッシュ値」を見ているようだ。 オプションの「並び順」を変えるだけで再コンパイルかかりましたので。 逆に言えば「並び順が同じなら」原型をとどめていない変化にも無反応(汗 て、いうか。 makeの場合は「変更時間」だけに注意を払えば良かったため ある意味「思考が単純だった」。 SConsは賢くなって「Makeタイミングが違う感じで」一瞬とまどってしまう 場合が多々あります。(多分正しい挙動だと思うけど) (注)最近は、このSConsの賢さに慣れてしまってMakeはもう使いたくない感じです。 そんな、あなたに て、自分か。 Decider('make') http://www.scons.org/doc/production/HTML/scons-user/c824.html#AEN898 という機能があって、これは「SConsの挙動をMake互換に変更します」 タイムスタンプだけを見るようになります。 この機能はデバッグ用に使おうかな。 ちなみにSConsは、 コンパイラのバージョンが変わるとそれを認識して「フルコンパイル」がかかります。 賢すぎて 脱帽です。 m(__)m *How to 編  今までmakeでやっていたことをどう実現するかを記述します。  ただし、実現方法は多数あってそのうちの一つですが  最適解かどうかは不明です。 *ソースフォルダ構成 現実に近いフォルダ構成をとります。 ビルドするソースは以下の様です。内容は不問。 「src」フォルダ:   メイン関数群 「src/lib」フォルダ: メインから呼び出すライブラリ          「libmylib.a」を生成してメイン側とリンクします。 SConstruct.xxx : 各種ビルドスクリプト。          「scons -f SConstruct.xxx」の形で呼び出します。 top | SConstruct.xxx | \---src | main.c | sub.c | sub.h | \---lib func1.c func1.h func2.c func2.h コンパイル結果の実行ファイルは「top」フォルダ(カレント)に生成します。 Windows環境なら VC++で「main.exe, src/lib/mylib.lib 」が生成されます。 Cygwin/MinGWならgccで 「main.exe, src/lib/libmylib.a」が生成されます。 実行可能環境は2行目に書いておきます。 「# vc++/cygwin」等。 *最初のコンパイルスクリプトSConstruct.gcc5
# SConstruct.gcc5
# vc++/cygwin
import os

env = Environment( )

src_dir = 'src'
lib_dir = src_dir+'/lib'

env['LIBS']    = 'mylib'                # Library
env['LIBPATH'] = [ lib_dir ]            # Library path
env['CPPPATH'] = [ lib_dir , src_dir ]  # Include path

# library builder
lib_obj_n  = env.Object(  Glob( lib_dir + '/*.c')     )
lib_a_n    = env.Library( lib_dir+'/mylib', lib_obj_n )

# main builder
prog_n = env.Program( 'main',  Glob( src_dir + '/*.c') )
上がコンパイル用のスクリプトの最初のバージョンです。 15行目:Object()関数はソースコードの配列(Glob()の返り値)を引数にとりオブジェクトファイルを生成します。      (*.o or *.obj)       Object()関数の返り値はNodeオブジェクトです。(注1) 16行目:15行目で生成されたNodeオブジェクトを引数にとりライブラリを生成します。      (*.a or *.lib) 19行目: 本体のコンパイル。生成されたライブラリもリンクされます。
$ scons -f SConstruct.gcc5
gcc -o src/main.o -c -Isrc/lib -Isrc src/main.c
gcc -o src/sub.o -c -Isrc/lib -Isrc src/sub.c
gcc -o src/lib/func1.o -c -Isrc/lib -Isrc src/lib/func1.c
gcc -o src/lib/func2.o -c -Isrc/lib -Isrc src/lib/func2.c
ar rc src/lib/libmylib.a src/lib/func1.o src/lib/func2.o
ranlib src/lib/libmylib.a
gcc -o main.exe src/main.o src/sub.o -Lsrc/lib -lmylib
上のようにビルドされます。。 アーカイバ(ar)とranlibが勝手に動いて、至れり尽くせりな感じです。 *ライブラリのCleanを阻止する。 上で「scons -f SConstruct.gcc5 -c」でプロジェクトをクリーンすると ライブラリも含めて全部削除されてしまいます。(注2)
$ scons -f SConstruct.gcc5 -c
Removed src/main.o
Removed src/sub.o
Removed src/lib/func1.o
Removed src/lib/func2.o
Removed src/lib/libmylib.a
Removed main.exe
ライブラリはCleanターゲットからはずしたいのです。 これはNoClean()関数で実現できます。 env.NoClean( lib_a_n + lib_obj_n ) 引数には削除を阻止したい「ファイルやNodeオブジェクト」を列挙します。
# SConstruct.gcc6
# vc++/cygwin
import os

env = Environment( )

src_dir = 'src'
lib_dir = src_dir+'/lib'
env['LIBS']    = 'mylib'                # Library
env['LIBPATH'] = [ lib_dir ]            # Library path
env['CPPPATH'] = [ lib_dir , src_dir ]  # Include path

# library builder
lib_obj_n  = env.Object(  Glob( lib_dir + '/*.c')     )
lib_a_n    = env.Library( lib_dir+'/mylib', lib_obj_n )
env.NoClean( lib_a_n + lib_obj_n )

# main builder
prog_n = env.Program( 'main', Glob( src_dir + '/*.c') )
16行目に追加しました。これだけでもそれなりに実用的なビルドスクリプトに なっていると思います。
$ scons -f SConstruct.gcc6 -c
Removed src/main.o
Removed src/sub.o
Removed main.exe
上のようにライブラリはクリーンされません。 *特定のファイルをCleanしたい 今度は、「*.map」ファイルなどの「派生ファイル」をCleanターゲットに 含めたい場合です。 makeなら「rm *.map」などを追加するだけですが。
# SConstruct.gcc7
# cygwin/MinGW
import os

env = Environment( tools=['mingw'] )

src_dir = 'src'
lib_dir = src_dir+'/lib'
env['LIBS']      = 'mylib'                # Library
env['LIBPATH']   = [ lib_dir ]            # Library path
env['CPPPATH']   = [ lib_dir , src_dir ]  # Include path
env['LINKFLAGS'] = '-Wl,--cref -Wl,-M -Wl,-Map=main.map'

# library builder
lib_obj_n  = env.Object(  Glob( lib_dir + '/*.c')     )
lib_a_n    = env.Library( lib_dir+'/mylib', lib_obj_n )
env.NoClean( lib_a_n + lib_obj_n )

# main builder
prog_n = env.Program( 'main', Glob( src_dir + '/*.c') )

# add clean files
Clean( prog_n, 'main.map' )

12行目で「main.map」を生成するオプションを追加しています。 23行目でClean()関数の引数に「prog_n」オブジェクトと「main.map」を 指定しています。 これで「main.exe」(prog_nオブジェクト)が削除されるタイミングで 「main.map」も削除されます。(scons -c のタイミング)
$ scons -Q -f SConstruct.gcc7 -c
Removed src/main.o
Removed src/sub.o
Removed main.exe
Removed main.map
*ライブラリを再構築する 上でライブラリのCleanを阻止したので、ライブラリだけ個別にCleanする 方法です。その後ビルドすれば再構築と同じです。
# SConstruct.gcc8
# cygwin/MinGW
import os

env = Environment( tools=['mingw'] )

src_dir = 'src'
lib_dir = src_dir+'/lib'
env['LIBS']      = 'mylib'                # Library
env['LIBPATH']   = [ lib_dir ]            # Library path
env['CPPPATH']   = [ lib_dir , src_dir ]  # Include path
env['LINKFLAGS'] = '-Wl,--cref -Wl,-M -Wl,-Map=main.map'

# library builder
lib_obj_n  = env.Object(  Glob( lib_dir + '/*.c')     )
lib_a_n    = env.Library( lib_dir+'/mylib', lib_obj_n )
env.NoClean( lib_a_n + lib_obj_n )

# main builder
prog_n = env.Program( 'main', Glob( src_dir + '/*.c') )

# add clean files
Clean( prog_n, 'main.map' )

# clean lib
# Usage: $ scons -c lib
if env.GetOption('clean'):
    if 'lib' in COMMAND_LINE_TARGETS:
        lib_objs = lib_obj_n + lib_a_n
        Execute( Delete( lib_objs ) )

上で、27〜30行目に追加しました。 使うときは、 scons -f SConstruct.gcc8 -c lib で、ライブラリ群「src/lib/*.a, src/lib/*.o 」だけが削除されます。
$ scons -f SConstruct.gcc8 -c lib
Delete(["src/lib/func1.o", "src/lib/func2.o", "src/lib/libmylib.a"])
*./configure機能を使う なんと、autoconf系のconfigureスクリプトに相当する機能があります。(サブセット)
# SConstruct.gcc9
# cygwin/MinGW
import os

env = Environment( )
# run confugure 
if not env.GetOption( 'clean' ):
    conf = Configure(env)
    if not conf.CheckLib( 'bfd' ):
        print 'Did not find libbfd.a !'
        #Exit(1)
    if  conf.CheckLib( 'm' ):
        print 'Found libm.a !'
        conf.env.Append( CPPDEFINES = [ '_DEBUG_', ( '_TEST',5 ) ] )
        #Exit(1)
    env = conf.Finish()

src_dir = 'src'
lib_dir = src_dir+'/lib'
env['LIBS']      = 'mylib'                # Library
env['LIBPATH']   = [ lib_dir ]            # Library path
env['CPPPATH']   = [ lib_dir , src_dir ]  # Include path
env['LINKFLAGS'] = '-Wl,--cref -Wl,-M -Wl,-Map=main.map'

# library builder
lib_obj_n  = env.Object(  Glob( lib_dir + '/*.c')     )
lib_a_n    = env.Library( lib_dir+'/mylib', lib_obj_n )
env.NoClean( lib_a_n + lib_obj_n )

# main builder
prog_n = env.Program( 'main', Glob( src_dir + '/*.c') )

# add clean files
Clean( prog_n, 'main.map' )

# clean lib
# Usage: $ scons -c lib
if env.GetOption('clean'):
    if 'lib' in COMMAND_LINE_TARGETS:
        lib_objs = lib_obj_n + lib_a_n
        Execute( Delete( lib_objs ) )

上で6〜16行目に追加しました。ライブラリ「'bfd','m'」の存在をチェックして コンパイルオプションを変更しています。
$ scons -f SConstruct.gcc9
scons: Reading SConscript files ...
Checking for C library bfd... yes
Checking for C library m... yes
Found libm.a !
scons: done reading SConscript files.
scons: Building targets ...
gcc -o src/main.o -c -D_DEBUG_ -D_TEST=5 -Isrc/lib -Isrc src/main.c
gcc -o src/sub.o -c -D_DEBUG_ -D_TEST=5 -Isrc/lib -Isrc src/sub.c
gcc -o src/lib/func1.o -c -D_DEBUG_ -D_TEST=5 -Isrc/lib -Isrc src/lib/func1.c
gcc -o src/lib/func2.o -c -D_DEBUG_ -D_TEST=5 -Isrc/lib -Isrc src/lib/func2.c
ar rc src/lib/libmylib.a src/lib/func1.o src/lib/func2.o
ranlib src/lib/libmylib.a
gcc -o main.exe -Wl,--cref -Wl,-M -Wl,-Map=main.map src/main.o src/sub.o -Lsrc/l
ib -lmylib
scons: done building targets.
上が実行結果です。 configure機能によりコンパイルオプションが設定されています。 上はCygwinコンソールですがこの機能は「MS-DOS窓の中でも動きます」。 標準で「ヘッダチェック」、「関数チェック」、「ライブラリチェック」、 「typedefチェック」が用意されていて、「カスタムチェック」も作ることが出来ます。 http://www.scons.org/doc/production/HTML/scons-user/x4132.html クールだね! (注)arm-none-eabi-gcc 4.5.1系だとCheckLib()関数がうまく動作しませんでした。 つづきは、 ARMマイコンでSConsを使う(2)へ (注1) http://www.scons.org/doc/production/HTML/scons-user/c718.html#AEN725 正確にはNodeオブジェクトが入ったリストが返されます。 Nodeオブジェクトは「ファイルやディレクトリを表すオブジェクト」です。 例えば、nObjがNodeオブジェクト変数で"main.o"を表すとすると nObj.path は 'main.o' を返し nObj.abspath は '/usr/local/src/main.o' を返します。 Nodeオブジェクト変数のリストなら、 [ nObj, nobj1, nobj2, nobj3 ]等となります。 nObj_list = [ nObj, nobj1, nobj2, nobj3 ] とすれば nObj_list[2].path で「nObj2」の「path」が得られます。 返り値が一つの時も [ nObj ] というリストが返ります。 従って nObj_list = Object( [ 'main.c' ] ) nObj_list[0].path => 'main.o' nObj_list = Object( [ 'main.c', 'sub.c' ] ) nObj_list[1].path => 'sub.o' 等々となります。 特徴的なのは返り値が「依存関係の連鎖キー」のように使われる 場合があることです。例えば Clean( nObj_list, 'main.map') など。 このページでは「Nodeオブジェクトが入ったリスト」を単に(Node)オブジェクト と表現する場合があります。(長くなるので) (注2) ライブラリ用に「SConstruct」を分離する手もあります。 参考: SCons ユーザーズガイド http://www.scons.org/doc/production/HTML/scons-user/index.html http://www.scons.org/wiki/UsingCodeGenerators BuildNumberProcessing http://www.scons.org/wiki/BuildNumberProcessing Copyright (C) 2010, audin All rights reserved.
posted by Copyright (C) avrin All Rights Reserved. at 22:31| Comment(0) | SCons | このブログの読者になる | 更新情報をチェックする