io-reactor 0.05 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/utils.rb DELETED
@@ -1,484 +0,0 @@
1
- #
2
- # Install/distribution utility functions
3
- # $Id: utils.rb,v 1.5 2003/08/04 23:52:07 deveiant Exp $
4
- #
5
- # Copyright (c) 2001-2003, The FaerieMUD Consortium.
6
- #
7
- # This is free software. You may use, modify, and/or redistribute this
8
- # software under the terms of the Perl Artistic License. (See
9
- # http://language.perl.com/misc/Artistic.html)
10
- #
11
-
12
-
13
- BEGIN {
14
- begin
15
- require 'readline'
16
- include Readline
17
- rescue LoadError => e
18
- $stderr.puts "Faking readline..."
19
- def readline( prompt )
20
- $stderr.print prompt.chomp
21
- return $stdin.gets.chomp
22
- end
23
- end
24
-
25
- begin
26
- require 'yaml'
27
- $yaml = true
28
- rescue LoadError => e
29
- $stderr.puts "No YAML; try() will use .inspect instead."
30
- $yaml = false
31
- end
32
- }
33
-
34
- module UtilityFunctions
35
-
36
- # The list of regexen that eliminate files from the MANIFEST
37
- ANTIMANIFEST = [
38
- /makedist\.rb/,
39
- /\bCVS\b/,
40
- /~$/,
41
- /^#/,
42
- %r{docs/html},
43
- %r{docs/man},
44
- /^TEMPLATE/,
45
- /\.cvsignore/,
46
- /\.s?o$/
47
- ]
48
-
49
- # Set some ANSI escape code constants (Shamelessly stolen from Perl's
50
- # Term::ANSIColor by Russ Allbery <rra@stanford.edu> and Zenin <zenin@best.com>
51
- AnsiAttributes = {
52
- 'clear' => 0,
53
- 'reset' => 0,
54
- 'bold' => 1,
55
- 'dark' => 2,
56
- 'underline' => 4,
57
- 'underscore' => 4,
58
- 'blink' => 5,
59
- 'reverse' => 7,
60
- 'concealed' => 8,
61
-
62
- 'black' => 30, 'on_black' => 40,
63
- 'red' => 31, 'on_red' => 41,
64
- 'green' => 32, 'on_green' => 42,
65
- 'yellow' => 33, 'on_yellow' => 43,
66
- 'blue' => 34, 'on_blue' => 44,
67
- 'magenta' => 35, 'on_magenta' => 45,
68
- 'cyan' => 36, 'on_cyan' => 46,
69
- 'white' => 37, 'on_white' => 47
70
- }
71
-
72
- ErasePreviousLine = "\033[A\033[K"
73
-
74
-
75
- ###############
76
- module_function
77
- ###############
78
-
79
- # Create a string that contains the ANSI codes specified and return it
80
- def ansiCode( *attributes )
81
- return '' unless /(?:vt10[03]|xterm(?:-color)?|linux)/i =~ ENV['TERM']
82
- attr = attributes.collect {|a| AnsiAttributes[a] ? AnsiAttributes[a] : nil}.compact.join(';')
83
- if attr.empty?
84
- return ''
85
- else
86
- return "\e[%sm" % attr
87
- end
88
- end
89
-
90
- # Test for the presence of the specified <tt>library</tt>, and output a
91
- # message describing the test using <tt>nicename</tt>. If <tt>nicename</tt>
92
- # is <tt>nil</tt>, the value in <tt>library</tt> is used to build a default.
93
- def testForLibrary( library, nicename=nil )
94
- nicename ||= library
95
- message( "Testing for the #{nicename} library..." )
96
- if $:.detect {|dir| File.exists?(File.join(dir,"#{library}.rb")) || File.exists?(File.join(dir,"#{library}.so"))}
97
- message( "found.\n" )
98
- return true
99
- else
100
- message( "not found.\n" )
101
- return false
102
- end
103
- end
104
-
105
- # Test for the presence of the specified <tt>library</tt>, and output a
106
- # message describing the problem using <tt>nicename</tt>. If
107
- # <tt>nicename</tt> is <tt>nil</tt>, the value in <tt>library</tt> is used
108
- # to build a default. If <tt>raaUrl</tt> and/or <tt>downloadUrl</tt> are
109
- # specified, they are also use to build a message describing how to find the
110
- # required library. If <tt>fatal</tt> is <tt>true</tt>, a missing library
111
- # will cause the program to abort.
112
- def testForRequiredLibrary( library, nicename=nil, raaUrl=nil, downloadUrl=nil, fatal=true )
113
- nicename ||= library
114
- unless testForLibrary( library, nicename )
115
- msgs = [ "You are missing the required #{nicename} library.\n" ]
116
- msgs << "RAA: #{raaUrl}\n" if raaUrl
117
- msgs << "Download: #{downloadUrl}\n" if downloadUrl
118
- if fatal
119
- abort msgs.join('')
120
- else
121
- errorMessage msgs.join('')
122
- end
123
- end
124
- return true
125
- end
126
-
127
- ### Output <tt>msg</tt> as a ANSI-colored program/section header (white on
128
- ### blue).
129
- def header( msg )
130
- msg.chomp!
131
- $stderr.puts ansiCode( 'bold', 'white', 'on_blue' ) + msg + ansiCode( 'reset' )
132
- $stderr.flush
133
- end
134
-
135
- ### Output <tt>msg</tt> to STDERR and flush it.
136
- def message( msg )
137
- $stderr.print ansiCode( 'cyan' ) + msg + ansiCode( 'reset' )
138
- $stderr.flush
139
- end
140
-
141
- ### Output the specified <tt>msg</tt> as an ANSI-colored error message
142
- ### (white on red).
143
- def errorMessage( msg )
144
- message ansiCode( 'bold', 'white', 'on_red' ) + msg + ansiCode( 'reset' )
145
- end
146
-
147
- ### Output the specified <tt>msg</tt> as an ANSI-colored debugging message
148
- ### (yellow on blue).
149
- def debugMsg( msg )
150
- return unless $DEBUG
151
- msg.chomp!
152
- $stderr.puts ansiCode( 'bold', 'yellow', 'on_blue' ) + ">>> #{msg}" + ansiCode( 'reset' )
153
- $stderr.flush
154
- end
155
-
156
- ### Erase the previous line (if supported by your terminal) and output the
157
- ### specified <tt>msg</tt> instead.
158
- def replaceMessage( msg )
159
- print ErasePreviousLine
160
- message( msg )
161
- end
162
-
163
- ### Output a divider made up of <tt>length</tt> hyphen characters.
164
- def divider( length=75 )
165
- puts "\r" + ("-" * length )
166
- end
167
- alias :writeLine :divider
168
-
169
- ### Output the specified <tt>msg</tt> colored in ANSI red and exit with a
170
- ### status of 1.
171
- def abort( msg )
172
- print ansiCode( 'bold', 'red' ) + "Aborted: " + msg.chomp + ansiCode( 'reset' ) + "\n\n"
173
- Kernel.exit!( 1 )
174
- end
175
-
176
- ### Output the specified <tt>promptString</tt> as a prompt (in green) and
177
- ### return the user's input with leading and trailing spaces removed.
178
- def prompt( promptString )
179
- promptString.chomp!
180
- return readline( ansiCode('bold', 'green') + "#{promptString}: " + ansiCode('reset') ).strip
181
- end
182
-
183
- ### Prompt the user with the given <tt>promptString</tt> via #prompt,
184
- ### substituting the given <tt>default</tt> if the user doesn't input
185
- ### anything.
186
- def promptWithDefault( promptString, default )
187
- response = prompt( "%s [%s]" % [ promptString, default ] )
188
- if response.empty?
189
- return default
190
- else
191
- return response
192
- end
193
- end
194
-
195
- ### Search for the program specified by the given <tt>progname</tt> in the
196
- ### user's <tt>PATH</tt>, and return the full path to it, or <tt>nil</tt> if
197
- ### no such program is in the path.
198
- def findProgram( progname )
199
- ENV['PATH'].split(File::PATH_SEPARATOR).each {|d|
200
- file = File.join( d, progname )
201
- return file if File.executable?( file )
202
- }
203
- return nil
204
- end
205
-
206
- ### Using the CVS log for the given <tt>file</tt> attempt to guess what the
207
- ### next release version might be. This only works if releases are tagged
208
- ### with tags like 'RELEASE_x_y'.
209
- def extractNextVersionFromTags( file )
210
- message "Attempting to extract next release version from CVS tags for #{file}...\n"
211
- raise RuntimeError, "No such file '#{file}'" unless File.exists?( file )
212
- cvsPath = findProgram( 'cvs' ) or
213
- raise RuntimeError, "Cannot find the 'cvs' program. Aborting."
214
-
215
- output = %x{#{cvsPath} log #{file}}
216
- release = [ 0, 0 ]
217
- output.scan( /RELEASE_(\d+)_(\d+)/ ) {|match|
218
- if $1.to_i > release[0] || $2.to_i > release[1]
219
- release = [ $1.to_i, $2.to_i ]
220
- replaceMessage( "Found %d.%02d...\n" % release )
221
- end
222
- }
223
-
224
- if release[1] >= 99
225
- release[0] += 1
226
- release[1] = 1
227
- else
228
- release[1] += 1
229
- end
230
-
231
- return "%d.%02d" % release
232
- end
233
-
234
- ### Extract the project name (CVS Repository name) for the given directory.
235
- def extractProjectName
236
- File.open( "CVS/Repository", "r").readline.chomp
237
- end
238
-
239
- ### Read the specified <tt>manifestFile</tt>, which is a text file
240
- ### describing which files to package up for a distribution. The manifest
241
- ### should consist of one or more lines, each containing one filename or
242
- ### shell glob pattern.
243
- def readManifest( manifestFile="MANIFEST" )
244
- message "Building manifest..."
245
- raise "Missing #{manifestFile}, please remake it" unless File.exists? manifestFile
246
-
247
- manifest = IO::readlines( manifestFile ).collect {|line|
248
- line.chomp
249
- }.select {|line|
250
- line !~ /^(\s*(#.*)?)?$/
251
- }
252
-
253
- filelist = []
254
- for pat in manifest
255
- $stderr.puts "Adding files that match '#{pat}' to the file list" if $VERBOSE
256
- filelist |= Dir.glob( pat ).find_all {|f| FileTest.file?(f)}
257
- end
258
-
259
- message "found #{filelist.length} files.\n"
260
- return filelist
261
- end
262
-
263
- ### Given a <tt>filelist</tt> like that returned by #readManifest, remove
264
- ### the entries therein which match the Regexp objects in the given
265
- ### <tt>antimanifest</tt> and return the resultant Array.
266
- def vetManifest( filelist, antimanifest=ANITMANIFEST )
267
- origLength = filelist.length
268
- message "Vetting manifest..."
269
-
270
- for regex in antimanifest
271
- if $VERBOSE
272
- message "\n\tPattern /#{regex.source}/ removed: " +
273
- filelist.find_all {|file| regex.match(file)}.join(', ')
274
- end
275
- filelist.delete_if {|file| regex.match(file)}
276
- end
277
-
278
- message "removed #{origLength - filelist.length} files from the list.\n"
279
- return filelist
280
- end
281
-
282
- ### Combine a call to #readManifest with one to #vetManifest.
283
- def getVettedManifest( manifestFile="MANIFEST", antimanifest=ANTIMANIFEST )
284
- vetManifest( readManifest(manifestFile), antimanifest )
285
- end
286
-
287
- ### Given a documentation <tt>catalogFile</tt>, extract the title, if
288
- ### available, and return it. Otherwise generate a title from the name of
289
- ### the CVS module.
290
- def findRdocTitle( catalogFile="docs/CATALOG" )
291
-
292
- # Try extracting it from the CATALOG file from a line that looks like:
293
- # Title: Foo Bar Module
294
- title = findCatalogKeyword( 'title', catalogFile )
295
-
296
- # If that doesn't work for some reason, try grabbing the name of the CVS
297
- # repository the directory belongs to.
298
- if title.nil? && File::directory?( "CVS" ) &&
299
- File::exists?( "CVS/Repository" )
300
- title = File::read( "CVS/Repository" ).chomp
301
- end
302
-
303
- # As a last resort, use the name of the project directory
304
- if title.nil?
305
- distdir = File::dirname( __FILE__ )
306
- distdir = File::dirname( distdir ) if /docs$/ =~ distdir
307
- title = File::basename( distdir )
308
- end
309
-
310
- return title
311
- end
312
-
313
- ### Given a documentation <tt>catalogFile</tt>, extract the name of the file
314
- ### to use as the initally displayed page. If extraction fails, the
315
- ### +default+ will be used if it exists. Returns +nil+ if there is no main
316
- ### file to be found.
317
- def findRdocMain( catalogFile="docs/CATALOG", default="README" )
318
-
319
- # Try extracting it from the CATALOG file from a line that looks like:
320
- # Main: Foo Bar Module
321
- main = findCatalogKeyword( 'main', catalogFile )
322
-
323
- # Try to make some educated guesses if that doesn't work
324
- if main.nil?
325
- basedir = File::dirname( __FILE__ )
326
- basedir = File::dirname( basedir ) if /docs$/ =~ basedir
327
-
328
- if File::exists?( File::join(basedir, default) )
329
- main = default
330
- end
331
- end
332
-
333
- return main
334
- end
335
-
336
-
337
- ### Given a documentation <tt>catalogFile</tt>, extract an upload URL for
338
- ### RDoc.
339
- def findRdocUpload( catalogFile="docs/CATALOG" )
340
- findCatalogKeyword( 'upload', catalogFile )
341
- end
342
-
343
-
344
- ### Given a documentation <tt>catalogFile</tt>, extract a CVS web frontend
345
- ### URL for RDoc.
346
- def findRdocCvsURL( catalogFile="docs/CATALOG" )
347
- findCatalogKeyword( 'webcvs', catalogFile )
348
- end
349
-
350
-
351
- ### Given a documentation <tt>catalogFile</tt>, try extracting the given
352
- ### +keyword+'s value from it. Keywords are lines that look like:
353
- ### # <keyword>: <value>
354
- ### Returns +nil+ if the catalog file was unreadable or didn't contain the
355
- ### specified +keyword+.
356
- def findCatalogKeyword( keyword, catalogFile="docs/CATALOG" )
357
- val = nil
358
-
359
- if File::exists? catalogFile
360
- message "Extracting '#{keyword}' from CATALOG file (%s).\n" % catalogFile
361
- File::foreach( catalogFile ) {|line|
362
- debugMsg( "Examining line #{line.inspect}..." )
363
- val = $1.strip and break if /^#\s*#{keyword}:\s*(.*)$/i =~ line
364
- }
365
- end
366
-
367
- return val
368
- end
369
-
370
-
371
- ### Given a documentation <tt>catalogFile</tt>, which is in the same format
372
- ### as that described by #readManifest, read and expand it, and then return
373
- ### a list of those files which appear to have RDoc documentation in
374
- ### them. If <tt>catalogFile</tt> is nil or does not exist, the MANIFEST
375
- ### file is used instead.
376
- def findRdocableFiles( catalogFile="docs/CATALOG" )
377
- startlist = []
378
- if File.exists? catalogFile
379
- message "Using CATALOG file (%s).\n" % catalogFile
380
- startlist = getVettedManifest( catalogFile )
381
- else
382
- message "Using default MANIFEST\n"
383
- startlist = getVettedManifest()
384
- end
385
-
386
- message "Looking for RDoc comments in:\n" if $VERBOSE
387
- startlist.select {|fn|
388
- message " #{fn}: " if $VERBOSE
389
- found = false
390
- File::open( fn, "r" ) {|fh|
391
- fh.each {|line|
392
- if line =~ /^(\s*#)?\s*=/ || line =~ /:\w+:/ || line =~ %r{/\*}
393
- found = true
394
- break
395
- end
396
- }
397
- }
398
-
399
- message( (found ? "yes" : "no") + "\n" ) if $VERBOSE
400
- found
401
- }
402
- end
403
-
404
- ### Open a file and filter each of its lines through the given block a
405
- ### <tt>line</tt> at a time. The return value of the block is used as the
406
- ### new line, or omitted if the block returns <tt>nil</tt> or
407
- ### <tt>false</tt>.
408
- def editInPlace( file ) # :yields: line
409
- raise "No block specified for editing operation" unless block_given?
410
-
411
- tempName = "#{file}.#{$$}"
412
- File::open( tempName, File::RDWR|File::CREAT, 0600 ) {|tempfile|
413
- File::unlink( tempName )
414
- File::open( file, File::RDONLY ) {|fh|
415
- fh.each {|line|
416
- newline = yield( line ) or next
417
- tempfile.print( newline )
418
- }
419
- }
420
-
421
- tempfile.seek(0)
422
-
423
- File::open( file, File::TRUNC|File::WRONLY, 0644 ) {|newfile|
424
- newfile.print( tempfile.read )
425
- }
426
- }
427
- end
428
-
429
- ### Execute the specified shell <tt>command</tt>, read the results, and
430
- ### return them. Like a %x{} that returns an Array instead of a String.
431
- def shellCommand( *command )
432
- raise "Empty command" if command.empty?
433
-
434
- cmdpipe = IO::popen( command.join(' '), 'r' )
435
- return cmdpipe.readlines
436
- end
437
-
438
- ### Execute a block with $VERBOSE set to +false+, restoring it to its
439
- ### previous value before returning.
440
- def verboseOff
441
- raise LocalJumpError, "No block given" unless block_given?
442
-
443
- thrcrit = Thread.critical
444
- oldverbose = $VERBOSE
445
- begin
446
- Thread.critical = true
447
- $VERBOSE = false
448
- yield
449
- ensure
450
- $VERBOSE = oldverbose
451
- Thread.critical = false
452
- end
453
- end
454
-
455
-
456
- ### Try the specified code block, printing the given
457
- def try( msg, bind=nil )
458
- result = nil
459
- message "Trying #{msg}..."
460
-
461
- begin
462
- rval = nil
463
- if block_given?
464
- rval = yield
465
- else
466
- file, line = caller(1)[0].split(/:/,2)
467
- rval = eval( msg, bind, file, line.to_i )
468
- end
469
-
470
- if $yaml
471
- result = rval.to_yaml
472
- else
473
- result = rval.inspect
474
- end
475
- rescue Exception => err
476
- nicetrace = err.backtrace.delete_if {|frame|
477
- /in `(try|eval)'/ =~ frame
478
- }.join("\n\t")
479
- result = err.message + "\n\t" + nicetrace
480
- ensure
481
- puts result
482
- end
483
- end
484
- end