You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

setup.rb 36 kB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585
  1. #
  2. # setup.rb
  3. #
  4. # Copyright (c) 2000-2005 Minero Aoki
  5. #
  6. # This program is free software.
  7. # You can distribute/modify this program under the terms of
  8. # the GNU LGPL, Lesser General Public License version 2.1.
  9. #
  10. unless Enumerable.method_defined?(:map) # Ruby 1.4.6
  11. module Enumerable
  12. alias map collect
  13. end
  14. end
  15. unless File.respond_to?(:read) # Ruby 1.6
  16. def File.read(fname)
  17. open(fname) {|f|
  18. return f.read
  19. }
  20. end
  21. end
  22. unless Errno.const_defined?(:ENOTEMPTY) # Windows?
  23. module Errno
  24. class ENOTEMPTY
  25. # We do not raise this exception, implementation is not needed.
  26. end
  27. end
  28. end
  29. def File.binread(fname)
  30. open(fname, 'rb') {|f|
  31. return f.read
  32. }
  33. end
  34. # for corrupted Windows' stat(2)
  35. def File.dir?(path)
  36. File.directory?((path[-1,1] == '/') ? path : path + '/')
  37. end
  38. class ConfigTable
  39. include Enumerable
  40. def initialize(rbconfig)
  41. @rbconfig = rbconfig
  42. @items = []
  43. @table = {}
  44. # options
  45. @install_prefix = nil
  46. @config_opt = nil
  47. @verbose = true
  48. @no_harm = false
  49. end
  50. attr_accessor :install_prefix
  51. attr_accessor :config_opt
  52. attr_writer :verbose
  53. def verbose?
  54. @verbose
  55. end
  56. attr_writer :no_harm
  57. def no_harm?
  58. @no_harm
  59. end
  60. def [](key)
  61. lookup(key).resolve(self)
  62. end
  63. def []=(key, val)
  64. lookup(key).set val
  65. end
  66. def names
  67. @items.map {|i| i.name }
  68. end
  69. def each(&block)
  70. @items.each(&block)
  71. end
  72. def key?(name)
  73. @table.key?(name)
  74. end
  75. def lookup(name)
  76. @table[name] or setup_rb_error "no such config item: #{name}"
  77. end
  78. def add(item)
  79. @items.push item
  80. @table[item.name] = item
  81. end
  82. def remove(name)
  83. item = lookup(name)
  84. @items.delete_if {|i| i.name == name }
  85. @table.delete_if {|name, i| i.name == name }
  86. item
  87. end
  88. def load_script(path, inst = nil)
  89. if File.file?(path)
  90. MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path
  91. end
  92. end
  93. def savefile
  94. '.config'
  95. end
  96. def load_savefile
  97. begin
  98. File.foreach(savefile()) do |line|
  99. k, v = *line.split(/=/, 2)
  100. self[k] = v.strip
  101. end
  102. rescue Errno::ENOENT
  103. setup_rb_error $!.message + "\n#{File.basename($0)} config first"
  104. end
  105. end
  106. def save
  107. @items.each {|i| i.value }
  108. File.open(savefile(), 'w') {|f|
  109. @items.each do |i|
  110. f.printf "%s=%s\n", i.name, i.value if i.value? and i.value
  111. end
  112. }
  113. end
  114. def load_standard_entries
  115. standard_entries(@rbconfig).each do |ent|
  116. add ent
  117. end
  118. end
  119. def standard_entries(rbconfig)
  120. c = rbconfig
  121. rubypath = File.join(c['bindir'], c['ruby_install_name'] + c['EXEEXT'])
  122. major = c['MAJOR'].to_i
  123. minor = c['MINOR'].to_i
  124. teeny = c['TEENY'].to_i
  125. version = "#{major}.#{minor}"
  126. # ruby ver. >= 1.4.4?
  127. newpath_p = ((major >= 2) or
  128. ((major == 1) and
  129. ((minor >= 5) or
  130. ((minor == 4) and (teeny >= 4)))))
  131. if c['rubylibdir']
  132. # V > 1.6.3
  133. libruby = "#{c['prefix']}/lib/ruby"
  134. librubyver = c['rubylibdir']
  135. librubyverarch = c['archdir']
  136. siteruby = c['sitedir']
  137. siterubyver = c['sitelibdir']
  138. siterubyverarch = c['sitearchdir']
  139. elsif newpath_p
  140. # 1.4.4 <= V <= 1.6.3
  141. libruby = "#{c['prefix']}/lib/ruby"
  142. librubyver = "#{c['prefix']}/lib/ruby/#{version}"
  143. librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
  144. siteruby = c['sitedir']
  145. siterubyver = "$siteruby/#{version}"
  146. siterubyverarch = "$siterubyver/#{c['arch']}"
  147. else
  148. # V < 1.4.4
  149. libruby = "#{c['prefix']}/lib/ruby"
  150. librubyver = "#{c['prefix']}/lib/ruby/#{version}"
  151. librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
  152. siteruby = "#{c['prefix']}/lib/ruby/#{version}/site_ruby"
  153. siterubyver = siteruby
  154. siterubyverarch = "$siterubyver/#{c['arch']}"
  155. end
  156. parameterize = lambda {|path|
  157. path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')
  158. }
  159. if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
  160. makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
  161. else
  162. makeprog = 'make'
  163. end
  164. [
  165. ExecItem.new('installdirs', 'std/site/home',
  166. 'std: install under libruby; site: install under site_ruby; home: install under $HOME')\
  167. {|val, table|
  168. case val
  169. when 'std'
  170. table['rbdir'] = '$librubyver'
  171. table['sodir'] = '$librubyverarch'
  172. when 'site'
  173. table['rbdir'] = '$siterubyver'
  174. table['sodir'] = '$siterubyverarch'
  175. when 'home'
  176. setup_rb_error '$HOME was not set' unless ENV['HOME']
  177. table['prefix'] = ENV['HOME']
  178. table['rbdir'] = '$libdir/ruby'
  179. table['sodir'] = '$libdir/ruby'
  180. end
  181. },
  182. PathItem.new('prefix', 'path', c['prefix'],
  183. 'path prefix of target environment'),
  184. PathItem.new('bindir', 'path', parameterize.call(c['bindir']),
  185. 'the directory for commands'),
  186. PathItem.new('libdir', 'path', parameterize.call(c['libdir']),
  187. 'the directory for libraries'),
  188. PathItem.new('datadir', 'path', parameterize.call(c['datadir']),
  189. 'the directory for shared data'),
  190. PathItem.new('mandir', 'path', parameterize.call(c['mandir']),
  191. 'the directory for man pages'),
  192. PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']),
  193. 'the directory for system configuration files'),
  194. PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']),
  195. 'the directory for local state data'),
  196. PathItem.new('libruby', 'path', libruby,
  197. 'the directory for ruby libraries'),
  198. PathItem.new('librubyver', 'path', librubyver,
  199. 'the directory for standard ruby libraries'),
  200. PathItem.new('librubyverarch', 'path', librubyverarch,
  201. 'the directory for standard ruby extensions'),
  202. PathItem.new('siteruby', 'path', siteruby,
  203. 'the directory for version-independent aux ruby libraries'),
  204. PathItem.new('siterubyver', 'path', siterubyver,
  205. 'the directory for aux ruby libraries'),
  206. PathItem.new('siterubyverarch', 'path', siterubyverarch,
  207. 'the directory for aux ruby binaries'),
  208. PathItem.new('rbdir', 'path', '$siterubyver',
  209. 'the directory for ruby scripts'),
  210. PathItem.new('sodir', 'path', '$siterubyverarch',
  211. 'the directory for ruby extentions'),
  212. PathItem.new('rubypath', 'path', rubypath,
  213. 'the path to set to #! line'),
  214. ProgramItem.new('rubyprog', 'name', rubypath,
  215. 'the ruby program using for installation'),
  216. ProgramItem.new('makeprog', 'name', makeprog,
  217. 'the make program to compile ruby extentions'),
  218. SelectItem.new('shebang', 'all/ruby/never', 'ruby',
  219. 'shebang line (#!) editing mode'),
  220. BoolItem.new('without-ext', 'yes/no', 'no',
  221. 'does not compile/install ruby extentions')
  222. ]
  223. end
  224. private :standard_entries
  225. def load_multipackage_entries
  226. multipackage_entries().each do |ent|
  227. add ent
  228. end
  229. end
  230. def multipackage_entries
  231. [
  232. PackageSelectionItem.new('with', 'name,name...', '', 'ALL',
  233. 'package names that you want to install'),
  234. PackageSelectionItem.new('without', 'name,name...', '', 'NONE',
  235. 'package names that you do not want to install')
  236. ]
  237. end
  238. private :multipackage_entries
  239. ALIASES = {
  240. 'std-ruby' => 'librubyver',
  241. 'stdruby' => 'librubyver',
  242. 'rubylibdir' => 'librubyver',
  243. 'archdir' => 'librubyverarch',
  244. 'site-ruby-common' => 'siteruby', # For backward compatibility
  245. 'site-ruby' => 'siterubyver', # For backward compatibility
  246. 'bin-dir' => 'bindir',
  247. 'bin-dir' => 'bindir',
  248. 'rb-dir' => 'rbdir',
  249. 'so-dir' => 'sodir',
  250. 'data-dir' => 'datadir',
  251. 'ruby-path' => 'rubypath',
  252. 'ruby-prog' => 'rubyprog',
  253. 'ruby' => 'rubyprog',
  254. 'make-prog' => 'makeprog',
  255. 'make' => 'makeprog'
  256. }
  257. def fixup
  258. ALIASES.each do |ali, name|
  259. @table[ali] = @table[name]
  260. end
  261. @items.freeze
  262. @table.freeze
  263. @options_re = /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/
  264. end
  265. def parse_opt(opt)
  266. m = @options_re.match(opt) or setup_rb_error "config: unknown option #{opt}"
  267. m.to_a[1,2]
  268. end
  269. def dllext
  270. @rbconfig['DLEXT']
  271. end
  272. def value_config?(name)
  273. lookup(name).value?
  274. end
  275. class Item
  276. def initialize(name, template, default, desc)
  277. @name = name.freeze
  278. @template = template
  279. @value = default
  280. @default = default
  281. @description = desc
  282. end
  283. attr_reader :name
  284. attr_reader :description
  285. attr_accessor :default
  286. alias help_default default
  287. def help_opt
  288. "--#{@name}=#{@template}"
  289. end
  290. def value?
  291. true
  292. end
  293. def value
  294. @value
  295. end
  296. def resolve(table)
  297. @value.gsub(%r<\$([^/]+)>) { table[$1] }
  298. end
  299. def set(val)
  300. @value = check(val)
  301. end
  302. private
  303. def check(val)
  304. setup_rb_error "config: --#{name} requires argument" unless val
  305. val
  306. end
  307. end
  308. class BoolItem < Item
  309. def config_type
  310. 'bool'
  311. end
  312. def help_opt
  313. "--#{@name}"
  314. end
  315. private
  316. def check(val)
  317. return 'yes' unless val
  318. case val
  319. when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes'
  320. when /\An(o)?\z/i, /\Af(alse)\z/i then 'no'
  321. else
  322. setup_rb_error "config: --#{@name} accepts only yes/no for argument"
  323. end
  324. end
  325. end
  326. class PathItem < Item
  327. def config_type
  328. 'path'
  329. end
  330. private
  331. def check(path)
  332. setup_rb_error "config: --#{@name} requires argument" unless path
  333. path[0,1] == '$' ? path : File.expand_path(path)
  334. end
  335. end
  336. class ProgramItem < Item
  337. def config_type
  338. 'program'
  339. end
  340. end
  341. class SelectItem < Item
  342. def initialize(name, selection, default, desc)
  343. super
  344. @ok = selection.split('/')
  345. end
  346. def config_type
  347. 'select'
  348. end
  349. private
  350. def check(val)
  351. unless @ok.include?(val.strip)
  352. setup_rb_error "config: use --#{@name}=#{@template} (#{val})"
  353. end
  354. val.strip
  355. end
  356. end
  357. class ExecItem < Item
  358. def initialize(name, selection, desc, &block)
  359. super name, selection, nil, desc
  360. @ok = selection.split('/')
  361. @action = block
  362. end
  363. def config_type
  364. 'exec'
  365. end
  366. def value?
  367. false
  368. end
  369. def resolve(table)
  370. setup_rb_error "$#{name()} wrongly used as option value"
  371. end
  372. undef set
  373. def evaluate(val, table)
  374. v = val.strip.downcase
  375. unless @ok.include?(v)
  376. setup_rb_error "invalid option --#{@name}=#{val} (use #{@template})"
  377. end
  378. @action.call v, table
  379. end
  380. end
  381. class PackageSelectionItem < Item
  382. def initialize(name, template, default, help_default, desc)
  383. super name, template, default, desc
  384. @help_default = help_default
  385. end
  386. attr_reader :help_default
  387. def config_type
  388. 'package'
  389. end
  390. private
  391. def check(val)
  392. unless File.dir?("packages/#{val}")
  393. setup_rb_error "config: no such package: #{val}"
  394. end
  395. val
  396. end
  397. end
  398. class MetaConfigEnvironment
  399. def initialize(config, installer)
  400. @config = config
  401. @installer = installer
  402. end
  403. def config_names
  404. @config.names
  405. end
  406. def config?(name)
  407. @config.key?(name)
  408. end
  409. def bool_config?(name)
  410. @config.lookup(name).config_type == 'bool'
  411. end
  412. def path_config?(name)
  413. @config.lookup(name).config_type == 'path'
  414. end
  415. def value_config?(name)
  416. @config.lookup(name).config_type != 'exec'
  417. end
  418. def add_config(item)
  419. @config.add item
  420. end
  421. def add_bool_config(name, default, desc)
  422. @config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc)
  423. end
  424. def add_path_config(name, default, desc)
  425. @config.add PathItem.new(name, 'path', default, desc)
  426. end
  427. def set_config_default(name, default)
  428. @config.lookup(name).default = default
  429. end
  430. def remove_config(name)
  431. @config.remove(name)
  432. end
  433. # For only multipackage
  434. def packages
  435. raise '[setup.rb fatal] multi-package metaconfig API packages() called for single-package; contact application package vendor' unless @installer
  436. @installer.packages
  437. end
  438. # For only multipackage
  439. def declare_packages(list)
  440. raise '[setup.rb fatal] multi-package metaconfig API declare_packages() called for single-package; contact application package vendor' unless @installer
  441. @installer.packages = list
  442. end
  443. end
  444. end # class ConfigTable
  445. # This module requires: #verbose?, #no_harm?
  446. module FileOperations
  447. def mkdir_p(dirname, prefix = nil)
  448. dirname = prefix + File.expand_path(dirname) if prefix
  449. $stderr.puts "mkdir -p #{dirname}" if verbose?
  450. return if no_harm?
  451. # Does not check '/', it's too abnormal.
  452. dirs = File.expand_path(dirname).split(%r<(?=/)>)
  453. if /\A[a-z]:\z/i =~ dirs[0]
  454. disk = dirs.shift
  455. dirs[0] = disk + dirs[0]
  456. end
  457. dirs.each_index do |idx|
  458. path = dirs[0..idx].join('')
  459. Dir.mkdir path unless File.dir?(path)
  460. end
  461. end
  462. def rm_f(path)
  463. $stderr.puts "rm -f #{path}" if verbose?
  464. return if no_harm?
  465. force_remove_file path
  466. end
  467. def rm_rf(path)
  468. $stderr.puts "rm -rf #{path}" if verbose?
  469. return if no_harm?
  470. remove_tree path
  471. end
  472. def remove_tree(path)
  473. if File.symlink?(path)
  474. remove_file path
  475. elsif File.dir?(path)
  476. remove_tree0 path
  477. else
  478. force_remove_file path
  479. end
  480. end
  481. def remove_tree0(path)
  482. Dir.foreach(path) do |ent|
  483. next if ent == '.'
  484. next if ent == '..'
  485. entpath = "#{path}/#{ent}"
  486. if File.symlink?(entpath)
  487. remove_file entpath
  488. elsif File.dir?(entpath)
  489. remove_tree0 entpath
  490. else
  491. force_remove_file entpath
  492. end
  493. end
  494. begin
  495. Dir.rmdir path
  496. rescue Errno::ENOTEMPTY
  497. # directory may not be empty
  498. end
  499. end
  500. def move_file(src, dest)
  501. force_remove_file dest
  502. begin
  503. File.rename src, dest
  504. rescue
  505. File.open(dest, 'wb') {|f|
  506. f.write File.binread(src)
  507. }
  508. File.chmod File.stat(src).mode, dest
  509. File.unlink src
  510. end
  511. end
  512. def force_remove_file(path)
  513. begin
  514. remove_file path
  515. rescue
  516. end
  517. end
  518. def remove_file(path)
  519. File.chmod 0777, path
  520. File.unlink path
  521. end
  522. def install(from, dest, mode, prefix = nil)
  523. $stderr.puts "install #{from} #{dest}" if verbose?
  524. return if no_harm?
  525. realdest = prefix ? prefix + File.expand_path(dest) : dest
  526. realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)
  527. str = File.binread(from)
  528. if diff?(str, realdest)
  529. verbose_off {
  530. rm_f realdest if File.exist?(realdest)
  531. }
  532. File.open(realdest, 'wb') {|f|
  533. f.write str
  534. }
  535. File.chmod mode, realdest
  536. File.open("#{objdir_root()}/InstalledFiles", 'a') {|f|
  537. if prefix
  538. f.puts realdest.sub(prefix, '')
  539. else
  540. f.puts realdest
  541. end
  542. }
  543. end
  544. end
  545. def diff?(new_content, path)
  546. return true unless File.exist?(path)
  547. new_content != File.binread(path)
  548. end
  549. def command(*args)
  550. $stderr.puts args.join(' ') if verbose?
  551. system(*args) or raise RuntimeError,
  552. "system(#{args.map{|a| a.inspect }.join(' ')}) failed"
  553. end
  554. def ruby(*args)
  555. command config('rubyprog'), *args
  556. end
  557. def make(task = nil)
  558. command(*[config('makeprog'), task].compact)
  559. end
  560. def extdir?(dir)
  561. File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb")
  562. end
  563. def files_of(dir)
  564. Dir.open(dir) {|d|
  565. return d.select {|ent| File.file?("#{dir}/#{ent}") }
  566. }
  567. end
  568. DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn )
  569. def directories_of(dir)
  570. Dir.open(dir) {|d|
  571. return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT
  572. }
  573. end
  574. end
  575. # This module requires: #srcdir_root, #objdir_root, #relpath
  576. module HookScriptAPI
  577. def get_config(key)
  578. @config[key]
  579. end
  580. alias config get_config
  581. # obsolete: use metaconfig to change configuration
  582. def set_config(key, val)
  583. @config[key] = val
  584. end
  585. #
  586. # srcdir/objdir (works only in the package directory)
  587. #
  588. def curr_srcdir
  589. "#{srcdir_root()}/#{relpath()}"
  590. end
  591. def curr_objdir
  592. "#{objdir_root()}/#{relpath()}"
  593. end
  594. def srcfile(path)
  595. "#{curr_srcdir()}/#{path}"
  596. end
  597. def srcexist?(path)
  598. File.exist?(srcfile(path))
  599. end
  600. def srcdirectory?(path)
  601. File.dir?(srcfile(path))
  602. end
  603. def srcfile?(path)
  604. File.file?(srcfile(path))
  605. end
  606. def srcentries(path = '.')
  607. Dir.open("#{curr_srcdir()}/#{path}") {|d|
  608. return d.to_a - %w(. ..)
  609. }
  610. end
  611. def srcfiles(path = '.')
  612. srcentries(path).select {|fname|
  613. File.file?(File.join(curr_srcdir(), path, fname))
  614. }
  615. end
  616. def srcdirectories(path = '.')
  617. srcentries(path).select {|fname|
  618. File.dir?(File.join(curr_srcdir(), path, fname))
  619. }
  620. end
  621. end
  622. class ToplevelInstaller
  623. Version = '3.4.1'
  624. Copyright = 'Copyright (c) 2000-2005 Minero Aoki'
  625. TASKS = [
  626. [ 'all', 'do config, setup, then install' ],
  627. [ 'config', 'saves your configurations' ],
  628. [ 'show', 'shows current configuration' ],
  629. [ 'setup', 'compiles ruby extentions and others' ],
  630. [ 'install', 'installs files' ],
  631. [ 'test', 'run all tests in test/' ],
  632. [ 'clean', "does `make clean' for each extention" ],
  633. [ 'distclean',"does `make distclean' for each extention" ]
  634. ]
  635. def ToplevelInstaller.invoke
  636. config = ConfigTable.new(load_rbconfig())
  637. config.load_standard_entries
  638. config.load_multipackage_entries if multipackage?
  639. config.fixup
  640. klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller)
  641. klass.new(File.dirname($0), config).invoke
  642. end
  643. def ToplevelInstaller.multipackage?
  644. File.dir?(File.dirname($0) + '/packages')
  645. end
  646. def ToplevelInstaller.load_rbconfig
  647. if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }
  648. ARGV.delete(arg)
  649. load File.expand_path(arg.split(/=/, 2)[1])
  650. $".push 'rbconfig.rb'
  651. else
  652. require 'rbconfig'
  653. end
  654. ::Config::CONFIG
  655. end
  656. def initialize(ardir_root, config)
  657. @ardir = File.expand_path(ardir_root)
  658. @config = config
  659. # cache
  660. @valid_task_re = nil
  661. end
  662. def config(key)
  663. @config[key]
  664. end
  665. def inspect
  666. "#<#{self.class} #{__id__()}>"
  667. end
  668. def invoke
  669. run_metaconfigs
  670. case task = parsearg_global()
  671. when nil, 'all'
  672. parsearg_config
  673. init_installers
  674. exec_config
  675. exec_setup
  676. exec_install
  677. else
  678. case task
  679. when 'config', 'test'
  680. ;
  681. when 'clean', 'distclean'
  682. @config.load_savefile if File.exist?(@config.savefile)
  683. else
  684. @config.load_savefile
  685. end
  686. __send__ "parsearg_#{task}"
  687. init_installers
  688. __send__ "exec_#{task}"
  689. end
  690. end
  691. def run_metaconfigs
  692. @config.load_script "#{@ardir}/metaconfig"
  693. end
  694. def init_installers
  695. @installer = Installer.new(@config, @ardir, File.expand_path('.'))
  696. end
  697. #
  698. # Hook Script API bases
  699. #
  700. def srcdir_root
  701. @ardir
  702. end
  703. def objdir_root
  704. '.'
  705. end
  706. def relpath
  707. '.'
  708. end
  709. #
  710. # Option Parsing
  711. #
  712. def parsearg_global
  713. while arg = ARGV.shift
  714. case arg
  715. when /\A\w+\z/
  716. setup_rb_error "invalid task: #{arg}" unless valid_task?(arg)
  717. return arg
  718. when '-q', '--quiet'
  719. @config.verbose = false
  720. when '--verbose'
  721. @config.verbose = true
  722. when '--help'
  723. print_usage $stdout
  724. exit 0
  725. when '--version'
  726. puts "#{File.basename($0)} version #{Version}"
  727. exit 0
  728. when '--copyright'
  729. puts Copyright
  730. exit 0
  731. else
  732. setup_rb_error "unknown global option '#{arg}'"
  733. end
  734. end
  735. nil
  736. end
  737. def valid_task?(t)
  738. valid_task_re() =~ t
  739. end
  740. def valid_task_re
  741. @valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/
  742. end
  743. def parsearg_no_options
  744. unless ARGV.empty?
  745. task = caller(0).first.slice(%r<`parsearg_(\w+)'>, 1)
  746. setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}"
  747. end
  748. end
  749. alias parsearg_show parsearg_no_options
  750. alias parsearg_setup parsearg_no_options
  751. alias parsearg_test parsearg_no_options
  752. alias parsearg_clean parsearg_no_options
  753. alias parsearg_distclean parsearg_no_options
  754. def parsearg_config
  755. evalopt = []
  756. set = []
  757. @config.config_opt = []
  758. while i = ARGV.shift
  759. if /\A--?\z/ =~ i
  760. @config.config_opt = ARGV.dup
  761. break
  762. end
  763. name, value = *@config.parse_opt(i)
  764. if @config.value_config?(name)
  765. @config[name] = value
  766. else
  767. evalopt.push [name, value]
  768. end
  769. set.push name
  770. end
  771. evalopt.each do |name, value|
  772. @config.lookup(name).evaluate value, @config
  773. end
  774. # Check if configuration is valid
  775. set.each do |n|
  776. @config[n] if @config.value_config?(n)
  777. end
  778. end
  779. def parsearg_install
  780. @config.no_harm = false
  781. @config.install_prefix = ''
  782. while a = ARGV.shift
  783. case a
  784. when '--no-harm'
  785. @config.no_harm = true
  786. when /\A--prefix=/
  787. path = a.split(/=/, 2)[1]
  788. path = File.expand_path(path) unless path[0,1] == '/'
  789. @config.install_prefix = path
  790. else
  791. setup_rb_error "install: unknown option #{a}"
  792. end
  793. end
  794. end
  795. def print_usage(out)
  796. out.puts 'Typical Installation Procedure:'
  797. out.puts " $ ruby #{File.basename $0} config"
  798. out.puts " $ ruby #{File.basename $0} setup"
  799. out.puts " # ruby #{File.basename $0} install (may require root privilege)"
  800. out.puts
  801. out.puts 'Detailed Usage:'
  802. out.puts " ruby #{File.basename $0} <global option>"
  803. out.puts " ruby #{File.basename $0} [<global options>] <task> [<task options>]"
  804. fmt = " %-24s %s\n"
  805. out.puts
  806. out.puts 'Global options:'
  807. out.printf fmt, '-q,--quiet', 'suppress message outputs'
  808. out.printf fmt, ' --verbose', 'output messages verbosely'
  809. out.printf fmt, ' --help', 'print this message'
  810. out.printf fmt, ' --version', 'print version and quit'
  811. out.printf fmt, ' --copyright', 'print copyright and quit'
  812. out.puts
  813. out.puts 'Tasks:'
  814. TASKS.each do |name, desc|
  815. out.printf fmt, name, desc
  816. end
  817. fmt = " %-24s %s [%s]\n"
  818. out.puts
  819. out.puts 'Options for CONFIG or ALL:'
  820. @config.each do |item|
  821. out.printf fmt, item.help_opt, item.description, item.help_default
  822. end
  823. out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's"
  824. out.puts
  825. out.puts 'Options for INSTALL:'
  826. out.printf fmt, '--no-harm', 'only display what to do if given', 'off'
  827. out.printf fmt, '--prefix=path', 'install path prefix', ''
  828. out.puts
  829. end
  830. #
  831. # Task Handlers
  832. #
  833. def exec_config
  834. @installer.exec_config
  835. @config.save # must be final
  836. end
  837. def exec_setup
  838. @installer.exec_setup
  839. end
  840. def exec_install
  841. @installer.exec_install
  842. end
  843. def exec_test
  844. @installer.exec_test
  845. end
  846. def exec_show
  847. @config.each do |i|
  848. printf "%-20s %s\n", i.name, i.value if i.value?
  849. end
  850. end
  851. def exec_clean
  852. @installer.exec_clean
  853. end
  854. def exec_distclean
  855. @installer.exec_distclean
  856. end
  857. end # class ToplevelInstaller
  858. class ToplevelInstallerMulti < ToplevelInstaller
  859. include FileOperations
  860. def initialize(ardir_root, config)
  861. super
  862. @packages = directories_of("#{@ardir}/packages")
  863. raise 'no package exists' if @packages.empty?
  864. @root_installer = Installer.new(@config, @ardir, File.expand_path('.'))
  865. end
  866. def run_metaconfigs
  867. @config.load_script "#{@ardir}/metaconfig", self
  868. @packages.each do |name|
  869. @config.load_script "#{@ardir}/packages/#{name}/metaconfig"
  870. end
  871. end
  872. attr_reader :packages
  873. def packages=(list)
  874. raise 'package list is empty' if list.empty?
  875. list.each do |name|
  876. raise "directory packages/#{name} does not exist"\
  877. unless File.dir?("#{@ardir}/packages/#{name}")
  878. end
  879. @packages = list
  880. end
  881. def init_installers
  882. @installers = {}
  883. @packages.each do |pack|
  884. @installers[pack] = Installer.new(@config,
  885. "#{@ardir}/packages/#{pack}",
  886. "packages/#{pack}")
  887. end
  888. with = extract_selection(config('with'))
  889. without = extract_selection(config('without'))
  890. @selected = @installers.keys.select {|name|
  891. (with.empty? or with.include?(name)) \
  892. and not without.include?(name)
  893. }
  894. end
  895. def extract_selection(list)
  896. a = list.split(/,/)
  897. a.each do |name|
  898. setup_rb_error "no such package: #{name}" unless @installers.key?(name)
  899. end
  900. a
  901. end
  902. def print_usage(f)
  903. super
  904. f.puts 'Inluded packages:'
  905. f.puts ' ' + @packages.sort.join(' ')
  906. f.puts
  907. end
  908. #
  909. # Task Handlers
  910. #
  911. def exec_config
  912. run_hook 'pre-config'
  913. each_selected_installers {|inst| inst.exec_config }
  914. run_hook 'post-config'
  915. @config.save # must be final
  916. end
  917. def exec_setup
  918. run_hook 'pre-setup'
  919. each_selected_installers {|inst| inst.exec_setup }
  920. run_hook 'post-setup'
  921. end
  922. def exec_install
  923. run_hook 'pre-install'
  924. each_selected_installers {|inst| inst.exec_install }
  925. run_hook 'post-install'
  926. end
  927. def exec_test
  928. run_hook 'pre-test'
  929. each_selected_installers {|inst| inst.exec_test }
  930. run_hook 'post-test'
  931. end
  932. def exec_clean
  933. rm_f @config.savefile
  934. run_hook 'pre-clean'
  935. each_selected_installers {|inst| inst.exec_clean }
  936. run_hook 'post-clean'
  937. end
  938. def exec_distclean
  939. rm_f @config.savefile
  940. run_hook 'pre-distclean'
  941. each_selected_installers {|inst| inst.exec_distclean }
  942. run_hook 'post-distclean'
  943. end
  944. #
  945. # lib
  946. #
  947. def each_selected_installers
  948. Dir.mkdir 'packages' unless File.dir?('packages')
  949. @selected.each do |pack|
  950. $stderr.puts "Processing the package `#{pack}' ..." if verbose?
  951. Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")
  952. Dir.chdir "packages/#{pack}"
  953. yield @installers[pack]
  954. Dir.chdir '../..'
  955. end
  956. end
  957. def run_hook(id)
  958. @root_installer.run_hook id
  959. end
  960. # module FileOperations requires this
  961. def verbose?
  962. @config.verbose?
  963. end
  964. # module FileOperations requires this
  965. def no_harm?
  966. @config.no_harm?
  967. end
  968. end # class ToplevelInstallerMulti
  969. class Installer
  970. FILETYPES = %w( bin lib ext data conf man )
  971. include FileOperations
  972. include HookScriptAPI
  973. def initialize(config, srcroot, objroot)
  974. @config = config
  975. @srcdir = File.expand_path(srcroot)
  976. @objdir = File.expand_path(objroot)
  977. @currdir = '.'
  978. end
  979. def inspect
  980. "#<#{self.class} #{File.basename(@srcdir)}>"
  981. end
  982. def noop(rel)
  983. end
  984. #
  985. # Hook Script API base methods
  986. #
  987. def srcdir_root
  988. @srcdir
  989. end
  990. def objdir_root
  991. @objdir
  992. end
  993. def relpath
  994. @currdir
  995. end
  996. #
  997. # Config Access
  998. #
  999. # module FileOperations requires this
  1000. def verbose?
  1001. @config.verbose?
  1002. end
  1003. # module FileOperations requires this
  1004. def no_harm?
  1005. @config.no_harm?
  1006. end
  1007. def verbose_off
  1008. begin
  1009. save, @config.verbose = @config.verbose?, false
  1010. yield
  1011. ensure
  1012. @config.verbose = save
  1013. end
  1014. end
  1015. #
  1016. # TASK config
  1017. #
  1018. def exec_config
  1019. exec_task_traverse 'config'
  1020. end
  1021. alias config_dir_bin noop
  1022. alias config_dir_lib noop
  1023. def config_dir_ext(rel)
  1024. extconf if extdir?(curr_srcdir())
  1025. end
  1026. alias config_dir_data noop
  1027. alias config_dir_conf noop
  1028. alias config_dir_man noop
  1029. def extconf
  1030. ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt
  1031. end
  1032. #
  1033. # TASK setup
  1034. #
  1035. def exec_setup
  1036. exec_task_traverse 'setup'
  1037. end
  1038. def setup_dir_bin(rel)
  1039. files_of(curr_srcdir()).each do |fname|
  1040. update_shebang_line "#{curr_srcdir()}/#{fname}"
  1041. end
  1042. end
  1043. alias setup_dir_lib noop
  1044. def setup_dir_ext(rel)
  1045. make if extdir?(curr_srcdir())
  1046. end
  1047. alias setup_dir_data noop
  1048. alias setup_dir_conf noop
  1049. alias setup_dir_man noop
  1050. def update_shebang_line(path)
  1051. return if no_harm?
  1052. return if config('shebang') == 'never'
  1053. old = Shebang.load(path)
  1054. if old
  1055. $stderr.puts "warning: #{path}: Shebang line includes too many args. It is not portable and your program may not work." if old.args.size > 1
  1056. new = new_shebang(old)
  1057. return if new.to_s == old.to_s
  1058. else
  1059. return unless config('shebang') == 'all'
  1060. new = Shebang.new(config('rubypath'))
  1061. end
  1062. $stderr.puts "updating shebang: #{File.basename(path)}" if verbose?
  1063. open_atomic_writer(path) {|output|
  1064. File.open(path, 'rb') {|f|
  1065. f.gets if old # discard
  1066. output.puts new.to_s
  1067. output.print f.read
  1068. }
  1069. }
  1070. end
  1071. def new_shebang(old)
  1072. if /\Aruby/ =~ File.basename(old.cmd)
  1073. Shebang.new(config('rubypath'), old.args)
  1074. elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby'
  1075. Shebang.new(config('rubypath'), old.args[1..-1])
  1076. else
  1077. return old unless config('shebang') == 'all'
  1078. Shebang.new(config('rubypath'))
  1079. end
  1080. end
  1081. def open_atomic_writer(path, &block)
  1082. tmpfile = File.basename(path) + '.tmp'
  1083. begin
  1084. File.open(tmpfile, 'wb', &block)
  1085. File.rename tmpfile, File.basename(path)
  1086. ensure
  1087. File.unlink tmpfile if File.exist?(tmpfile)
  1088. end
  1089. end
  1090. class Shebang
  1091. def Shebang.load(path)
  1092. line = nil
  1093. File.open(path) {|f|
  1094. line = f.gets
  1095. }
  1096. return nil unless /\A#!/ =~ line
  1097. parse(line)
  1098. end
  1099. def Shebang.parse(line)
  1100. cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ')
  1101. new(cmd, args)
  1102. end
  1103. def initialize(cmd, args = [])
  1104. @cmd = cmd
  1105. @args = args
  1106. end
  1107. attr_reader :cmd
  1108. attr_reader :args
  1109. def to_s
  1110. "#! #{@cmd}" + (@args.empty? ? '' : " #{@args.join(' ')}")
  1111. end
  1112. end
  1113. #
  1114. # TASK install
  1115. #
  1116. def exec_install
  1117. rm_f 'InstalledFiles'
  1118. exec_task_traverse 'install'
  1119. end
  1120. def install_dir_bin(rel)
  1121. install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755
  1122. end
  1123. def install_dir_lib(rel)
  1124. install_files libfiles(), "#{config('rbdir')}/#{rel}", 0644
  1125. end
  1126. def install_dir_ext(rel)
  1127. return unless extdir?(curr_srcdir())
  1128. install_files rubyextentions('.'),
  1129. "#{config('sodir')}/#{File.dirname(rel)}",
  1130. 0555
  1131. end
  1132. def install_dir_data(rel)
  1133. install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644
  1134. end
  1135. def install_dir_conf(rel)
  1136. # FIXME: should not remove current config files
  1137. # (rename previous file to .old/.org)
  1138. install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644
  1139. end
  1140. def install_dir_man(rel)
  1141. install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644
  1142. end
  1143. def install_files(list, dest, mode)
  1144. mkdir_p dest, @config.install_prefix
  1145. list.each do |fname|
  1146. install fname, dest, mode, @config.install_prefix
  1147. end
  1148. end
  1149. def libfiles
  1150. glob_reject(%w(*.y *.output), targetfiles())
  1151. end
  1152. def rubyextentions(dir)
  1153. ents = glob_select("*.#{@config.dllext}", targetfiles())
  1154. if ents.empty?
  1155. setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first"
  1156. end
  1157. ents
  1158. end
  1159. def targetfiles
  1160. mapdir(existfiles() - hookfiles())
  1161. end
  1162. def mapdir(ents)
  1163. ents.map {|ent|
  1164. if File.exist?(ent)
  1165. then ent # objdir
  1166. else "#{curr_srcdir()}/#{ent}" # srcdir
  1167. end
  1168. }
  1169. end
  1170. # picked up many entries from cvs-1.11.1/src/ignore.c
  1171. JUNK_FILES = %w(
  1172. core RCSLOG tags TAGS .make.state
  1173. .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
  1174. *~ *.old *.bak *.BAK *.orig *.rej _$* *$
  1175. *.org *.in .*
  1176. )
  1177. def existfiles
  1178. glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.')))
  1179. end
  1180. def hookfiles
  1181. %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|
  1182. %w( config setup install clean ).map {|t| sprintf(fmt, t) }
  1183. }.flatten
  1184. end
  1185. def glob_select(pat, ents)
  1186. re = globs2re([pat])
  1187. ents.select {|ent| re =~ ent }
  1188. end
  1189. def glob_reject(pats, ents)
  1190. re = globs2re(pats)
  1191. ents.reject {|ent| re =~ ent }
  1192. end
  1193. GLOB2REGEX = {
  1194. '.' => '\.',
  1195. '$' => '\$',
  1196. '#' => '\#',
  1197. '*' => '.*'
  1198. }
  1199. def globs2re(pats)
  1200. /\A(?:#{
  1201. pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|')
  1202. })\z/
  1203. end
  1204. #
  1205. # TASK test
  1206. #
  1207. TESTDIR = 'test'
  1208. def exec_test
  1209. unless File.directory?('test')
  1210. $stderr.puts 'no test in this package' if verbose?
  1211. return
  1212. end
  1213. $stderr.puts 'Running tests...' if verbose?
  1214. begin
  1215. require 'test/unit'
  1216. rescue LoadError
  1217. setup_rb_error 'test/unit cannot loaded. You need Ruby 1.8 or later to invoke this task.'
  1218. end
  1219. runner = Test::Unit::AutoRunner.new(true)
  1220. runner.to_run << TESTDIR
  1221. runner.run
  1222. end
  1223. #
  1224. # TASK clean
  1225. #
  1226. def exec_clean
  1227. exec_task_traverse 'clean'
  1228. rm_f @config.savefile
  1229. rm_f 'InstalledFiles'
  1230. end
  1231. alias clean_dir_bin noop
  1232. alias clean_dir_lib noop
  1233. alias clean_dir_data noop
  1234. alias clean_dir_conf noop
  1235. alias clean_dir_man noop
  1236. def clean_dir_ext(rel)
  1237. return unless extdir?(curr_srcdir())
  1238. make 'clean' if File.file?('Makefile')
  1239. end
  1240. #
  1241. # TASK distclean
  1242. #
  1243. def exec_distclean
  1244. exec_task_traverse 'distclean'
  1245. rm_f @config.savefile
  1246. rm_f 'InstalledFiles'
  1247. end
  1248. alias distclean_dir_bin noop
  1249. alias distclean_dir_lib noop
  1250. def distclean_dir_ext(rel)
  1251. return unless extdir?(curr_srcdir())
  1252. make 'distclean' if File.file?('Makefile')
  1253. end
  1254. alias distclean_dir_data noop
  1255. alias distclean_dir_conf noop
  1256. alias distclean_dir_man noop
  1257. #
  1258. # Traversing
  1259. #
  1260. def exec_task_traverse(task)
  1261. run_hook "pre-#{task}"
  1262. FILETYPES.each do |type|
  1263. if type == 'ext' and config('without-ext') == 'yes'
  1264. $stderr.puts 'skipping ext/* by user option' if verbose?
  1265. next
  1266. end
  1267. traverse task, type, "#{task}_dir_#{type}"
  1268. end
  1269. run_hook "post-#{task}"
  1270. end
  1271. def traverse(task, rel, mid)
  1272. dive_into(rel) {
  1273. run_hook "pre-#{task}"
  1274. __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
  1275. directories_of(curr_srcdir()).each do |d|
  1276. traverse task, "#{rel}/#{d}", mid
  1277. end
  1278. run_hook "post-#{task}"
  1279. }
  1280. end
  1281. def dive_into(rel)
  1282. return unless File.dir?("#{@srcdir}/#{rel}")
  1283. dir = File.basename(rel)
  1284. Dir.mkdir dir unless File.dir?(dir)
  1285. prevdir = Dir.pwd
  1286. Dir.chdir dir
  1287. $stderr.puts '---> ' + rel if verbose?
  1288. @currdir = rel
  1289. yield
  1290. Dir.chdir prevdir
  1291. $stderr.puts '<--- ' + rel if verbose?
  1292. @currdir = File.dirname(rel)
  1293. end
  1294. def run_hook(id)
  1295. path = [ "#{curr_srcdir()}/#{id}",
  1296. "#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) }
  1297. return unless path
  1298. begin
  1299. instance_eval File.read(path), path, 1
  1300. rescue
  1301. raise if $DEBUG
  1302. setup_rb_error "hook #{path} failed:\n" + $!.message
  1303. end
  1304. end
  1305. end # class Installer
  1306. class SetupError < StandardError; end
  1307. def setup_rb_error(msg)
  1308. raise SetupError, msg
  1309. end
  1310. if $0 == __FILE__
  1311. begin
  1312. ToplevelInstaller.invoke
  1313. rescue SetupError
  1314. raise if $DEBUG
  1315. $stderr.puts $!.message
  1316. $stderr.puts "Try 'ruby #{$0} --help' for detailed usage."
  1317. exit 1
  1318. end
  1319. end

Ruby binding for the igraph library.