ledermann-tmail 1.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. data/CHANGES +74 -0
  2. data/LICENSE +21 -0
  3. data/NOTES +7 -0
  4. data/README +179 -0
  5. data/Rakefile +2 -0
  6. data/ext/Makefile +20 -0
  7. data/ext/tmailscanner/tmail/MANIFEST +4 -0
  8. data/ext/tmailscanner/tmail/depend +1 -0
  9. data/ext/tmailscanner/tmail/extconf.rb +33 -0
  10. data/ext/tmailscanner/tmail/tmailscanner.c +614 -0
  11. data/lib/tmail.rb +6 -0
  12. data/lib/tmail/Makefile +18 -0
  13. data/lib/tmail/address.rb +392 -0
  14. data/lib/tmail/attachments.rb +65 -0
  15. data/lib/tmail/base64.rb +46 -0
  16. data/lib/tmail/compat.rb +41 -0
  17. data/lib/tmail/config.rb +67 -0
  18. data/lib/tmail/core_extensions.rb +63 -0
  19. data/lib/tmail/encode.rb +590 -0
  20. data/lib/tmail/header.rb +962 -0
  21. data/lib/tmail/index.rb +9 -0
  22. data/lib/tmail/interface.rb +1162 -0
  23. data/lib/tmail/loader.rb +3 -0
  24. data/lib/tmail/mail.rb +578 -0
  25. data/lib/tmail/mailbox.rb +496 -0
  26. data/lib/tmail/main.rb +6 -0
  27. data/lib/tmail/mbox.rb +3 -0
  28. data/lib/tmail/net.rb +250 -0
  29. data/lib/tmail/obsolete.rb +132 -0
  30. data/lib/tmail/parser.rb +1060 -0
  31. data/lib/tmail/parser.y +416 -0
  32. data/lib/tmail/port.rb +379 -0
  33. data/lib/tmail/quoting.rb +155 -0
  34. data/lib/tmail/require_arch.rb +58 -0
  35. data/lib/tmail/scanner.rb +49 -0
  36. data/lib/tmail/scanner_r.rb +261 -0
  37. data/lib/tmail/stringio.rb +280 -0
  38. data/lib/tmail/utils.rb +361 -0
  39. data/lib/tmail/version.rb +39 -0
  40. data/log/BugTrackingLog.txt +1245 -0
  41. data/log/Changelog.txt +534 -0
  42. data/log/Testlog.txt +2340 -0
  43. data/log/Todo.txt +30 -0
  44. data/meta/MANIFEST +128 -0
  45. data/meta/VERSION +1 -0
  46. data/meta/project.yaml +30 -0
  47. data/meta/unixname +1 -0
  48. data/sample/bench_base64.rb +48 -0
  49. data/sample/data/multipart +23 -0
  50. data/sample/data/normal +29 -0
  51. data/sample/data/sendtest +5 -0
  52. data/sample/data/simple +14 -0
  53. data/sample/data/test +27 -0
  54. data/sample/extract-attachements.rb +33 -0
  55. data/sample/from-check.rb +26 -0
  56. data/sample/multipart.rb +26 -0
  57. data/sample/parse-bench.rb +68 -0
  58. data/sample/parse-test.rb +19 -0
  59. data/sample/sendmail.rb +94 -0
  60. data/setup.rb +1482 -0
  61. data/site/contributing/index.html +183 -0
  62. data/site/css/clean.css +27 -0
  63. data/site/css/layout.css +31 -0
  64. data/site/css/style.css +60 -0
  65. data/site/download/index.html +61 -0
  66. data/site/img/envelope.jpg +0 -0
  67. data/site/img/mailman.gif +0 -0
  68. data/site/img/stamp-sm.jpg +0 -0
  69. data/site/img/stamp.jpg +0 -0
  70. data/site/img/stampborder.jpg +0 -0
  71. data/site/img/tfire.jpg +0 -0
  72. data/site/img/tmail.png +0 -0
  73. data/site/index.html +272 -0
  74. data/site/js/jquery.js +31 -0
  75. data/site/log/Changelog.xsl +33 -0
  76. data/site/log/changelog.xml +1677 -0
  77. data/site/outdated/BUGS +3 -0
  78. data/site/outdated/DEPENDS +1 -0
  79. data/site/outdated/Incompatibilities +89 -0
  80. data/site/outdated/Incompatibilities.ja +102 -0
  81. data/site/outdated/NEWS +9 -0
  82. data/site/outdated/README.ja +73 -0
  83. data/site/outdated/doc.ja/address.html +275 -0
  84. data/site/outdated/doc.ja/basics.html +405 -0
  85. data/site/outdated/doc.ja/config.html +49 -0
  86. data/site/outdated/doc.ja/details.html +146 -0
  87. data/site/outdated/doc.ja/index.html +39 -0
  88. data/site/outdated/doc.ja/mail.html +793 -0
  89. data/site/outdated/doc.ja/mailbox.html +265 -0
  90. data/site/outdated/doc.ja/port.html +95 -0
  91. data/site/outdated/doc.ja/tmail.html +58 -0
  92. data/site/outdated/doc.ja/usage.html +202 -0
  93. data/site/outdated/rdd/address.rrd.m +229 -0
  94. data/site/outdated/rdd/basics.rd.m +275 -0
  95. data/site/outdated/rdd/config.rrd.m +26 -0
  96. data/site/outdated/rdd/details.rd.m +117 -0
  97. data/site/outdated/rdd/index.rhtml.m +54 -0
  98. data/site/outdated/rdd/mail.rrd.m +701 -0
  99. data/site/outdated/rdd/mailbox.rrd.m +228 -0
  100. data/site/outdated/rdd/port.rrd.m +69 -0
  101. data/site/outdated/rdd/tmail.rrd.m +33 -0
  102. data/site/outdated/rdd/usage.rd.m +247 -0
  103. data/site/quickstart/index.html +69 -0
  104. data/site/quickstart/quickstart.html +52 -0
  105. data/site/quickstart/usage.html +193 -0
  106. data/site/reference/address.html +247 -0
  107. data/site/reference/config.html +30 -0
  108. data/site/reference/index.html +101 -0
  109. data/site/reference/mail.html +726 -0
  110. data/site/reference/mailbox.html +245 -0
  111. data/site/reference/port.html +75 -0
  112. data/site/reference/tmail.html +35 -0
  113. data/test/extctrl.rb +6 -0
  114. data/test/fixtures/mailbox +414 -0
  115. data/test/fixtures/mailbox_without_any_from_or_sender +10 -0
  116. data/test/fixtures/mailbox_without_from +11 -0
  117. data/test/fixtures/mailbox_without_return_path +12 -0
  118. data/test/fixtures/raw_attack_email_with_zero_length_whitespace +29 -0
  119. data/test/fixtures/raw_base64_decoded_string +0 -0
  120. data/test/fixtures/raw_base64_email +83 -0
  121. data/test/fixtures/raw_base64_encoded_string +1 -0
  122. data/test/fixtures/raw_email +14 -0
  123. data/test/fixtures/raw_email10 +20 -0
  124. data/test/fixtures/raw_email11 +34 -0
  125. data/test/fixtures/raw_email12 +32 -0
  126. data/test/fixtures/raw_email13 +29 -0
  127. data/test/fixtures/raw_email2 +114 -0
  128. data/test/fixtures/raw_email3 +70 -0
  129. data/test/fixtures/raw_email4 +59 -0
  130. data/test/fixtures/raw_email5 +19 -0
  131. data/test/fixtures/raw_email6 +20 -0
  132. data/test/fixtures/raw_email7 +66 -0
  133. data/test/fixtures/raw_email8 +47 -0
  134. data/test/fixtures/raw_email9 +28 -0
  135. data/test/fixtures/raw_email_multiple_from +30 -0
  136. data/test/fixtures/raw_email_quoted_with_0d0a +14 -0
  137. data/test/fixtures/raw_email_reply +32 -0
  138. data/test/fixtures/raw_email_simple +11 -0
  139. data/test/fixtures/raw_email_with_bad_date +48 -0
  140. data/test/fixtures/raw_email_with_illegal_boundary +58 -0
  141. data/test/fixtures/raw_email_with_mimepart_without_content_type +94 -0
  142. data/test/fixtures/raw_email_with_multipart_mixed_quoted_boundary +50 -0
  143. data/test/fixtures/raw_email_with_nested_attachment +100 -0
  144. data/test/fixtures/raw_email_with_partially_quoted_subject +14 -0
  145. data/test/fixtures/raw_email_with_quoted_illegal_boundary +58 -0
  146. data/test/kcode.rb +14 -0
  147. data/test/temp_test_one.rb +46 -0
  148. data/test/test_address.rb +1216 -0
  149. data/test/test_attachments.rb +131 -0
  150. data/test/test_base64.rb +64 -0
  151. data/test/test_encode.rb +139 -0
  152. data/test/test_header.rb +1021 -0
  153. data/test/test_helper.rb +9 -0
  154. data/test/test_mail.rb +756 -0
  155. data/test/test_mbox.rb +184 -0
  156. data/test/test_port.rb +440 -0
  157. data/test/test_quote.rb +107 -0
  158. data/test/test_scanner.rb +209 -0
  159. data/test/test_utils.rb +36 -0
  160. data/work/script/make +26 -0
  161. data/work/script/rdoc +39 -0
  162. data/work/script/setup +1616 -0
  163. data/work/script/test +30 -0
  164. metadata +297 -0
@@ -0,0 +1,68 @@
1
+ #
2
+ # parser benchmark
3
+ #
4
+
5
+ require 'tmail'
6
+
7
+
8
+ if ARGV.empty? then
9
+ $stderr.puts "usage: #{$0} <mhdir> <mhdir>..."
10
+ exit 0
11
+ end
12
+ ARGV.each do |dname|
13
+ unless File.directory? dname then
14
+ $stderr.puts "not directory: #{dname}"
15
+ exit 1
16
+ end
17
+ end
18
+
19
+ $stdout.sync = true
20
+
21
+ $count = 0
22
+ $failnum = 0
23
+ $dirfail = 0
24
+ $fieldname = ''
25
+ $dirname = ''
26
+ $port = nil
27
+
28
+ begin
29
+ ARGV.each do |dirname|
30
+ $dirname = dirname
31
+
32
+ TMail::MhLoader.new( dirname ).each do |port|
33
+ begin
34
+ t = TMail::Mail.new( port )
35
+ $port = port
36
+
37
+ t.each_header do |key, field|
38
+ $fieldname = key
39
+ next if /received/i === key
40
+ if ::TMail::StructH === field then
41
+ field.instance_eval { parse unless @parsed }
42
+ end
43
+ end
44
+ rescue TMail::SyntaxError
45
+ $stderr.puts "fail in #{$count+1}, field #{$fieldname}"
46
+ $stderr.puts $!.to_s
47
+ $failnum += 1
48
+ $dirfail += 1
49
+
50
+ if $failnum % 10 == 0 then
51
+ puts 'fail = ' + $failnum.to_s
52
+ #raise
53
+ end
54
+ end
55
+
56
+ $count += 1
57
+ puts "end #{$count}" if $count % 50 == 0
58
+ end
59
+
60
+ puts "directory #{dirname} end, fail=#{$dirfail}"
61
+ $dirfail = 0
62
+ end
63
+ rescue
64
+ puts "at #{$port.inspect}, non ParseError raised"
65
+ raise
66
+ end
67
+
68
+ puts "parse #{$count} files, fail=#{$failnum}"
@@ -0,0 +1,19 @@
1
+ #
2
+ # parser test
3
+ #
4
+
5
+ require 'tmail'
6
+
7
+ puts "testing parser --------------------------------"
8
+ puts
9
+
10
+ TMail::Mail.load( 'data/normal' ).each_header do |key, field|
11
+ if field.respond_to? :parse, true then
12
+ field.instance_eval {
13
+ parse
14
+ @written = true
15
+ }
16
+ end
17
+ printf "%s ok\n", field.name
18
+ # puts field.decoded
19
+ end
@@ -0,0 +1,94 @@
1
+ #
2
+ # sendmail.rb
3
+ #
4
+
5
+ require 'tmail'
6
+ require 'net/smtp'
7
+ require 'nkf'
8
+ require 'etc'
9
+ require 'socket'
10
+ require 'getopts'
11
+
12
+
13
+ def usage( status, msg = nil )
14
+ output = (status == 0 ? $stdout : $stderr)
15
+ output.puts msg if msg
16
+ output.print(<<EOS)
17
+ Usage: cat msg | #{File.basename $0} [-j|--ja] [-s <subject>] [-f <from>] <to>
18
+
19
+ -h,--host=addr SMTP server address. (default=localhost)
20
+ -s,--subject=sbj subject of the message. (default=(none))
21
+ -f,--from=from from address.
22
+ -j,--ja handle japanese message. (default=off)
23
+
24
+ EOS
25
+ exit status
26
+ end
27
+
28
+ def main
29
+ getopts('j', 'ja', 'h:', 'host:',
30
+ 's:', 'subject:', 'f:', 'from:',
31
+ 'help') or usage(1)
32
+
33
+ smtphost = $OPT_host || $OPT_h || 'localhost'
34
+ subject = $OPT_subject || $OPT_s
35
+ from = $OPT_from || $OPT_f || guess_from_address()
36
+ usage(1, 'Sender address not given') unless from
37
+ to = ARGV
38
+ usage(1, 'Receipt address(es) not given') if to.empty?
39
+ ja_locale = $OPT_ja || $OPT_j
40
+
41
+ send_mail smtphost, setup_mail(from, to, subject, $stdin.read, ja_locale)
42
+ end
43
+
44
+ def setup_mail( from, to, subject, body, ja_locale )
45
+ mail = TMail::Mail.new
46
+ mail.date = Time.now
47
+ mail.from = from
48
+ mail.to = to
49
+ mail.subject = subject if subject
50
+ mail.mime_version = '1.0'
51
+ if ja_locale
52
+ mail.body = NKF.nkf('-j', body)
53
+ mail.set_content_type 'text', 'plain', 'charset' => 'iso-2022-jp'
54
+ else
55
+ mail.body = body
56
+ mail.set_content_type 'text', 'plain'
57
+ end
58
+ mail
59
+ end
60
+
61
+ def send_mail( host, mail )
62
+ msg = mail.encoded
63
+ $stderr.print msg if $DEBUG
64
+
65
+ smtp = Net::SMTP.new(host, 25)
66
+ smtp.set_debug_output $stderr if $DEBUG
67
+ smtp.start {
68
+ smtp.send_mail msg, mail.from_address, mail.destinations
69
+ }
70
+ end
71
+
72
+ def guess_from_address
73
+ user = getlogin()
74
+ unless user
75
+ $stderr.puts 'cannot get user account; abort.'
76
+ exit 1
77
+ end
78
+ if domain = (Socket.gethostname || ENV['HOSTNAME'] || ENV['HOST'])
79
+ user + '@' + domain
80
+ else
81
+ user
82
+ end
83
+ end
84
+
85
+ def getlogin
86
+ begin
87
+ require 'etc'
88
+ Etc.getlogin
89
+ rescue LoadError
90
+ ENV['LOGNAME'] || ENV['USER']
91
+ end
92
+ end
93
+
94
+ main
data/setup.rb ADDED
@@ -0,0 +1,1482 @@
1
+ #!/usr/bin/env ruby
2
+ # Setup.rb v3.5.0
3
+ # Copyright (c) 2008 Minero Aoki, Trans
4
+ #
5
+ # This program is free software.
6
+ # You can distribute/modify this program under the terms of
7
+ # the GNU LGPL, Lesser General Public License version 2.1.
8
+
9
+ # Need the package name, and whether to generate documentation.
10
+ PACKAGE = File.read(Dir.glob('{.,meta/}unixname{,.txt}', File::FNM_CASEFOLD).first).strip
11
+ GENERATE_RDOCS = true # package developer may need to deactivate
12
+
13
+ require 'optparse'
14
+ require 'rbconfig'
15
+
16
+ class SetupError < StandardError; end
17
+
18
+ # Typical installation procedure:
19
+ #
20
+ # $ ./setup.rb
21
+ #
22
+ # -- or --
23
+ #
24
+ # $ ./setup.rb config
25
+ # $ ./setup.rb setup
26
+ # $ ./setup.rb install
27
+ #
28
+ # @all@ and @install@ may require root privileges.
29
+ #
30
+ # This update only works with Ruby 1.6.3 and above.
31
+ #
32
+ # TODO: Update shebangs on install of binaries.
33
+ # TODO: Make cleaning more comprehensive (?)
34
+
35
+ module Setup
36
+ Version = "3.5.0"
37
+
38
+ Copyright = "Copyright (c) 2000,2008 Minero Aoki, Trans"
39
+
40
+ # ConfigTable stores platform information.
41
+
42
+ class ConfigTable
43
+
44
+ RBCONFIG = ::Config::CONFIG
45
+
46
+ CONFIGFILE = '.config'
47
+
48
+ DESCRIPTIONS = [
49
+ [:prefix , :path, 'path prefix of target environment'],
50
+ [:bindir , :path, 'directory for commands'],
51
+ [:libdir , :path, 'directory for libraries'],
52
+ [:datadir , :path, 'directory for shared data'],
53
+ [:mandir , :path, 'directory for man pages'],
54
+ [:docdir , :path, 'Directory for documentation'],
55
+ [:sysconfdir , :path, 'directory for system configuration files'],
56
+ [:localstatedir , :path, 'directory for local state data'],
57
+ [:libruby , :path, 'directory for ruby libraries'],
58
+ [:librubyver , :path, 'directory for standard ruby libraries'],
59
+ [:librubyverarch , :path, 'directory for standard ruby extensions'],
60
+ [:siteruby , :path, 'directory for version-independent aux ruby libraries'],
61
+ [:siterubyver , :path, 'directory for aux ruby libraries'],
62
+ [:siterubyverarch , :path, 'directory for aux ruby binaries'],
63
+ [:rbdir , :path, 'directory for ruby scripts'],
64
+ [:sodir , :path, 'directory for ruby extentions'],
65
+ [:rubypath , :prog, 'path to set to #! line'],
66
+ [:rubyprog , :prog, 'ruby program using for installation'],
67
+ [:makeprog , :prog, 'make program to compile ruby extentions'],
68
+ [:extconfopt , :name, 'options to pass-thru to extconf.rb'],
69
+ [:without_ext , :bool, 'do not compile/install ruby extentions'],
70
+ [:without_doc , :bool, 'do not generate html documentation'],
71
+ [:shebang , :pick, 'shebang line (#!) editing mode (all,ruby,never)'],
72
+ [:doctemplate , :pick, 'document template to use (html|xml)'],
73
+ [:testrunner , :pick, 'Runner to use for testing (auto|console|tk|gtk|gtk2)'],
74
+ [:installdirs , :pick, 'install location mode (std,site,home :: libruby,site_ruby,$HOME)']
75
+ ]
76
+
77
+ # List of configurable options.
78
+ OPTIONS = DESCRIPTIONS.collect{ |(k,t,v)| k.to_s }
79
+
80
+ # Pathname attribute. Pathnames are automatically expanded
81
+ # unless they start with '$', a path variable.
82
+ def self.attr_pathname(name)
83
+ class_eval %{
84
+ def #{name}
85
+ @#{name}.gsub(%r<\\$([^/]+)>){ self[$1] }
86
+ end
87
+ def #{name}=(path)
88
+ raise SetupError, "bad config: #{name.to_s.upcase} requires argument" unless path
89
+ @#{name} = (path[0,1] == '$' ? path : File.expand_path(path))
90
+ end
91
+ }
92
+ end
93
+
94
+ # List of pathnames. These are not expanded though.
95
+ def self.attr_pathlist(name)
96
+ class_eval %{
97
+ def #{name}
98
+ @#{name}
99
+ end
100
+ def #{name}=(pathlist)
101
+ case pathlist
102
+ when Array
103
+ @#{name} = pathlist
104
+ else
105
+ @#{name} = pathlist.to_s.split(/[:;,]/)
106
+ end
107
+ end
108
+ }
109
+ end
110
+
111
+ # Adds boolean support.
112
+ def self.attr_accessor(*names)
113
+ bools, attrs = names.partition{ |name| name.to_s =~ /\?$/ }
114
+ attr_boolean *bools
115
+ super *attrs
116
+ end
117
+
118
+ # Boolean attribute. Can be assigned true, false, nil, or
119
+ # a string matching yes|true|y|t or no|false|n|f.
120
+ def self.attr_boolean(*names)
121
+ names.each do |name|
122
+ name = name.to_s.chomp('?')
123
+ attr_reader name # MAYBE: Deprecate
124
+ code = %{
125
+ def #{name}?; @#{name}; end
126
+ def #{name}=(val)
127
+ case val
128
+ when true, false, nil
129
+ @#{name} = val
130
+ else
131
+ case val.to_s.downcase
132
+ when 'y', 'yes', 't', 'true'
133
+ @#{name} = true
134
+ when 'n', 'no', 'f', 'false'
135
+ @#{name} = false
136
+ else
137
+ raise SetupError, "bad config: use #{name.upcase}=(yes|no) [\#{val}]"
138
+ end
139
+ end
140
+ end
141
+ }
142
+ class_eval code
143
+ end
144
+ end
145
+
146
+ DESCRIPTIONS.each do |k,t,d|
147
+ case t
148
+ when :path
149
+ attr_pathname k
150
+ when :bool
151
+ attr_boolean k
152
+ else
153
+ attr_accessor k
154
+ end
155
+ end
156
+
157
+ # # provide verbosity (default is true)
158
+ # attr_accessor :verbose?
159
+
160
+ # # don't actually write files to system
161
+ # attr_accessor :no_harm?
162
+
163
+ # shebang has only three options.
164
+ def shebang=(val)
165
+ if %w(all ruby never).include?(val)
166
+ @shebang = val
167
+ else
168
+ raise SetupError, "bad config: use SHEBANG=(all|ruby|never) [#{val}]"
169
+ end
170
+ end
171
+
172
+ # installdirs has only three options; and it has side-effects.
173
+ def installdirs=(val)
174
+ @installdirs = val
175
+ case val.to_s
176
+ when 'std'
177
+ self.rbdir = '$librubyver'
178
+ self.sodir = '$librubyverarch'
179
+ when 'site'
180
+ self.rbdir = '$siterubyver'
181
+ self.sodir = '$siterubyverarch'
182
+ when 'home'
183
+ raise SetupError, 'HOME is not set.' unless ENV['HOME']
184
+ self.prefix = ENV['HOME']
185
+ self.rbdir = '$libdir/ruby'
186
+ self.sodir = '$libdir/ruby'
187
+ else
188
+ raise SetupError, "bad config: use INSTALLDIRS=(std|site|home|local) [#{val}]"
189
+ end
190
+ end
191
+
192
+ # New ConfigTable
193
+ def initialize(values=nil)
194
+ initialize_defaults
195
+ if values
196
+ values.each{ |k,v| __send__("#{k}=", v) }
197
+ end
198
+ yeild(self) if block_given?
199
+ load_config if File.file?(CONFIGFILE)
200
+ end
201
+
202
+ # Assign CONFIG defaults
203
+ #
204
+ # TODO: Does this handle 'nmake' on windows?
205
+ def initialize_defaults
206
+ prefix = RBCONFIG['prefix']
207
+
208
+ rubypath = File.join(RBCONFIG['bindir'], RBCONFIG['ruby_install_name'] + RBCONFIG['EXEEXT'])
209
+
210
+ major = RBCONFIG['MAJOR'].to_i
211
+ minor = RBCONFIG['MINOR'].to_i
212
+ teeny = RBCONFIG['TEENY'].to_i
213
+ version = "#{major}.#{minor}"
214
+
215
+ # ruby ver. >= 1.4.4?
216
+ newpath_p = ((major >= 2) or
217
+ ((major == 1) and
218
+ ((minor >= 5) or
219
+ ((minor == 4) and (teeny >= 4)))))
220
+
221
+ if RBCONFIG['rubylibdir']
222
+ # V > 1.6.3
223
+ libruby = "#{prefix}/lib/ruby"
224
+ librubyver = RBCONFIG['rubylibdir']
225
+ librubyverarch = RBCONFIG['archdir']
226
+ siteruby = RBCONFIG['sitedir']
227
+ siterubyver = RBCONFIG['sitelibdir']
228
+ siterubyverarch = RBCONFIG['sitearchdir']
229
+ elsif newpath_p
230
+ # 1.4.4 <= V <= 1.6.3
231
+ libruby = "#{prefix}/lib/ruby"
232
+ librubyver = "#{prefix}/lib/ruby/#{version}"
233
+ librubyverarch = "#{prefix}/lib/ruby/#{version}/#{c['arch']}"
234
+ siteruby = RBCONFIG['sitedir']
235
+ siterubyver = "$siteruby/#{version}"
236
+ siterubyverarch = "$siterubyver/#{RBCONFIG['arch']}"
237
+ else
238
+ # V < 1.4.4
239
+ libruby = "#{prefix}/lib/ruby"
240
+ librubyver = "#{prefix}/lib/ruby/#{version}"
241
+ librubyverarch = "#{prefix}/lib/ruby/#{version}/#{c['arch']}"
242
+ siteruby = "#{prefix}/lib/ruby/#{version}/site_ruby"
243
+ siterubyver = siteruby
244
+ siterubyverarch = "$siterubyver/#{RBCONFIG['arch']}"
245
+ end
246
+
247
+ if arg = RBCONFIG['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
248
+ makeprog = arg.sub(/'/, '').split( /=/, 2)[1]
249
+ else
250
+ makeprog = 'make'
251
+ end
252
+
253
+ parameterize = lambda do |path|
254
+ val = RBCONFIG[path]
255
+ raise "Unknown path -- #{path}" if val.nil?
256
+ val.sub(/\A#{Regexp.quote(prefix)}/, '$prefix')
257
+ end
258
+
259
+ self.prefix = prefix
260
+ self.bindir = parameterize['bindir']
261
+ self.libdir = parameterize['libdir']
262
+ self.datadir = parameterize['datadir']
263
+ self.mandir = parameterize['mandir']
264
+ self.docdir = File.dirname(parameterize['docdir']) # b/c of trailing $(PACKAGE)
265
+ self.sysconfdir = parameterize['sysconfdir']
266
+ self.localstatedir = parameterize['localstatedir']
267
+ self.libruby = libruby
268
+ self.librubyver = librubyver
269
+ self.librubyverarch = librubyverarch
270
+ self.siteruby = siteruby
271
+ self.siterubyver = siterubyver
272
+ self.siterubyverarch = siterubyverarch
273
+ self.rbdir = '$siterubyver'
274
+ self.sodir = '$siterubyverarch'
275
+ self.rubypath = rubypath
276
+ self.rubyprog = rubypath
277
+ self.makeprog = makeprog
278
+ self.extconfopt = ''
279
+ self.shebang = 'ruby'
280
+ self.without_ext = 'no'
281
+ self.without_doc = 'yes'
282
+ self.doctemplate = 'html'
283
+ self.testrunner = 'auto'
284
+ self.installdirs = 'site'
285
+ end
286
+
287
+ # Get configuration from environment.
288
+ def env_config
289
+ OPTIONS.each do |name|
290
+ if value = ENV[name]
291
+ __send__("#{name}=",value)
292
+ end
293
+ end
294
+ end
295
+
296
+ # Load configuration.
297
+ def load_config
298
+ #if File.file?(CONFIGFILE)
299
+ begin
300
+ File.foreach(CONFIGFILE) do |line|
301
+ k, v = *line.split( /=/, 2)
302
+ __send__("#{k}=",v.strip) #self[k] = v.strip
303
+ end
304
+ rescue Errno::ENOENT
305
+ raise SetupError, $!.message + "\n#{File.basename($0)} config first"
306
+ end
307
+ #end
308
+ end
309
+
310
+ # Save configuration.
311
+ def save_config
312
+ File.open(CONFIGFILE, 'w') do |f|
313
+ OPTIONS.each do |name|
314
+ val = self[name]
315
+ f << "#{name}=#{val}\n"
316
+ end
317
+ end
318
+ end
319
+
320
+ def show
321
+ fmt = "%-20s %s\n"
322
+ OPTIONS.each do |name|
323
+ value = self[name]
324
+ reslv = __send__(name)
325
+ case reslv
326
+ when String
327
+ reslv = "(none)" if reslv.empty?
328
+ when false, nil
329
+ reslv = "no"
330
+ when true
331
+ reslv = "yes"
332
+ end
333
+ printf fmt, name, reslv
334
+ end
335
+ end
336
+
337
+ #
338
+
339
+ def extconfs
340
+ @extconfs ||= Dir['ext/**/extconf.rb']
341
+ end
342
+
343
+ def extensions
344
+ @extensions ||= extconfs.collect{ |f| File.dirname(f) }
345
+ end
346
+
347
+ def compiles?
348
+ !extensions.empty?
349
+ end
350
+
351
+ private
352
+
353
+ # Get unresloved attribute.
354
+ def [](name)
355
+ instance_variable_get("@#{name}")
356
+ end
357
+
358
+ # Set attribute.
359
+ def []=(name, value)
360
+ instance_variable_set("@#{name}", value)
361
+ end
362
+
363
+ # Resolved attribute. (for paths)
364
+ #def resolve(name)
365
+ # self[name].gsub(%r<\\$([^/]+)>){ self[$1] }
366
+ #end
367
+
368
+ end
369
+
370
+ # Installer class handles the actual install procedure,
371
+ # as well as the other tasks, such as testing.
372
+
373
+ class Installer
374
+
375
+ MANIFEST = '.installedfiles'
376
+
377
+ FILETYPES = %w( bin lib ext data conf man doc )
378
+
379
+ TASK_DESCRIPTIONS = [
380
+ [ 'all', 'do config, setup, then install' ],
381
+ [ 'config', 'saves your configurations' ],
382
+ [ 'show', 'shows current configuration' ],
383
+ [ 'setup', 'compiles ruby extentions and others' ],
384
+ [ 'doc', 'generate html documentation' ],
385
+ [ 'index', 'generate index documentation' ],
386
+ [ 'install', 'installs files' ],
387
+ [ 'test', 'run all tests in test/' ],
388
+ [ 'clean', "does `make clean' for each extention" ],
389
+ [ 'distclean',"does `make distclean' for each extention" ]
390
+ ]
391
+
392
+ TASKS = %w(all config show setup test install uninstall doc index clean distclean)
393
+
394
+ # Configuration
395
+ attr :config
396
+
397
+ attr_writer :no_harm
398
+ attr_writer :verbose
399
+ attr_writer :quiet
400
+
401
+ attr_accessor :install_prefix
402
+
403
+ # New Installer.
404
+ def initialize #:yield:
405
+ srcroot = '.'
406
+ objroot = '.'
407
+
408
+ @config = ConfigTable.new
409
+
410
+ @srcdir = File.expand_path(srcroot)
411
+ @objdir = File.expand_path(objroot)
412
+ @currdir = '.'
413
+
414
+ self.quiet = ENV['quiet'] if ENV['quiet']
415
+ self.verbose = ENV['verbose'] if ENV['verbose']
416
+ self.no_harm = ENV['nowrite'] if ENV['nowrite']
417
+
418
+ yield(self) if block_given?
419
+ end
420
+
421
+ def inspect
422
+ "#<#{self.class} #{File.basename(@srcdir)}>"
423
+ end
424
+
425
+ # Are we running an installation?
426
+ def installation?; @installation; end
427
+ def installation!; @installation = true; end
428
+
429
+ def no_harm?; @no_harm; end
430
+ def verbose?; @verbose; end
431
+ def quiet?; @quiet; end
432
+
433
+ def verbose_off #:yield:
434
+ begin
435
+ save, @verbose = verbose?, false
436
+ yield
437
+ ensure
438
+ @verbose = save
439
+ end
440
+ end
441
+
442
+ # Rake task handlers
443
+ def rake_define
444
+ require 'rake/clean'
445
+
446
+ desc 'Config, setup and then install'
447
+ task :all => [:config, :setup, :install]
448
+
449
+ desc 'Saves your configurations'
450
+ task :config do exec_config end
451
+
452
+ desc 'Compiles ruby extentions'
453
+ task :setup do exec_setup end
454
+
455
+ desc 'Runs unit tests'
456
+ task :test do exec_test end
457
+
458
+ desc 'Generate html api docs'
459
+ task :doc do exec_doc end
460
+
461
+ desc 'Generate api index docs'
462
+ task :index do exec_index end
463
+
464
+ desc 'Installs files'
465
+ task :install do exec_install end
466
+
467
+ desc 'Uninstalls files'
468
+ task :uninstall do exec_uninstall end
469
+
470
+ #desc "Does `make clean' for each extention"
471
+ task :makeclean do exec_clean end
472
+
473
+ task :clean => [:makeclean]
474
+
475
+ #desc "Does `make distclean' for each extention"
476
+ task :distclean do exec_distclean end
477
+
478
+ task :clobber => [:distclean]
479
+
480
+ desc 'Shows current configuration'
481
+ task :show do exec_show end
482
+ end
483
+
484
+ # Added these for future use in simplificaiton of design.
485
+
486
+ def extensions
487
+ @extensions ||= Dir['ext/**/extconf.rb']
488
+ end
489
+
490
+ def compiles?
491
+ !extensions.empty?
492
+ end
493
+
494
+ #
495
+ def noop(rel); end
496
+
497
+ #
498
+ # Hook Script API bases
499
+ #
500
+
501
+ def srcdir_root
502
+ @srcdir
503
+ end
504
+
505
+ def objdir_root
506
+ @objdir
507
+ end
508
+
509
+ def relpath
510
+ @currdir
511
+ end
512
+
513
+ #
514
+ # Task all
515
+ #
516
+
517
+ def exec_all
518
+ exec_config
519
+ exec_setup
520
+ exec_test # TODO: we need to stop here if tests fail (how?)
521
+ exec_doc if GENERATE_RDOCS && !config.without_doc?
522
+ exec_install
523
+ end
524
+
525
+ #
526
+ # TASK config
527
+ #
528
+
529
+ def exec_config
530
+ config.env_config
531
+ config.save_config
532
+ config.show unless quiet?
533
+ puts("Configuration saved.") unless quiet?
534
+ exec_task_traverse 'config'
535
+ end
536
+
537
+ alias config_dir_bin noop
538
+ alias config_dir_lib noop
539
+
540
+ def config_dir_ext(rel)
541
+ extconf if extdir?(curr_srcdir())
542
+ end
543
+
544
+ alias config_dir_data noop
545
+ alias config_dir_conf noop
546
+ alias config_dir_man noop
547
+ alias config_dir_doc noop
548
+
549
+ def extconf
550
+ ruby "#{curr_srcdir()}/extconf.rb", config.extconfopt
551
+ end
552
+
553
+ #
554
+ # TASK show
555
+ #
556
+
557
+ def exec_show
558
+ config.show
559
+ end
560
+
561
+ #
562
+ # TASK setup
563
+ #
564
+ # FIXME: Update shebang at time of install not before.
565
+ # for now I've commented it out the shebang.
566
+
567
+ def exec_setup
568
+ exec_task_traverse 'setup'
569
+ end
570
+
571
+ def setup_dir_bin(rel)
572
+ files_of(curr_srcdir()).each do |fname|
573
+ #update_shebang_line "#{curr_srcdir()}/#{fname}"
574
+ end
575
+ end
576
+
577
+ alias setup_dir_lib noop
578
+
579
+ def setup_dir_ext(rel)
580
+ make if extdir?(curr_srcdir())
581
+ end
582
+
583
+ alias setup_dir_data noop
584
+ alias setup_dir_conf noop
585
+ alias setup_dir_man noop
586
+ alias setup_dir_doc noop
587
+
588
+ def update_shebang_line(path)
589
+ return if no_harm?
590
+ return if config.shebang == 'never'
591
+ old = Shebang.load(path)
592
+ if old
593
+ if old.args.size > 1
594
+ $stderr.puts "warning: #{path}"
595
+ $stderr.puts "Shebang line has too many args."
596
+ $stderr.puts "It is not portable and your program may not work."
597
+ end
598
+ new = new_shebang(old)
599
+ return if new.to_s == old.to_s
600
+ else
601
+ return unless config.shebang == 'all'
602
+ new = Shebang.new(config.rubypath)
603
+ end
604
+ $stderr.puts "updating shebang: #{File.basename(path)}" if verbose?
605
+ open_atomic_writer(path) {|output|
606
+ File.open(path, 'rb') {|f|
607
+ f.gets if old # discard
608
+ output.puts new.to_s
609
+ output.print f.read
610
+ }
611
+ }
612
+ end
613
+
614
+ def new_shebang(old)
615
+ if /\Aruby/ =~ File.basename(old.cmd)
616
+ Shebang.new(config.rubypath, old.args)
617
+ elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby'
618
+ Shebang.new(config.rubypath, old.args[1..-1])
619
+ else
620
+ return old unless config.shebang == 'all'
621
+ Shebang.new(config.rubypath)
622
+ end
623
+ end
624
+
625
+ def open_atomic_writer(path, &block)
626
+ tmpfile = File.basename(path) + '.tmp'
627
+ begin
628
+ File.open(tmpfile, 'wb', &block)
629
+ File.rename tmpfile, File.basename(path)
630
+ ensure
631
+ File.unlink tmpfile if File.exist?(tmpfile)
632
+ end
633
+ end
634
+
635
+ class Shebang
636
+ def Shebang.load(path)
637
+ line = nil
638
+ File.open(path) {|f|
639
+ line = f.gets
640
+ }
641
+ return nil unless /\A#!/ =~ line
642
+ parse(line)
643
+ end
644
+
645
+ def Shebang.parse(line)
646
+ cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ')
647
+ new(cmd, args)
648
+ end
649
+
650
+ def initialize(cmd, args = [])
651
+ @cmd = cmd
652
+ @args = args
653
+ end
654
+
655
+ attr_reader :cmd
656
+ attr_reader :args
657
+
658
+ def to_s
659
+ "#! #{@cmd}" + (@args.empty? ? '' : " #{@args.join(' ')}")
660
+ end
661
+ end
662
+
663
+ #
664
+ # TASK test
665
+ #
666
+ # TODO: Add spec support.
667
+
668
+ def exec_test
669
+ $stderr.puts 'Running tests...' if verbose?
670
+
671
+ runner = config.testrunner
672
+
673
+ case runner
674
+ when 'auto'
675
+ unless File.directory?('test')
676
+ $stderr.puts 'no test in this package' if verbose?
677
+ return
678
+ end
679
+ begin
680
+ require 'test/unit'
681
+ rescue LoadError
682
+ setup_rb_error 'test/unit cannot loaded. You need Ruby 1.8 or later to invoke this task.'
683
+ end
684
+ autorunner = Test::Unit::AutoRunner.new(true)
685
+ autorunner.to_run << 'test'
686
+ autorunner.run
687
+ else # use testrb
688
+ opt = []
689
+ opt << " -v" if verbose?
690
+ opt << " --runner #{runner}"
691
+ if File.file?('test/suite.rb')
692
+ notests = false
693
+ opt << "test/suite.rb"
694
+ else
695
+ notests = Dir["test/**/*.rb"].empty?
696
+ lib = ["lib"] + config.extensions.collect{ |d| File.dirname(d) }
697
+ opt << "-I" + lib.join(':')
698
+ opt << Dir["test/**/{test,tc}*.rb"]
699
+ end
700
+ opt = opt.flatten.join(' ').strip
701
+ # run tests
702
+ if notests
703
+ $stderr.puts 'no test in this package' if verbose?
704
+ else
705
+ cmd = "testrb #{opt}"
706
+ $stderr.puts cmd if verbose?
707
+ system cmd #config.ruby "-S tesrb", opt
708
+ end
709
+ end
710
+ end
711
+
712
+ # MAYBE: We could traverse and run each test independently (?)
713
+ #def test_dir_test
714
+ #end
715
+
716
+ #
717
+ # TASK doc
718
+ #
719
+
720
+ def exec_doc
721
+ output = File.join('doc', 'rdoc')
722
+ title = (PACKAGE.capitalize + " API").strip
723
+ main = Dir.glob("README{,.txt}", File::FNM_CASEFOLD).first
724
+ template = config.doctemplate || 'html'
725
+
726
+ opt = []
727
+ opt << "-U"
728
+ opt << "-S"
729
+ opt << "--op=#{output}"
730
+ opt << "--template=#{template}"
731
+ opt << "--title=#{title}"
732
+ opt << "--main=#{main}" if main
733
+
734
+ if File.exist?('.document')
735
+ files = File.read('.document').split("\n")
736
+ files.reject!{ |l| l =~ /^\s*[#]/ || l !~ /\S/ }
737
+ files.collect!{ |f| f.strip }
738
+ opt << files
739
+ else
740
+ opt << main if main
741
+ opt << ["lib", "ext"]
742
+ end
743
+
744
+ opt = opt.flatten
745
+
746
+ if no_harm?
747
+ puts "rdoc " + opt.join(' ').strip
748
+ else
749
+ #sh "rdoc {opt.join(' ').strip}"
750
+ require 'rdoc/rdoc'
751
+ ::RDoc::RDoc.new.document(opt)
752
+ end
753
+ end
754
+
755
+ #
756
+ # TASK index
757
+ #
758
+ # TODO: Totally deprecate stadard ri support in favor of fastri.
759
+
760
+ def exec_index
761
+ begin
762
+ require 'fastri/version'
763
+ fastri = true
764
+ rescue LoadError
765
+ fastri = false
766
+ end
767
+ if fastri
768
+ if no_harm?
769
+ $stderr.puts "fastri-server -b"
770
+ else
771
+ system "fastri-server -b"
772
+ end
773
+ else
774
+ case config.installdirs
775
+ when 'std'
776
+ output = "--ri-system"
777
+ when 'site'
778
+ output = "--ri-site"
779
+ when 'home'
780
+ output = "--ri"
781
+ else
782
+ abort "bad config: sould not be possible -- installdirs = #{config.installdirs}"
783
+ end
784
+
785
+ if File.exist?('.document')
786
+ files = File.read('.document').split("\n")
787
+ files.reject!{ |l| l =~ /^\s*[#]/ || l !~ /\S/ }
788
+ files.collect!{ |f| f.strip }
789
+ else
790
+ files = ["lib", "ext"]
791
+ end
792
+
793
+ opt = []
794
+ opt << "-U"
795
+ opt << output
796
+ opt << files
797
+ opt = opt.flatten
798
+
799
+ if no_harm?
800
+ puts "rdoc #{opt.join(' ').strip}"
801
+ else
802
+ #sh "rdoc #{opt.join(' ').strip}"
803
+ require 'rdoc/rdoc'
804
+ ::RDoc::RDoc.new.document(opt)
805
+ end
806
+ end
807
+ end
808
+
809
+ #
810
+ # TASK install
811
+ #
812
+
813
+ def exec_install
814
+ installation! # were are installing
815
+ #rm_f MANIFEST # we'll append rather then delete!
816
+ exec_task_traverse 'install'
817
+ end
818
+
819
+ def install_dir_bin(rel)
820
+ install_files targetfiles(), "#{config.bindir}/#{rel}", 0755
821
+ end
822
+
823
+ def install_dir_lib(rel)
824
+ install_files libfiles(), "#{config.rbdir}/#{rel}", 0644
825
+ end
826
+
827
+ def install_dir_ext(rel)
828
+ return unless extdir?(curr_srcdir())
829
+ install_files rubyextentions('.'),
830
+ "#{config.sodir}/#{File.dirname(rel)}", 0555
831
+ end
832
+
833
+ def install_dir_data(rel)
834
+ install_files targetfiles(), "#{config.datadir}/#{rel}", 0644
835
+ end
836
+
837
+ def install_dir_conf(rel)
838
+ # FIXME: should not remove current config files
839
+ # (rename previous file to .old/.org)
840
+ install_files targetfiles(), "#{config.sysconfdir}/#{rel}", 0644
841
+ end
842
+
843
+ def install_dir_man(rel)
844
+ install_files targetfiles(), "#{config.mandir}/#{rel}", 0644
845
+ end
846
+
847
+ # doc installs to directory named: "ruby-#{package}"
848
+ def install_dir_doc(rel)
849
+ return if config.without_doc?
850
+ dir = "#{config.docdir}/ruby-#{PACKAGE}/#{rel}" # "#{config.docdir}/#{rel}"
851
+ install_files targetfiles(), dir, 0644
852
+ end
853
+
854
+ def install_files(list, dest, mode)
855
+ mkdir_p dest, install_prefix
856
+ list.each do |fname|
857
+ install fname, dest, mode, install_prefix
858
+ end
859
+ end
860
+
861
+ def libfiles
862
+ glob_reject(%w(*.y *.output), targetfiles())
863
+ end
864
+
865
+ def rubyextentions(dir)
866
+ ents = glob_select("*.#{dllext}", targetfiles())
867
+ if ents.empty?
868
+ setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first"
869
+ end
870
+ ents
871
+ end
872
+
873
+ def dllext
874
+ ConfigTable::RBCONFIG['DLEXT']
875
+ end
876
+
877
+ def targetfiles
878
+ mapdir(existfiles() - hookfiles())
879
+ end
880
+
881
+ def mapdir(ents)
882
+ ents.map {|ent|
883
+ if File.exist?(ent)
884
+ then ent # objdir
885
+ else "#{curr_srcdir()}/#{ent}" # srcdir
886
+ end
887
+ }
888
+ end
889
+
890
+ # picked up many entries from cvs-1.11.1/src/ignore.c
891
+ JUNK_FILES = %w(
892
+ core RCSLOG tags TAGS .make.state
893
+ .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
894
+ *~ *.old *.bak *.BAK *.orig *.rej _$* *$
895
+
896
+ *.org *.in .*
897
+ )
898
+
899
+ def existfiles
900
+ glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.')))
901
+ end
902
+
903
+ def hookfiles
904
+ %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|
905
+ %w( config setup install clean ).map {|t| sprintf(fmt, t) }
906
+ }.flatten
907
+ end
908
+
909
+ def glob_select(pat, ents)
910
+ re = globs2re([pat])
911
+ ents.select {|ent| re =~ ent }
912
+ end
913
+
914
+ def glob_reject(pats, ents)
915
+ re = globs2re(pats)
916
+ ents.reject {|ent| re =~ ent }
917
+ end
918
+
919
+ GLOB2REGEX = {
920
+ '.' => '\.',
921
+ '$' => '\$',
922
+ '#' => '\#',
923
+ '*' => '.*'
924
+ }
925
+
926
+ def globs2re(pats)
927
+ /\A(?:#{
928
+ pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|')
929
+ })\z/
930
+ end
931
+
932
+ #
933
+ # TASK uninstall
934
+ #
935
+
936
+ def exec_uninstall
937
+ paths = File.read(MANIFEST).split("\n")
938
+ dirs, files = paths.partition{ |f| File.dir?(f) }
939
+
940
+ files.each do |file|
941
+ next if /^\#/ =~ file # skip comments
942
+ rm_f(file) if File.exist?(file)
943
+ end
944
+
945
+ dirs.each do |dir|
946
+ # okay this is over kill, but playing it safe...
947
+ empty = Dir[File.join(dir,'*')].empty?
948
+ begin
949
+ if no_harm?
950
+ $stderr.puts "rmdir #{dir}"
951
+ else
952
+ rmdir(dir) if empty
953
+ end
954
+ rescue Errno::ENOTEMPTY
955
+ $stderr.puts "may not be empty -- #{dir}" if verbose?
956
+ end
957
+ end
958
+
959
+ rm_f(MANIFEST)
960
+ end
961
+
962
+ #
963
+ # TASK clean
964
+ #
965
+
966
+ def exec_clean
967
+ exec_task_traverse 'clean'
968
+ rm_f ConfigTable::CONFIGFILE
969
+ #rm_f MANIFEST # only on clobber!
970
+ end
971
+
972
+ alias clean_dir_bin noop
973
+ alias clean_dir_lib noop
974
+ alias clean_dir_data noop
975
+ alias clean_dir_conf noop
976
+ alias clean_dir_man noop
977
+ alias clean_dir_doc noop
978
+
979
+ def clean_dir_ext(rel)
980
+ return unless extdir?(curr_srcdir())
981
+ make 'clean' if File.file?('Makefile')
982
+ end
983
+
984
+ #
985
+ # TASK distclean
986
+ #
987
+
988
+ def exec_distclean
989
+ exec_task_traverse 'distclean'
990
+ rm_f ConfigTable::CONFIGFILE
991
+ rm_f MANIFEST
992
+ end
993
+
994
+ alias distclean_dir_bin noop
995
+ alias distclean_dir_lib noop
996
+
997
+ def distclean_dir_ext(rel)
998
+ return unless extdir?(curr_srcdir())
999
+ make 'distclean' if File.file?('Makefile')
1000
+ end
1001
+
1002
+ alias distclean_dir_data noop
1003
+ alias distclean_dir_conf noop
1004
+ alias distclean_dir_man noop
1005
+
1006
+ def distclean_dir_doc(rel)
1007
+ if GENERATE_RDOCS
1008
+ rm_rf('rdoc') if File.directory?('rdoc')
1009
+ end
1010
+ end
1011
+
1012
+ #
1013
+ # Traversing
1014
+ #
1015
+
1016
+ def exec_task_traverse(task)
1017
+ run_hook "pre-#{task}"
1018
+ FILETYPES.each do |type|
1019
+ if type == 'ext' and config.without_ext == 'yes'
1020
+ $stderr.puts 'skipping ext/* by user option' if verbose?
1021
+ next
1022
+ end
1023
+ traverse task, type, "#{task}_dir_#{type}"
1024
+ end
1025
+ run_hook "post-#{task}"
1026
+ end
1027
+
1028
+ def traverse(task, rel, mid)
1029
+ dive_into(rel) {
1030
+ run_hook "pre-#{task}"
1031
+ __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
1032
+ directories_of(curr_srcdir()).each do |d|
1033
+ traverse task, "#{rel}/#{d}", mid
1034
+ end
1035
+ run_hook "post-#{task}"
1036
+ }
1037
+ end
1038
+
1039
+ def dive_into(rel)
1040
+ return unless File.dir?("#{@srcdir}/#{rel}")
1041
+
1042
+ dir = File.basename(rel)
1043
+ Dir.mkdir dir unless File.dir?(dir)
1044
+ prevdir = Dir.pwd
1045
+ Dir.chdir dir
1046
+ $stderr.puts '---> ' + rel if verbose?
1047
+ @currdir = rel
1048
+ yield
1049
+ Dir.chdir prevdir
1050
+ $stderr.puts '<--- ' + rel if verbose?
1051
+ @currdir = File.dirname(rel)
1052
+ end
1053
+
1054
+ def run_hook(id)
1055
+ path = [ "#{curr_srcdir()}/#{id}",
1056
+ "#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) }
1057
+ return unless path
1058
+ begin
1059
+ instance_eval File.read(path), path, 1
1060
+ rescue
1061
+ raise if $DEBUG
1062
+ setup_rb_error "hook #{path} failed:\n" + $!.message
1063
+ end
1064
+ end
1065
+
1066
+ # File Operations
1067
+ #
1068
+ # This module requires: #verbose?, #no_harm?
1069
+
1070
+ def binread(fname)
1071
+ File.open(fname, 'rb'){ |f|
1072
+ return f.read
1073
+ }
1074
+ end
1075
+
1076
+ def mkdir_p(dirname, prefix = nil)
1077
+ dirname = prefix + File.expand_path(dirname) if prefix
1078
+ $stderr.puts "mkdir -p #{dirname}" if verbose?
1079
+ return if no_harm?
1080
+
1081
+ # Does not check '/', it's too abnormal.
1082
+ dirs = File.expand_path(dirname).split(%r<(?=/)>)
1083
+ if /\A[a-z]:\z/i =~ dirs[0]
1084
+ disk = dirs.shift
1085
+ dirs[0] = disk + dirs[0]
1086
+ end
1087
+ dirs.each_index do |idx|
1088
+ path = dirs[0..idx].join('')
1089
+ Dir.mkdir path unless File.dir?(path)
1090
+ record_installation(path) # also record directories made
1091
+ end
1092
+ end
1093
+
1094
+ def rm_f(path)
1095
+ $stderr.puts "rm -f #{path}" if verbose?
1096
+ return if no_harm?
1097
+ force_remove_file path
1098
+ end
1099
+
1100
+ def rm_rf(path)
1101
+ $stderr.puts "rm -rf #{path}" if verbose?
1102
+ return if no_harm?
1103
+ remove_tree path
1104
+ end
1105
+
1106
+ def rmdir(path)
1107
+ $stderr.puts "rmdir #{path}" if verbose?
1108
+ return if no_harm?
1109
+ Dir.rmdir path
1110
+ end
1111
+
1112
+ def remove_tree(path)
1113
+ if File.symlink?(path)
1114
+ remove_file path
1115
+ elsif File.dir?(path)
1116
+ remove_tree0 path
1117
+ else
1118
+ force_remove_file path
1119
+ end
1120
+ end
1121
+
1122
+ def remove_tree0(path)
1123
+ Dir.foreach(path) do |ent|
1124
+ next if ent == '.'
1125
+ next if ent == '..'
1126
+ entpath = "#{path}/#{ent}"
1127
+ if File.symlink?(entpath)
1128
+ remove_file entpath
1129
+ elsif File.dir?(entpath)
1130
+ remove_tree0 entpath
1131
+ else
1132
+ force_remove_file entpath
1133
+ end
1134
+ end
1135
+ begin
1136
+ Dir.rmdir path
1137
+ rescue Errno::ENOTEMPTY
1138
+ # directory may not be empty
1139
+ end
1140
+ end
1141
+
1142
+ def move_file(src, dest)
1143
+ force_remove_file dest
1144
+ begin
1145
+ File.rename src, dest
1146
+ rescue
1147
+ File.open(dest, 'wb') {|f|
1148
+ f.write binread(src)
1149
+ }
1150
+ File.chmod File.stat(src).mode, dest
1151
+ File.unlink src
1152
+ end
1153
+ end
1154
+
1155
+ def force_remove_file(path)
1156
+ begin
1157
+ remove_file path
1158
+ rescue
1159
+ end
1160
+ end
1161
+
1162
+ def remove_file(path)
1163
+ File.chmod 0777, path
1164
+ File.unlink path
1165
+ end
1166
+
1167
+ def install(from, dest, mode, prefix = nil)
1168
+ $stderr.puts "install #{from} #{dest}" if verbose?
1169
+ return if no_harm?
1170
+
1171
+ realdest = prefix ? prefix + File.expand_path(dest) : dest
1172
+ realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)
1173
+ str = binread(from)
1174
+ if diff?(str, realdest)
1175
+ verbose_off {
1176
+ rm_f realdest if File.exist?(realdest)
1177
+ }
1178
+ File.open(realdest, 'wb') {|f|
1179
+ f.write str
1180
+ }
1181
+ File.chmod mode, realdest
1182
+
1183
+ if prefix
1184
+ path = realdest.sub(prefix, '')
1185
+ else
1186
+ path = realdest
1187
+ end
1188
+
1189
+ record_installation(path)
1190
+ end
1191
+ end
1192
+
1193
+ def record_installation(path)
1194
+ File.open("#{objdir_root()}/#{MANIFEST}", 'a') do |f|
1195
+ f.puts(path)
1196
+ end
1197
+ end
1198
+
1199
+ def diff?(new_content, path)
1200
+ return true unless File.exist?(path)
1201
+ new_content != binread(path)
1202
+ end
1203
+
1204
+ def command(*args)
1205
+ $stderr.puts args.join(' ') if verbose?
1206
+ system(*args) or raise RuntimeError,
1207
+ "system(#{args.map{|a| a.inspect }.join(' ')}) failed"
1208
+ end
1209
+
1210
+ def ruby(*args)
1211
+ command config.rubyprog, *args
1212
+ end
1213
+
1214
+ def make(task = nil)
1215
+ command(*[config.makeprog, task].compact)
1216
+ end
1217
+
1218
+ def extdir?(dir)
1219
+ File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb")
1220
+ end
1221
+
1222
+ def files_of(dir)
1223
+ Dir.open(dir) {|d|
1224
+ return d.select {|ent| File.file?("#{dir}/#{ent}") }
1225
+ }
1226
+ end
1227
+
1228
+ DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn )
1229
+
1230
+ def directories_of(dir)
1231
+ Dir.open(dir) {|d|
1232
+ return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT
1233
+ }
1234
+ end
1235
+
1236
+ #
1237
+ # Hook Script API
1238
+ #
1239
+ # These require: #srcdir_root, #objdir_root, #relpath
1240
+ #
1241
+
1242
+ #
1243
+ def get_config(key)
1244
+ config.__send__(key)
1245
+ end
1246
+
1247
+ # obsolete: use metaconfig to change configuration
1248
+ # TODO: what to do with?
1249
+ def set_config(key, val)
1250
+ config[key] = val
1251
+ end
1252
+
1253
+ #
1254
+ # srcdir/objdir (works only in the package directory)
1255
+ #
1256
+
1257
+ def curr_srcdir
1258
+ "#{srcdir_root()}/#{relpath()}"
1259
+ end
1260
+
1261
+ def curr_objdir
1262
+ "#{objdir_root()}/#{relpath()}"
1263
+ end
1264
+
1265
+ def srcfile(path)
1266
+ "#{curr_srcdir()}/#{path}"
1267
+ end
1268
+
1269
+ def srcexist?(path)
1270
+ File.exist?(srcfile(path))
1271
+ end
1272
+
1273
+ def srcdirectory?(path)
1274
+ File.dir?(srcfile(path))
1275
+ end
1276
+
1277
+ def srcfile?(path)
1278
+ File.file?(srcfile(path))
1279
+ end
1280
+
1281
+ def srcentries(path = '.')
1282
+ Dir.open("#{curr_srcdir()}/#{path}") {|d|
1283
+ return d.to_a - %w(. ..)
1284
+ }
1285
+ end
1286
+
1287
+ def srcfiles(path = '.')
1288
+ srcentries(path).select {|fname|
1289
+ File.file?(File.join(curr_srcdir(), path, fname))
1290
+ }
1291
+ end
1292
+
1293
+ def srcdirectories(path = '.')
1294
+ srcentries(path).select {|fname|
1295
+ File.dir?(File.join(curr_srcdir(), path, fname))
1296
+ }
1297
+ end
1298
+
1299
+ end
1300
+
1301
+ # CLI runner.
1302
+ def self.run_cli
1303
+ installer = Setup::Installer.new
1304
+
1305
+ task = ARGV.find{ |a| a !~ /^[-]/ }
1306
+ task = 'all' unless task
1307
+
1308
+ unless Setup::Installer::TASKS.include?(task)
1309
+ $stderr.puts "Not a valid task -- #{task}"
1310
+ exit 1
1311
+ end
1312
+
1313
+ opts = OptionParser.new
1314
+
1315
+ opts.banner = "Usage: #{File.basename($0)} [task] [options]"
1316
+
1317
+ if task == 'config' or task == 'all'
1318
+ opts.separator ""
1319
+ opts.separator "Config options:"
1320
+ Setup::ConfigTable::DESCRIPTIONS.each do |name, type, desc|
1321
+ opts.on("--#{name} #{type.to_s.upcase}", desc) do |val|
1322
+ ENV[name.to_s] = val.to_s
1323
+ end
1324
+ end
1325
+ end
1326
+
1327
+ if task == 'install'
1328
+ opts.separator ""
1329
+ opts.separator "Install options:"
1330
+
1331
+ opts.on("--prefix PATH", "Installation prefix") do |val|
1332
+ installer.install_prefix = val
1333
+ end
1334
+ end
1335
+
1336
+ if task == 'test'
1337
+ opts.separator ""
1338
+ opts.separator "Install options:"
1339
+
1340
+ opts.on("--runner TYPE", "Test runner (auto|console|gtk|gtk2|tk)") do |val|
1341
+ installer.config.testrunner = val
1342
+ end
1343
+ end
1344
+
1345
+ # common options
1346
+ opts.separator ""
1347
+ opts.separator "General options:"
1348
+
1349
+ opts.on("-q", "--quiet", "Silence output") do |val|
1350
+ installer.quiet = val
1351
+ end
1352
+
1353
+ opts.on("--verbose", "Provide verbose output") do |val|
1354
+ installer.verbose = val
1355
+ end
1356
+
1357
+ opts.on("-n", "--no-write", "Do not write to disk") do |val|
1358
+ installer.no_harm = !val
1359
+ end
1360
+
1361
+ opts.on("--dryrun", "Same as --no-write") do |val|
1362
+ installer.no_harm = val
1363
+ end
1364
+
1365
+ # common options
1366
+ opts.separator ""
1367
+ opts.separator "Inform options:"
1368
+
1369
+ # Tail options (eg. commands in option form)
1370
+ opts.on_tail("-h", "--help", "display this help information") do
1371
+ puts Setup.help
1372
+ exit
1373
+ end
1374
+
1375
+ opts.on_tail("--version", "Show version") do
1376
+ puts File.basename($0) + ' v' + Setup::Version.join('.')
1377
+ exit
1378
+ end
1379
+
1380
+ opts.on_tail("--copyright", "Show copyright") do
1381
+ puts Setup::Copyright
1382
+ exit
1383
+ end
1384
+
1385
+ begin
1386
+ opts.parse!(ARGV)
1387
+ rescue OptionParser::InvalidOption
1388
+ $stderr.puts $!.to_s.capitalize
1389
+ exit 1
1390
+ end
1391
+
1392
+ begin
1393
+ installer.__send__("exec_#{task}")
1394
+ rescue SetupError
1395
+ raise if $DEBUG
1396
+ $stderr.puts $!.message
1397
+ $stderr.puts "Try 'ruby #{$0} --help' for detailed usage."
1398
+ exit 1
1399
+ end
1400
+ end
1401
+
1402
+ # Generate help text.
1403
+ def self.help
1404
+ fmt = " " * 10 + "%-10s %s"
1405
+ commands = Installer::TASK_DESCRIPTIONS.collect do |k,d|
1406
+ (fmt % ["#{k}", d])
1407
+ end.join("\n").strip
1408
+
1409
+ fmt = " " * 13 + "%-20s %s"
1410
+ configs = ConfigTable::DESCRIPTIONS.collect do |k,t,d|
1411
+ (fmt % ["--#{k}", d])
1412
+ end.join("\n").strip
1413
+
1414
+ text = <<-END
1415
+ USAGE: #{File.basename($0)} [command] [options]
1416
+
1417
+ Commands:
1418
+ #{commands}
1419
+
1420
+ Options for CONFIG:
1421
+ #{configs}
1422
+
1423
+ Options for INSTALL:
1424
+ --prefix Set the install prefix
1425
+
1426
+ Options in common:
1427
+ -q --quiet Silence output
1428
+ --verbose Provide verbose output
1429
+ -n --no-write Do not write to disk
1430
+
1431
+ END
1432
+
1433
+ text.gsub(/^ \ \ \ \ \ /, '')
1434
+ end
1435
+
1436
+ #
1437
+ def self.run_rake
1438
+ installer = Setup::Installer.new
1439
+ installer.rake_define
1440
+ end
1441
+
1442
+ end
1443
+
1444
+ #
1445
+ # Ruby Extensions
1446
+ #
1447
+
1448
+ unless File.respond_to?(:read) # Ruby 1.6 and less
1449
+ def File.read(fname)
1450
+ open(fname) {|f|
1451
+ return f.read
1452
+ }
1453
+ end
1454
+ end
1455
+
1456
+ unless Errno.const_defined?(:ENOTEMPTY) # Windows?
1457
+ module Errno
1458
+ class ENOTEMPTY
1459
+ # We do not raise this exception, implementation is not needed.
1460
+ end
1461
+ end
1462
+ end
1463
+
1464
+ # for corrupted Windows' stat(2)
1465
+ def File.dir?(path)
1466
+ File.directory?((path[-1,1] == '/') ? path : path + '/')
1467
+ end
1468
+
1469
+ #
1470
+ # Runners
1471
+ #
1472
+
1473
+ if $0 == __FILE__
1474
+
1475
+ Setup.run_cli
1476
+
1477
+ elsif defined?(Rake)
1478
+
1479
+ Setup.run_rake
1480
+
1481
+ end
1482
+