setup.rbを読む
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
]
開始行:
#contents
*はじめに [#sf691330]
Rubyパッケージの伝統的なインストーラとして[[青木さんのset...
ruby setup.rb
です。Ruby on Railsを理解する、前にRubyGemsを理解する、前...
なお、今回対象としたバージョンは3.4.1です。
*起動部分 [#tc6d6dab]
ではまず、ruby setup.rbとした時に何が起こるかを見てみまし...
if $0 == __FILE__
ToplevelInstaller.invoke
end
$0は実行中のファイル名、__FILE__は現在のソースファイル名...
foo.rb
puts "$0\t:#{$0}"
puts "__FILE__:#{__FILE__}"
if $0 == __FILE__
puts 'foo.rb'
end
bar.rb
require 'foo'
puts "$0\t:#{$0}"
puts "__FILE__:#{__FILE__}"
foo.rbを実行すると以下のように表示されます。
$ ruby foo.rb
$0 :foo.rb
__FILE__:foo.rb
foo.rb
bar.rbを実行すると以下のように表示されます。
$ ruby bar.rb
$0 :bar.rb
__FILE__:./foo.rb
$0 :bar.rb
__FILE__:bar.rb
$0は変わらないけど__FILE__は変わります。__FILE__はRubyイ...
さて、以上の説明およびfoo.rbの実行結果を見ていただくとご...
if $0 == __FILE__
何とか
end
というのはスクリプトが直接実行されたときに実行したいコー...
*ToplevelInstaller.invoke [#c798b1a5]
前置きが長くなってしまいましたが、起動時に呼ばれるTopleve...
def ToplevelInstaller.invoke
config = ConfigTable.new(load_rbconfig())
config.load_standard_entries
config.load_multipackage_entries if multipackage?
config.fixup
klass = (multipackage?() ? ToplevelInstallerMulti : To...
klass.new(File.dirname($0), config).invoke
end
一行ずつ読んでいきましょう。
*ToplevelInstaller.load_rbconfig [#z85024e7]
まず、ToplevelInstaller.load_rbconfigです。
def ToplevelInstaller.load_rbconfig
if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }
ARGV.delete(arg)
load File.expand_path(arg.split(/=/, 2)[1])
$".push 'rbconfig.rb'
else
require 'rbconfig'
end
::Config::CONFIG
end
まあそんな感じかなというところですが注目すべきは以下の一...
load File.expand_path(arg.split(/=/, 2)[1])
というか解説したいところが2つあるので二行に分けましょう。
path = arg.split(/=/, 2)[1]
load File.expand_path(path)
一行目、普通ならsplit(/=/)とだけしてしまいそうですが省略...
--rb-config=a=b.rb
としていても適切にa=b.rbが読み込まれます。そんな変なファ...
次に二行目、File.expand_pathメソッドを使って相対パスを絶...
rbconfig.rbって何?という方のために書いておくとrbconfig.r...
*ConfigTable#standard_entries [#tdb1b328]
さて、ToplevelInstaller.invokeに戻ると次にConfigTable#loa...
addメソッドは以下のようになっています。
def add(item)
@items.push item
@table[item.name] = item
end
クラス名がConfigTableで、@tableというインスタンス変数名か...
さて、セクション名のstandard_entriesメソッドです。バージ...
というのも興味深いところですがこのメソッドではどうやらオ...
PathItem.new('prefix', 'path', c['prefix'],
'path prefix of target environment')
これはどうやら
ruby setup --help
としたときに表示されるオプション一覧のようです。これらが...
*追補ToplevelInstaller.invoke [#yd90abd9]
ToplevelInstaller.invokeの次の行はマルチパッケージに関す...
次の行はConfigTable#fixupメソッドです。興味深いのは次の一...
@options_re = /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/
先ほど設定したオプションテーブルを使って正規表現を組み上...
さらに次の行に進んで・・・、マルチパッケージは対象としな...
klass = (multipackage?() ? ToplevelInstallerMulti : Topl...
三項演算子にてmultipackage?メソッドを呼んでいます。初め、...
klass = (multipackage? ? ToplevelInstallerMulti : Toplev...
と書いてないのかな?と思ったのですがそうではない(上のコ...
*ToplevelInstaller#invoke [#o187d3a7]
さてようやくインストール処理をしてそうなところまでたどり...
parsearg_config
init_installers
exec_config
exec_setup
exec_install
parsearg_configメソッドではオプションの解析を行っています...
*ToplevelInstaller#parsearg_config [#l6f62886]
さて、前半で仕込んだオプションテーブルを使う部分がやって...
def parsearg_config
evalopt = []
set = []
while i = ARGV.shift
name, value = *@config.parse_opt(i)
if @config.value_config?(name)
@config[name] = value
else
evalopt.push [name, value]
end
set.push name
end
evalopt.each do |name, value|
@config.lookup(name).evaluate value, @config
end
# Check if configuration is valid
set.each do |n|
@config[n] if @config.value_config?(n)
end
end
まず、ConfigTable#parse_optメソッドを見てみましょう。
def parse_opt(opt)
m = @options_re.match(opt) or setup_rb_error "config: ...
m.to_a[1,2]
end
先ほど構築したオプション解析用正規表現に通し、オプション...
次に、ConfigTable#value_config?ですが読み進めていくとConf...
*ConfigTable::ExecItem [#qfde74e8]
では、ExecItemを見てみましょう。ExecItemを構築していると...
ExecItem.new('installdirs', 'std/site/home',
'std: install under libruby; site: install ...
{|val, table|
case val
when 'std'
table['rbdir'] = '$librubyver'
table['sodir'] = '$librubyverarch'
<<中略>>
end
}
def initialize(name, selection, desc, &block)
super name, selection, nil, desc
@ok = selection.split('/')
@action = block
end
def evaluate(val, table)
v = val.strip.downcase
unless @ok.include?(v)
setup_rb_error "invalid option --#{@name}=#{val} (us...
end
@action.call v, table
end
というわけで、オプションが指定されたときに設定をスクリプ...
*ConfigTable::Item#resolve [#ce14bbf4]
ConfigTable#[]メソッドを見てみると以下の処理が行われまし...
def [](key)
lookup(key).resolve(self)
end
解決しています。何を解決しているのでしょうか?見てみまし...
def resolve(table)
@value.gsub(%r<\$([^/]+)>) { table[$1] }
end
Itemが自分の持っている値の$で始まる名前を実際の値にしてい...
a=$b/c
b=$d/e
d=f
の場合にaを参照しても、
+bを参照
+dを参照
+fを返す
+f/eを返す
+f/e/cを返す
という具合に再帰的に名前が解決されています。
*Installer#traverse [#kc2f14b9]
ToplevelInstaller#exec_configを見ると先ほど構築したInstal...
exec_task_traverse 'config'
次にexec_task_traveseメソッドです。オプション処理の部分を...
def exec_task_traverse(task)
run_hook "pre-#{task}"
FILETYPES.each do |type|
traverse task, type, "#{task}_dir_#{type}"
end
run_hook "post-#{task}"
end
FILETYPESってなんだろうと思ったらsetup.rbが対象としている...
def traverse(task, rel, mid)
dive_into(rel) {
run_hook "pre-#{task}"
__send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
directories_of(curr_srcdir()).each do |d|
traverse task, "#{rel}/#{d}", mid
end
run_hook "post-#{task}"
}
end
このあたりになってくると幾分わかりづらくなりますが、どう...
def config_dir_ext(rel)
extconf if extdir?(curr_srcdir())
end
configなので拡張ライブラリのための設定処理を実行している...
タスクの処理ではリフレクションを使って処理を行っているよ...
*おわりに [#wf39b730]
今回は青木さんのsetup.rbを読みました。今回学んだこととし...
-オブジェクトテーブルを使ったオプションの処理方法
-リフレクションを使った各タスク各ディレクトリの処理方法
それではみなさんもよいコードリーディングを。
終了行:
#contents
*はじめに [#sf691330]
Rubyパッケージの伝統的なインストーラとして[[青木さんのset...
ruby setup.rb
です。Ruby on Railsを理解する、前にRubyGemsを理解する、前...
なお、今回対象としたバージョンは3.4.1です。
*起動部分 [#tc6d6dab]
ではまず、ruby setup.rbとした時に何が起こるかを見てみまし...
if $0 == __FILE__
ToplevelInstaller.invoke
end
$0は実行中のファイル名、__FILE__は現在のソースファイル名...
foo.rb
puts "$0\t:#{$0}"
puts "__FILE__:#{__FILE__}"
if $0 == __FILE__
puts 'foo.rb'
end
bar.rb
require 'foo'
puts "$0\t:#{$0}"
puts "__FILE__:#{__FILE__}"
foo.rbを実行すると以下のように表示されます。
$ ruby foo.rb
$0 :foo.rb
__FILE__:foo.rb
foo.rb
bar.rbを実行すると以下のように表示されます。
$ ruby bar.rb
$0 :bar.rb
__FILE__:./foo.rb
$0 :bar.rb
__FILE__:bar.rb
$0は変わらないけど__FILE__は変わります。__FILE__はRubyイ...
さて、以上の説明およびfoo.rbの実行結果を見ていただくとご...
if $0 == __FILE__
何とか
end
というのはスクリプトが直接実行されたときに実行したいコー...
*ToplevelInstaller.invoke [#c798b1a5]
前置きが長くなってしまいましたが、起動時に呼ばれるTopleve...
def ToplevelInstaller.invoke
config = ConfigTable.new(load_rbconfig())
config.load_standard_entries
config.load_multipackage_entries if multipackage?
config.fixup
klass = (multipackage?() ? ToplevelInstallerMulti : To...
klass.new(File.dirname($0), config).invoke
end
一行ずつ読んでいきましょう。
*ToplevelInstaller.load_rbconfig [#z85024e7]
まず、ToplevelInstaller.load_rbconfigです。
def ToplevelInstaller.load_rbconfig
if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }
ARGV.delete(arg)
load File.expand_path(arg.split(/=/, 2)[1])
$".push 'rbconfig.rb'
else
require 'rbconfig'
end
::Config::CONFIG
end
まあそんな感じかなというところですが注目すべきは以下の一...
load File.expand_path(arg.split(/=/, 2)[1])
というか解説したいところが2つあるので二行に分けましょう。
path = arg.split(/=/, 2)[1]
load File.expand_path(path)
一行目、普通ならsplit(/=/)とだけしてしまいそうですが省略...
--rb-config=a=b.rb
としていても適切にa=b.rbが読み込まれます。そんな変なファ...
次に二行目、File.expand_pathメソッドを使って相対パスを絶...
rbconfig.rbって何?という方のために書いておくとrbconfig.r...
*ConfigTable#standard_entries [#tdb1b328]
さて、ToplevelInstaller.invokeに戻ると次にConfigTable#loa...
addメソッドは以下のようになっています。
def add(item)
@items.push item
@table[item.name] = item
end
クラス名がConfigTableで、@tableというインスタンス変数名か...
さて、セクション名のstandard_entriesメソッドです。バージ...
というのも興味深いところですがこのメソッドではどうやらオ...
PathItem.new('prefix', 'path', c['prefix'],
'path prefix of target environment')
これはどうやら
ruby setup --help
としたときに表示されるオプション一覧のようです。これらが...
*追補ToplevelInstaller.invoke [#yd90abd9]
ToplevelInstaller.invokeの次の行はマルチパッケージに関す...
次の行はConfigTable#fixupメソッドです。興味深いのは次の一...
@options_re = /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/
先ほど設定したオプションテーブルを使って正規表現を組み上...
さらに次の行に進んで・・・、マルチパッケージは対象としな...
klass = (multipackage?() ? ToplevelInstallerMulti : Topl...
三項演算子にてmultipackage?メソッドを呼んでいます。初め、...
klass = (multipackage? ? ToplevelInstallerMulti : Toplev...
と書いてないのかな?と思ったのですがそうではない(上のコ...
*ToplevelInstaller#invoke [#o187d3a7]
さてようやくインストール処理をしてそうなところまでたどり...
parsearg_config
init_installers
exec_config
exec_setup
exec_install
parsearg_configメソッドではオプションの解析を行っています...
*ToplevelInstaller#parsearg_config [#l6f62886]
さて、前半で仕込んだオプションテーブルを使う部分がやって...
def parsearg_config
evalopt = []
set = []
while i = ARGV.shift
name, value = *@config.parse_opt(i)
if @config.value_config?(name)
@config[name] = value
else
evalopt.push [name, value]
end
set.push name
end
evalopt.each do |name, value|
@config.lookup(name).evaluate value, @config
end
# Check if configuration is valid
set.each do |n|
@config[n] if @config.value_config?(n)
end
end
まず、ConfigTable#parse_optメソッドを見てみましょう。
def parse_opt(opt)
m = @options_re.match(opt) or setup_rb_error "config: ...
m.to_a[1,2]
end
先ほど構築したオプション解析用正規表現に通し、オプション...
次に、ConfigTable#value_config?ですが読み進めていくとConf...
*ConfigTable::ExecItem [#qfde74e8]
では、ExecItemを見てみましょう。ExecItemを構築していると...
ExecItem.new('installdirs', 'std/site/home',
'std: install under libruby; site: install ...
{|val, table|
case val
when 'std'
table['rbdir'] = '$librubyver'
table['sodir'] = '$librubyverarch'
<<中略>>
end
}
def initialize(name, selection, desc, &block)
super name, selection, nil, desc
@ok = selection.split('/')
@action = block
end
def evaluate(val, table)
v = val.strip.downcase
unless @ok.include?(v)
setup_rb_error "invalid option --#{@name}=#{val} (us...
end
@action.call v, table
end
というわけで、オプションが指定されたときに設定をスクリプ...
*ConfigTable::Item#resolve [#ce14bbf4]
ConfigTable#[]メソッドを見てみると以下の処理が行われまし...
def [](key)
lookup(key).resolve(self)
end
解決しています。何を解決しているのでしょうか?見てみまし...
def resolve(table)
@value.gsub(%r<\$([^/]+)>) { table[$1] }
end
Itemが自分の持っている値の$で始まる名前を実際の値にしてい...
a=$b/c
b=$d/e
d=f
の場合にaを参照しても、
+bを参照
+dを参照
+fを返す
+f/eを返す
+f/e/cを返す
という具合に再帰的に名前が解決されています。
*Installer#traverse [#kc2f14b9]
ToplevelInstaller#exec_configを見ると先ほど構築したInstal...
exec_task_traverse 'config'
次にexec_task_traveseメソッドです。オプション処理の部分を...
def exec_task_traverse(task)
run_hook "pre-#{task}"
FILETYPES.each do |type|
traverse task, type, "#{task}_dir_#{type}"
end
run_hook "post-#{task}"
end
FILETYPESってなんだろうと思ったらsetup.rbが対象としている...
def traverse(task, rel, mid)
dive_into(rel) {
run_hook "pre-#{task}"
__send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
directories_of(curr_srcdir()).each do |d|
traverse task, "#{rel}/#{d}", mid
end
run_hook "post-#{task}"
}
end
このあたりになってくると幾分わかりづらくなりますが、どう...
def config_dir_ext(rel)
extconf if extdir?(curr_srcdir())
end
configなので拡張ライブラリのための設定処理を実行している...
タスクの処理ではリフレクションを使って処理を行っているよ...
*おわりに [#wf39b730]
今回は青木さんのsetup.rbを読みました。今回学んだこととし...
-オブジェクトテーブルを使ったオプションの処理方法
-リフレクションを使った各タスク各ディレクトリの処理方法
それではみなさんもよいコードリーディングを。
ページ名: