setup.rb 28 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331
  1. #
  2. # setup.rb
  3. #
  4. # Copyright (c) 2000-2004 Minero Aoki
  5. #
  6. # This program is free software.
  7. # You can distribute/modify this program under the terms of
  8. # the GNU Lesser General Public License version 2.1.
  9. #
  10. #
  11. # For backward compatibility
  12. #
  13. unless Enumerable.method_defined?(:map)
  14. module Enumerable
  15. alias map collect
  16. end
  17. end
  18. unless Enumerable.method_defined?(:detect)
  19. module Enumerable
  20. alias detect find
  21. end
  22. end
  23. unless Enumerable.method_defined?(:select)
  24. module Enumerable
  25. alias select find_all
  26. end
  27. end
  28. unless Enumerable.method_defined?(:reject)
  29. module Enumerable
  30. def reject
  31. result = []
  32. each do |i|
  33. result.push i unless yield(i)
  34. end
  35. result
  36. end
  37. end
  38. end
  39. unless Enumerable.method_defined?(:inject)
  40. module Enumerable
  41. def inject(result)
  42. each do |i|
  43. result = yield(result, i)
  44. end
  45. result
  46. end
  47. end
  48. end
  49. unless Enumerable.method_defined?(:any?)
  50. module Enumerable
  51. def any?
  52. each do |i|
  53. return true if yield(i)
  54. end
  55. false
  56. end
  57. end
  58. end
  59. unless File.respond_to?(:read)
  60. def File.read(fname)
  61. open(fname) {|f|
  62. return f.read
  63. }
  64. end
  65. end
  66. #
  67. # Application independent utilities
  68. #
  69. def File.binread(fname)
  70. open(fname, 'rb') {|f|
  71. return f.read
  72. }
  73. end
  74. # for corrupted windows stat(2)
  75. def File.dir?(path)
  76. File.directory?((path[-1,1] == '/') ? path : path + '/')
  77. end
  78. #
  79. # Config
  80. #
  81. if arg = ARGV.detect{|arg| /\A--rbconfig=/ =~ arg }
  82. ARGV.delete(arg)
  83. require arg.split(/=/, 2)[1]
  84. $".push 'rbconfig.rb'
  85. else
  86. require 'rbconfig'
  87. end
  88. def multipackage_install?
  89. FileTest.directory?(File.dirname($0) + '/packages')
  90. end
  91. class ConfigTable
  92. c = ::Config::CONFIG
  93. rubypath = c['bindir'] + '/' + c['ruby_install_name']
  94. major = c['MAJOR'].to_i
  95. minor = c['MINOR'].to_i
  96. teeny = c['TEENY'].to_i
  97. version = "#{major}.#{minor}"
  98. # ruby ver. >= 1.4.4?
  99. newpath_p = ((major >= 2) or
  100. ((major == 1) and
  101. ((minor >= 5) or
  102. ((minor == 4) and (teeny >= 4)))))
  103. subprefix = lambda {|path|
  104. path.sub(/\A#{Regexp.quote(c['prefix'])}/o, '$prefix')
  105. }
  106. if c['rubylibdir']
  107. # V < 1.6.3
  108. stdruby = subprefix.call(c['rubylibdir'])
  109. siteruby = subprefix.call(c['sitedir'])
  110. versite = subprefix.call(c['sitelibdir'])
  111. sodir = subprefix.call(c['sitearchdir'])
  112. elsif newpath_p
  113. # 1.4.4 <= V <= 1.6.3
  114. stdruby = "$prefix/lib/ruby/#{version}"
  115. siteruby = subprefix.call(c['sitedir'])
  116. versite = siteruby + '/' + version
  117. sodir = "$site-ruby/#{c['arch']}"
  118. else
  119. # V < 1.4.4
  120. stdruby = "$prefix/lib/ruby/#{version}"
  121. siteruby = "$prefix/lib/ruby/#{version}/site_ruby"
  122. versite = siteruby
  123. sodir = "$site-ruby/#{c['arch']}"
  124. end
  125. if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
  126. makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
  127. else
  128. makeprog = 'make'
  129. end
  130. common_descripters = [
  131. [ 'prefix', [ c['prefix'],
  132. 'path',
  133. 'path prefix of target environment' ] ],
  134. [ 'std-ruby', [ stdruby,
  135. 'path',
  136. 'the directory for standard ruby libraries' ] ],
  137. [ 'site-ruby-common', [ siteruby,
  138. 'path',
  139. 'the directory for version-independent non-standard ruby libraries' ] ],
  140. [ 'site-ruby', [ versite,
  141. 'path',
  142. 'the directory for non-standard ruby libraries' ] ],
  143. [ 'bin-dir', [ '$prefix/bin',
  144. 'path',
  145. 'the directory for commands' ] ],
  146. [ 'rb-dir', [ '$site-ruby',
  147. 'path',
  148. 'the directory for ruby scripts' ] ],
  149. [ 'so-dir', [ sodir,
  150. 'path',
  151. 'the directory for ruby extentions' ] ],
  152. [ 'data-dir', [ '$prefix/share',
  153. 'path',
  154. 'the directory for shared data' ] ],
  155. [ 'ruby-path', [ rubypath,
  156. 'path',
  157. 'path to set to #! line' ] ],
  158. [ 'ruby-prog', [ rubypath,
  159. 'name',
  160. 'the ruby program using for installation' ] ],
  161. [ 'make-prog', [ makeprog,
  162. 'name',
  163. 'the make program to compile ruby extentions' ] ],
  164. [ 'without-ext', [ 'no',
  165. 'yes/no',
  166. 'does not compile/install ruby extentions' ] ]
  167. ]
  168. multipackage_descripters = [
  169. [ 'with', [ '',
  170. 'name,name...',
  171. 'package names that you want to install',
  172. 'ALL' ] ],
  173. [ 'without', [ '',
  174. 'name,name...',
  175. 'package names that you do not want to install',
  176. 'NONE' ] ]
  177. ]
  178. if multipackage_install?
  179. DESCRIPTER = common_descripters + multipackage_descripters
  180. else
  181. DESCRIPTER = common_descripters
  182. end
  183. SAVE_FILE = 'config.save'
  184. def ConfigTable.each_name(&block)
  185. keys().each(&block)
  186. end
  187. def ConfigTable.keys
  188. DESCRIPTER.map {|name, *dummy| name }
  189. end
  190. def ConfigTable.each_definition(&block)
  191. DESCRIPTER.each(&block)
  192. end
  193. def ConfigTable.get_entry(name)
  194. name, ent = DESCRIPTER.assoc(name)
  195. ent
  196. end
  197. def ConfigTable.get_entry!(name)
  198. get_entry(name) or raise ArgumentError, "no such config: #{name}"
  199. end
  200. def ConfigTable.add_entry(name, vals)
  201. ConfigTable::DESCRIPTER.push [name,vals]
  202. end
  203. def ConfigTable.remove_entry(name)
  204. get_entry(name) or raise ArgumentError, "no such config: #{name}"
  205. DESCRIPTER.delete_if {|n, arr| n == name }
  206. end
  207. def ConfigTable.config_key?(name)
  208. get_entry(name) ? true : false
  209. end
  210. def ConfigTable.bool_config?(name)
  211. ent = get_entry(name) or return false
  212. ent[1] == 'yes/no'
  213. end
  214. def ConfigTable.value_config?(name)
  215. ent = get_entry(name) or return false
  216. ent[1] != 'yes/no'
  217. end
  218. def ConfigTable.path_config?(name)
  219. ent = get_entry(name) or return false
  220. ent[1] == 'path'
  221. end
  222. class << self
  223. alias newobj new
  224. end
  225. def ConfigTable.new
  226. c = newobj()
  227. c.initialize_from_table
  228. c
  229. end
  230. def ConfigTable.load
  231. c = newobj()
  232. c.initialize_from_file
  233. c
  234. end
  235. def initialize_from_table
  236. @table = {}
  237. DESCRIPTER.each do |k, (default, vname, desc, default2)|
  238. @table[k] = default
  239. end
  240. end
  241. def initialize_from_file
  242. raise InstallError, "#{File.basename $0} config first"\
  243. unless File.file?(SAVE_FILE)
  244. @table = {}
  245. File.foreach(SAVE_FILE) do |line|
  246. k, v = line.split(/=/, 2)
  247. @table[k] = v.strip
  248. end
  249. end
  250. def save
  251. File.open(SAVE_FILE, 'w') {|f|
  252. @table.each do |k, v|
  253. f.printf "%s=%s\n", k, v if v
  254. end
  255. }
  256. end
  257. def []=(k, v)
  258. raise InstallError, "unknown config option #{k}"\
  259. unless ConfigTable.config_key?(k)
  260. @table[k] = v
  261. end
  262. def [](key)
  263. return nil unless @table[key]
  264. @table[key].gsub(%r<\$([^/]+)>) { self[$1] }
  265. end
  266. def set_raw(key, val)
  267. @table[key] = val
  268. end
  269. def get_raw(key)
  270. @table[key]
  271. end
  272. end
  273. module MetaConfigAPI
  274. def eval_file_ifexist(fname)
  275. instance_eval File.read(fname), fname, 1 if File.file?(fname)
  276. end
  277. def config_names
  278. ConfigTable.keys
  279. end
  280. def config?(name)
  281. ConfigTable.config_key?(name)
  282. end
  283. def bool_config?(name)
  284. ConfigTable.bool_config?(name)
  285. end
  286. def value_config?(name)
  287. ConfigTable.value_config?(name)
  288. end
  289. def path_config?(name)
  290. ConfigTable.path_config?(name)
  291. end
  292. def add_config(name, argname, default, desc)
  293. ConfigTable.add_entry name,[default,argname,desc]
  294. end
  295. def add_path_config(name, default, desc)
  296. add_config name, 'path', default, desc
  297. end
  298. def add_bool_config(name, default, desc)
  299. add_config name, 'yes/no', default ? 'yes' : 'no', desc
  300. end
  301. def set_config_default(name, default)
  302. if bool_config?(name)
  303. ConfigTable.get_entry!(name)[0] = (default ? 'yes' : 'no')
  304. else
  305. ConfigTable.get_entry!(name)[0] = default
  306. end
  307. end
  308. def remove_config(name)
  309. ent = ConfigTable.get_entry(name)
  310. ConfigTable.remove_entry name
  311. ent
  312. end
  313. end
  314. #
  315. # File Operations
  316. #
  317. module FileOperations
  318. def mkdir_p(dirname, prefix = nil)
  319. dirname = prefix + dirname if prefix
  320. $stderr.puts "mkdir -p #{dirname}" if verbose?
  321. return if no_harm?
  322. # does not check '/'... it's too abnormal case
  323. dirs = dirname.split(%r<(?=/)>)
  324. if /\A[a-z]:\z/i =~ dirs[0]
  325. disk = dirs.shift
  326. dirs[0] = disk + dirs[0]
  327. end
  328. dirs.each_index do |idx|
  329. path = dirs[0..idx].join('')
  330. Dir.mkdir path unless File.dir?(path)
  331. end
  332. end
  333. def rm_f(fname)
  334. $stderr.puts "rm -f #{fname}" if verbose?
  335. return if no_harm?
  336. if File.exist?(fname) or File.symlink?(fname)
  337. File.chmod 0777, fname
  338. File.unlink fname
  339. end
  340. end
  341. def rm_rf(dn)
  342. $stderr.puts "rm -rf #{dn}" if verbose?
  343. return if no_harm?
  344. Dir.chdir dn
  345. Dir.foreach('.') do |fn|
  346. next if fn == '.'
  347. next if fn == '..'
  348. if File.dir?(fn)
  349. verbose_off {
  350. rm_rf fn
  351. }
  352. else
  353. verbose_off {
  354. rm_f fn
  355. }
  356. end
  357. end
  358. Dir.chdir '..'
  359. Dir.rmdir dn
  360. end
  361. def move_file(src, dest)
  362. File.unlink dest if File.exist?(dest)
  363. begin
  364. File.rename src, dest
  365. rescue
  366. File.open(dest, 'wb') {|f| f.write File.binread(src) }
  367. File.chmod File.stat(src).mode, dest
  368. File.unlink src
  369. end
  370. end
  371. def install(from, dest, mode, prefix = nil)
  372. $stderr.puts "install #{from} #{dest}" if verbose?
  373. return if no_harm?
  374. realdest = prefix + dest if prefix
  375. realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)
  376. str = File.binread(from)
  377. if diff?(str, realdest)
  378. verbose_off {
  379. rm_f realdest if File.exist?(realdest)
  380. }
  381. File.open(realdest, 'wb') {|f|
  382. f.write str
  383. }
  384. File.chmod mode, realdest
  385. File.open("#{objdir_root()}/InstalledFiles", 'a') {|f|
  386. if prefix
  387. f.puts realdest.sub(prefix, '')
  388. else
  389. f.puts realdest
  390. end
  391. }
  392. end
  393. end
  394. def diff?(new_content, path)
  395. return true unless File.exist?(path)
  396. new_content != File.binread(path)
  397. end
  398. def command(str)
  399. $stderr.puts str if verbose?
  400. system str or raise RuntimeError, "'system #{str}' failed"
  401. end
  402. def ruby(str)
  403. command config('ruby-prog') + ' ' + str
  404. end
  405. def make(task = '')
  406. command config('make-prog') + ' ' + task
  407. end
  408. def extdir?(dir)
  409. File.exist?(dir + '/MANIFEST')
  410. end
  411. def all_files_in(dirname)
  412. Dir.open(dirname) {|d|
  413. return d.select {|ent| File.file?("#{dirname}/#{ent}") }
  414. }
  415. end
  416. REJECT_DIRS = %w(
  417. CVS SCCS RCS CVS.adm
  418. )
  419. def all_dirs_in(dirname)
  420. Dir.open(dirname) {|d|
  421. return d.select {|n| File.dir?("#{dirname}/#{n}") } - %w(. ..) - REJECT_DIRS
  422. }
  423. end
  424. end
  425. #
  426. # Main Installer
  427. #
  428. class InstallError < StandardError; end
  429. module HookUtils
  430. def run_hook(name)
  431. try_run_hook "#{curr_srcdir()}/#{name}" or
  432. try_run_hook "#{curr_srcdir()}/#{name}.rb"
  433. end
  434. def try_run_hook(fname)
  435. return false unless File.file?(fname)
  436. begin
  437. instance_eval File.read(fname), fname, 1
  438. rescue
  439. raise InstallError, "hook #{fname} failed:\n" + $!.message
  440. end
  441. true
  442. end
  443. end
  444. module HookScriptAPI
  445. def get_config(key)
  446. @config[key]
  447. end
  448. alias config get_config
  449. def set_config(key, val)
  450. @config[key] = val
  451. end
  452. #
  453. # srcdir/objdir (works only in the package directory)
  454. #
  455. #abstract srcdir_root
  456. #abstract objdir_root
  457. #abstract relpath
  458. def curr_srcdir
  459. "#{srcdir_root()}/#{relpath()}"
  460. end
  461. def curr_objdir
  462. "#{objdir_root()}/#{relpath()}"
  463. end
  464. def srcfile(path)
  465. "#{curr_srcdir()}/#{path}"
  466. end
  467. def srcexist?(path)
  468. File.exist?(srcfile(path))
  469. end
  470. def srcdirectory?(path)
  471. File.dir?(srcfile(path))
  472. end
  473. def srcfile?(path)
  474. File.file? srcfile(path)
  475. end
  476. def srcentries(path = '.')
  477. Dir.open("#{curr_srcdir()}/#{path}") {|d|
  478. return d.to_a - %w(. ..)
  479. }
  480. end
  481. def srcfiles(path = '.')
  482. srcentries(path).select {|fname|
  483. File.file?(File.join(curr_srcdir(), path, fname))
  484. }
  485. end
  486. def srcdirectories(path = '.')
  487. srcentries(path).select {|fname|
  488. File.dir?(File.join(curr_srcdir(), path, fname))
  489. }
  490. end
  491. end
  492. class ToplevelInstaller
  493. Version = '3.2.4'
  494. Copyright = 'Copyright (c) 2000-2004 Minero Aoki'
  495. TASKS = [
  496. [ 'config', 'saves your configurations' ],
  497. [ 'show', 'shows current configuration' ],
  498. [ 'setup', 'compiles ruby extentions and others' ],
  499. [ 'install', 'installs files' ],
  500. [ 'clean', "does `make clean' for each extention" ],
  501. [ 'distclean',"does `make distclean' for each extention" ]
  502. ]
  503. def ToplevelInstaller.invoke
  504. instance().invoke
  505. end
  506. @singleton = nil
  507. def ToplevelInstaller.instance
  508. @singleton ||= new(File.dirname($0))
  509. @singleton
  510. end
  511. include MetaConfigAPI
  512. def initialize(ardir_root)
  513. @config = nil
  514. @options = { 'verbose' => true }
  515. @ardir = File.expand_path(ardir_root)
  516. end
  517. def inspect
  518. "#<#{self.class} #{__id__()}>"
  519. end
  520. def invoke
  521. run_metaconfigs
  522. task = parsearg_global()
  523. @config = load_config(task)
  524. __send__ "parsearg_#{task}"
  525. init_installers
  526. __send__ "exec_#{task}"
  527. end
  528. def run_metaconfigs
  529. eval_file_ifexist "#{@ardir}/metaconfig"
  530. end
  531. def load_config(task)
  532. case task
  533. when 'config'
  534. ConfigTable.new
  535. when 'clean', 'distclean'
  536. if File.exist?('config.save')
  537. then ConfigTable.load
  538. else ConfigTable.new
  539. end
  540. else
  541. ConfigTable.load
  542. end
  543. end
  544. def init_installers
  545. @installer = Installer.new(@config, @options, @ardir, File.expand_path('.'))
  546. end
  547. #
  548. # Hook Script API bases
  549. #
  550. def srcdir_root
  551. @ardir
  552. end
  553. def objdir_root
  554. '.'
  555. end
  556. def relpath
  557. '.'
  558. end
  559. #
  560. # Option Parsing
  561. #
  562. def parsearg_global
  563. valid_task = /\A(?:#{TASKS.map {|task,desc| task }.join '|'})\z/
  564. while arg = ARGV.shift
  565. case arg
  566. when /\A\w+\z/
  567. raise InstallError, "invalid task: #{arg}" unless valid_task =~ arg
  568. return arg
  569. when '-q', '--quiet'
  570. @options['verbose'] = false
  571. when '--verbose'
  572. @options['verbose'] = true
  573. when '-h', '--help'
  574. print_usage $stdout
  575. exit 0
  576. when '-v', '--version'
  577. puts "#{File.basename($0)} version #{Version}"
  578. exit 0
  579. when '--copyright'
  580. puts Copyright
  581. exit 0
  582. else
  583. raise InstallError, "unknown global option '#{arg}'"
  584. end
  585. end
  586. raise InstallError, <<EOS
  587. No task or global option given.
  588. Typical installation procedure is:
  589. $ ruby #{File.basename($0)} config
  590. $ ruby #{File.basename($0)} setup
  591. # ruby #{File.basename($0)} install (may require root privilege)
  592. EOS
  593. end
  594. def parsearg_no_options
  595. raise InstallError, "#{task}: unknown options: #{ARGV.join ' '}"\
  596. unless ARGV.empty?
  597. end
  598. alias parsearg_show parsearg_no_options
  599. alias parsearg_setup parsearg_no_options
  600. alias parsearg_clean parsearg_no_options
  601. alias parsearg_distclean parsearg_no_options
  602. def parsearg_config
  603. re = /\A--(#{ConfigTable.keys.join '|'})(?:=(.*))?\z/
  604. @options['config-opt'] = []
  605. while i = ARGV.shift
  606. if /\A--?\z/ =~ i
  607. @options['config-opt'] = ARGV.dup
  608. break
  609. end
  610. m = re.match(i) or raise InstallError, "config: unknown option #{i}"
  611. name, value = m.to_a[1,2]
  612. if value
  613. if ConfigTable.bool_config?(name)
  614. raise InstallError, "config: --#{name} allows only yes/no for argument"\
  615. unless /\A(y(es)?|n(o)?|t(rue)?|f(alse))\z/i =~ value
  616. value = (/\Ay(es)?|\At(rue)/i =~ value) ? 'yes' : 'no'
  617. end
  618. else
  619. raise InstallError, "config: --#{name} requires argument"\
  620. unless ConfigTable.bool_config?(name)
  621. value = 'yes'
  622. end
  623. @config[name] = value
  624. end
  625. end
  626. def parsearg_install
  627. @options['no-harm'] = false
  628. @options['install-prefix'] = ''
  629. while a = ARGV.shift
  630. case a
  631. when /\A--no-harm\z/
  632. @options['no-harm'] = true
  633. when /\A--prefix=(.*)\z/
  634. path = $1
  635. path = File.expand_path(path) unless path[0,1] == '/'
  636. @options['install-prefix'] = path
  637. else
  638. raise InstallError, "install: unknown option #{a}"
  639. end
  640. end
  641. end
  642. def print_usage(out)
  643. out.puts 'Typical Installation Procedure:'
  644. out.puts " $ ruby #{File.basename $0} config"
  645. out.puts " $ ruby #{File.basename $0} setup"
  646. out.puts " # ruby #{File.basename $0} install (may require root privilege)"
  647. out.puts
  648. out.puts 'Detailed Usage:'
  649. out.puts " ruby #{File.basename $0} <global option>"
  650. out.puts " ruby #{File.basename $0} [<global options>] <task> [<task options>]"
  651. fmt = " %-20s %s\n"
  652. out.puts
  653. out.puts 'Global options:'
  654. out.printf fmt, '-q,--quiet', 'suppress message outputs'
  655. out.printf fmt, ' --verbose', 'output messages verbosely'
  656. out.printf fmt, '-h,--help', 'print this message'
  657. out.printf fmt, '-v,--version', 'print version and quit'
  658. out.printf fmt, ' --copyright', 'print copyright and quit'
  659. out.puts
  660. out.puts 'Tasks:'
  661. TASKS.each do |name, desc|
  662. out.printf " %-10s %s\n", name, desc
  663. end
  664. out.puts
  665. out.puts 'Options for config:'
  666. ConfigTable.each_definition do |name, (default, arg, desc, default2)|
  667. out.printf " %-20s %s [%s]\n",
  668. '--'+ name + (ConfigTable.bool_config?(name) ? '' : '='+arg),
  669. desc,
  670. default2 || default
  671. end
  672. out.printf " %-20s %s [%s]\n",
  673. '--rbconfig=path', 'your rbconfig.rb to load', "running ruby's"
  674. out.puts
  675. out.puts 'Options for install:'
  676. out.printf " %-20s %s [%s]\n",
  677. '--no-harm', 'only display what to do if given', 'off'
  678. out.printf " %-20s %s [%s]\n",
  679. '--prefix', 'install path prefix', '$prefix'
  680. out.puts
  681. end
  682. #
  683. # Task Handlers
  684. #
  685. def exec_config
  686. @installer.exec_config
  687. @config.save # must be final
  688. end
  689. def exec_setup
  690. @installer.exec_setup
  691. end
  692. def exec_install
  693. @installer.exec_install
  694. end
  695. def exec_show
  696. ConfigTable.each_name do |k|
  697. v = @config.get_raw(k)
  698. if not v or v.empty?
  699. v = '(not specified)'
  700. end
  701. printf "%-10s %s\n", k, v
  702. end
  703. end
  704. def exec_clean
  705. @installer.exec_clean
  706. end
  707. def exec_distclean
  708. @installer.exec_distclean
  709. end
  710. end
  711. class ToplevelInstallerMulti < ToplevelInstaller
  712. include HookUtils
  713. include HookScriptAPI
  714. include FileOperations
  715. def initialize(ardir)
  716. super
  717. @packages = all_dirs_in("#{@ardir}/packages")
  718. raise 'no package exists' if @packages.empty?
  719. end
  720. def run_metaconfigs
  721. eval_file_ifexist "#{@ardir}/metaconfig"
  722. @packages.each do |name|
  723. eval_file_ifexist "#{@ardir}/packages/#{name}/metaconfig"
  724. end
  725. end
  726. def init_installers
  727. @installers = {}
  728. @packages.each do |pack|
  729. @installers[pack] = Installer.new(@config, @options,
  730. "#{@ardir}/packages/#{pack}",
  731. "packages/#{pack}")
  732. end
  733. with = extract_selection(config('with'))
  734. without = extract_selection(config('without'))
  735. @selected = @installers.keys.select {|name|
  736. (with.empty? or with.include?(name)) \
  737. and not without.include?(name)
  738. }
  739. end
  740. def extract_selection(list)
  741. a = list.split(/,/)
  742. a.each do |name|
  743. raise InstallError, "no such package: #{name}" \
  744. unless @installers.key?(name)
  745. end
  746. a
  747. end
  748. def print_usage(f)
  749. super
  750. f.puts 'Inluded packages:'
  751. f.puts ' ' + @packages.sort.join(' ')
  752. f.puts
  753. end
  754. #
  755. # multi-package metaconfig API
  756. #
  757. attr_reader :packages
  758. def declare_packages(list)
  759. raise 'package list is empty' if list.empty?
  760. list.each do |name|
  761. raise "directory packages/#{name} does not exist"\
  762. unless File.dir?("#{@ardir}/packages/#{name}")
  763. end
  764. @packages = list
  765. end
  766. #
  767. # Task Handlers
  768. #
  769. def exec_config
  770. run_hook 'pre-config'
  771. each_selected_installers {|inst| inst.exec_config }
  772. run_hook 'post-config'
  773. @config.save # must be final
  774. end
  775. def exec_setup
  776. run_hook 'pre-setup'
  777. each_selected_installers {|inst| inst.exec_setup }
  778. run_hook 'post-setup'
  779. end
  780. def exec_install
  781. run_hook 'pre-install'
  782. each_selected_installers {|inst| inst.exec_install }
  783. run_hook 'post-install'
  784. end
  785. def exec_clean
  786. rm_f 'config.save'
  787. run_hook 'pre-clean'
  788. each_selected_installers {|inst| inst.exec_clean }
  789. run_hook 'post-clean'
  790. end
  791. def exec_distclean
  792. rm_f 'config.save'
  793. run_hook 'pre-distclean'
  794. each_selected_installers {|inst| inst.exec_distclean }
  795. run_hook 'post-distclean'
  796. end
  797. #
  798. # lib
  799. #
  800. def each_selected_installers
  801. Dir.mkdir 'packages' unless File.dir?('packages')
  802. @selected.each do |pack|
  803. $stderr.puts "Processing the package `#{pack}' ..." if @options['verbose']
  804. Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")
  805. Dir.chdir "packages/#{pack}"
  806. yield @installers[pack]
  807. Dir.chdir '../..'
  808. end
  809. end
  810. def verbose?
  811. @options['verbose']
  812. end
  813. def no_harm?
  814. @options['no-harm']
  815. end
  816. end
  817. class Installer
  818. FILETYPES = %w( bin lib ext data )
  819. include HookScriptAPI
  820. include HookUtils
  821. include FileOperations
  822. def initialize(config, opt, srcroot, objroot)
  823. @config = config
  824. @options = opt
  825. @srcdir = File.expand_path(srcroot)
  826. @objdir = File.expand_path(objroot)
  827. @currdir = '.'
  828. end
  829. def inspect
  830. "#<#{self.class} #{File.basename(@srcdir)}>"
  831. end
  832. #
  833. # Hook Script API bases
  834. #
  835. def srcdir_root
  836. @srcdir
  837. end
  838. def objdir_root
  839. @objdir
  840. end
  841. def relpath
  842. @currdir
  843. end
  844. #
  845. # configs/options
  846. #
  847. def no_harm?
  848. @options['no-harm']
  849. end
  850. def verbose?
  851. @options['verbose']
  852. end
  853. def verbose_off
  854. begin
  855. save, @options['verbose'] = @options['verbose'], false
  856. yield
  857. ensure
  858. @options['verbose'] = save
  859. end
  860. end
  861. #
  862. # TASK config
  863. #
  864. def exec_config
  865. exec_task_traverse 'config'
  866. end
  867. def config_dir_bin(rel)
  868. end
  869. def config_dir_lib(rel)
  870. end
  871. def config_dir_ext(rel)
  872. extconf if extdir?(curr_srcdir())
  873. end
  874. def extconf
  875. opt = @options['config-opt'].join(' ')
  876. command "#{config('ruby-prog')} #{curr_srcdir()}/extconf.rb #{opt}"
  877. end
  878. def config_dir_data(rel)
  879. end
  880. #
  881. # TASK setup
  882. #
  883. def exec_setup
  884. exec_task_traverse 'setup'
  885. end
  886. def setup_dir_bin(rel)
  887. all_files_in(curr_srcdir()).each do |fname|
  888. adjust_shebang "#{curr_srcdir()}/#{fname}"
  889. end
  890. end
  891. # modify: #!/usr/bin/ruby
  892. # modify: #! /usr/bin/ruby
  893. # modify: #!ruby
  894. # not modify: #!/usr/bin/env ruby
  895. SHEBANG_RE = /\A\#!\s*\S*ruby\S*/
  896. def adjust_shebang(path)
  897. return if no_harm?
  898. tmpfile = File.basename(path) + '.tmp'
  899. begin
  900. File.open(path, 'rb') {|r|
  901. File.open(tmpfile, 'wb') {|w|
  902. first = r.gets
  903. return unless SHEBANG_RE =~ first
  904. $stderr.puts "adjusting shebang: #{File.basename path}" if verbose?
  905. w.print first.sub(SHEBANG_RE, '#!' + config('ruby-path'))
  906. w.write r.read
  907. }
  908. }
  909. move_file tmpfile, File.basename(path)
  910. ensure
  911. File.unlink tmpfile if File.exist?(tmpfile)
  912. end
  913. end
  914. def setup_dir_lib(rel)
  915. end
  916. def setup_dir_ext(rel)
  917. make if extdir?(curr_srcdir())
  918. end
  919. def setup_dir_data(rel)
  920. end
  921. #
  922. # TASK install
  923. #
  924. def exec_install
  925. exec_task_traverse 'install'
  926. end
  927. def install_dir_bin(rel)
  928. install_files collect_filenames_auto(), "#{config('bin-dir')}/#{rel}", 0755
  929. end
  930. def install_dir_lib(rel)
  931. install_files ruby_scripts(), "#{config('rb-dir')}/#{rel}", 0644
  932. end
  933. def install_dir_ext(rel)
  934. return unless extdir?(curr_srcdir())
  935. install_files ruby_extentions('.'),
  936. "#{config('so-dir')}/#{File.dirname(rel)}",
  937. 0555
  938. end
  939. def install_dir_data(rel)
  940. install_files collect_filenames_auto(), "#{config('data-dir')}/#{rel}", 0644
  941. end
  942. def install_files(list, dest, mode)
  943. mkdir_p dest, @options['install-prefix']
  944. list.each do |fname|
  945. install fname, dest, mode, @options['install-prefix']
  946. end
  947. end
  948. def ruby_scripts
  949. collect_filenames_auto().select {|n| /\.rb\z/ =~ n }
  950. end
  951. # picked up many entries from cvs-1.11.1/src/ignore.c
  952. reject_patterns = %w(
  953. core RCSLOG tags TAGS .make.state
  954. .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
  955. *~ *.old *.bak *.BAK *.orig *.rej _$* *$
  956. *.org *.in .*
  957. )
  958. mapping = {
  959. '.' => '\.',
  960. '$' => '\$',
  961. '#' => '\#',
  962. '*' => '.*'
  963. }
  964. REJECT_PATTERNS = Regexp.new('\A(?:' +
  965. reject_patterns.map {|pat|
  966. pat.gsub(/[\.\$\#\*]/) {|ch| mapping[ch] }
  967. }.join('|') +
  968. ')\z')
  969. def collect_filenames_auto
  970. mapdir((existfiles() - hookfiles()).reject {|fname|
  971. REJECT_PATTERNS =~ fname
  972. })
  973. end
  974. def existfiles
  975. all_files_in(curr_srcdir()) | all_files_in('.')
  976. end
  977. def hookfiles
  978. %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|
  979. %w( config setup install clean ).map {|t| sprintf(fmt, t) }
  980. }.flatten
  981. end
  982. def mapdir(filelist)
  983. filelist.map {|fname|
  984. if File.exist?(fname) # objdir
  985. fname
  986. else # srcdir
  987. File.join(curr_srcdir(), fname)
  988. end
  989. }
  990. end
  991. def ruby_extentions(dir)
  992. _ruby_extentions(dir) or
  993. raise InstallError, "no ruby extention exists: 'ruby #{$0} setup' first"
  994. end
  995. DLEXT = /\.#{ ::Config::CONFIG['DLEXT'] }\z/
  996. def _ruby_extentions(dir)
  997. Dir.open(dir) {|d|
  998. return d.select {|fname| DLEXT =~ fname }
  999. }
  1000. end
  1001. #
  1002. # TASK clean
  1003. #
  1004. def exec_clean
  1005. exec_task_traverse 'clean'
  1006. rm_f 'config.save'
  1007. rm_f 'InstalledFiles'
  1008. end
  1009. def clean_dir_bin(rel)
  1010. end
  1011. def clean_dir_lib(rel)
  1012. end
  1013. def clean_dir_ext(rel)
  1014. return unless extdir?(curr_srcdir())
  1015. make 'clean' if File.file?('Makefile')
  1016. end
  1017. def clean_dir_data(rel)
  1018. end
  1019. #
  1020. # TASK distclean
  1021. #
  1022. def exec_distclean
  1023. exec_task_traverse 'distclean'
  1024. rm_f 'config.save'
  1025. rm_f 'InstalledFiles'
  1026. end
  1027. def distclean_dir_bin(rel)
  1028. end
  1029. def distclean_dir_lib(rel)
  1030. end
  1031. def distclean_dir_ext(rel)
  1032. return unless extdir?(curr_srcdir())
  1033. make 'distclean' if File.file?('Makefile')
  1034. end
  1035. #
  1036. # lib
  1037. #
  1038. def exec_task_traverse(task)
  1039. run_hook "pre-#{task}"
  1040. FILETYPES.each do |type|
  1041. if config('without-ext') == 'yes' and type == 'ext'
  1042. $stderr.puts 'skipping ext/* by user option' if verbose?
  1043. next
  1044. end
  1045. traverse task, type, "#{task}_dir_#{type}"
  1046. end
  1047. run_hook "post-#{task}"
  1048. end
  1049. def traverse(task, rel, mid)
  1050. dive_into(rel) {
  1051. run_hook "pre-#{task}"
  1052. __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
  1053. all_dirs_in(curr_srcdir()).each do |d|
  1054. traverse task, "#{rel}/#{d}", mid
  1055. end
  1056. run_hook "post-#{task}"
  1057. }
  1058. end
  1059. def dive_into(rel)
  1060. return unless File.dir?("#{@srcdir}/#{rel}")
  1061. dir = File.basename(rel)
  1062. Dir.mkdir dir unless File.dir?(dir)
  1063. prevdir = Dir.pwd
  1064. Dir.chdir dir
  1065. $stderr.puts '---> ' + rel if verbose?
  1066. @currdir = rel
  1067. yield
  1068. Dir.chdir prevdir
  1069. $stderr.puts '<--- ' + rel if verbose?
  1070. @currdir = File.dirname(rel)
  1071. end
  1072. end
  1073. if $0 == __FILE__
  1074. begin
  1075. if multipackage_install?
  1076. ToplevelInstallerMulti.invoke
  1077. else
  1078. ToplevelInstaller.invoke
  1079. end
  1080. rescue
  1081. raise if $DEBUG
  1082. $stderr.puts $!.message
  1083. $stderr.puts "Try 'ruby #{$0} --help' for detailed usage."
  1084. exit 1
  1085. end
  1086. end