analogger 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/INSTALL ADDED
@@ -0,0 +1,60 @@
1
+ Swiftcore Analogger 0.5
2
+
3
+ Homepage:: http://analogger.swiftcore.org
4
+ Copyright:: (C) 2007 by Kirk Haines. All Rights Reserved.
5
+ Email:: wyhaines@gmail.com
6
+
7
+
8
+ Installation
9
+ ------------
10
+
11
+ To install analogger:
12
+
13
+ ruby setup.rb
14
+
15
+ The analogger executable will be installed into the ruby installation's
16
+ bindir, along with server and client libraries into the site_lib. The
17
+ rdoc documentation will also be generated.
18
+
19
+ ruby setup.rb --help
20
+
21
+ to se a full list of options.
22
+
23
+
24
+ Quickstart
25
+ ----------
26
+
27
+ To start an Analogger instance, first create a configuration file:
28
+
29
+ port: 6766
30
+ host: 127.0.0.1
31
+ default_log: /var/log/weblogs/default
32
+ daemonize: true
33
+ syncinterval: 60
34
+ logs:
35
+ - service: bigapp
36
+ logfile: /var/log/bigapp
37
+ cull: true
38
+ - service:
39
+ - smallapp1
40
+ - smallapp2
41
+ logfile: /var/log/smallapps
42
+ cull: true
43
+ - service: newsletter_sender
44
+ logfile: /var/log/newsletter.log
45
+ cull: false
46
+
47
+
48
+ Then start the analogger:
49
+
50
+ /usr/bin/env analogger -c config_file
51
+
52
+
53
+ To use the client library to connect to an Analogger instance and send
54
+ logging messages to it:
55
+
56
+ require 'swiftcore/Analogger/Client'
57
+
58
+ logger = Swiftcdore::Analogger::Client.new('smallapp1','127.0.0.1','6766')
59
+
60
+ logger.log('info','This is a log message.')
data/README ADDED
@@ -0,0 +1,19 @@
1
+ Swiftcore Analogger 0.5
2
+
3
+ Homepage:: http://analogger.swiftcore.org
4
+ Copyright:: (C) 2007 by Kirk Haines. All Rights Reserved.
5
+ Email:: wyhaines@gmail.com
6
+ License:: Ruby's License
7
+
8
+ The Swiftcore Analogger implements a fast asynchronous logging system
9
+ for Ruby programs as well as client library for sending logging messages
10
+ to the Analogger process.
11
+
12
+ Analogger will accept logs from multiple sources and can have multiple
13
+ logging destinations. Currently, logging to a file, to STDOUT, or to
14
+ STDERR is supported. A future revision may support logging to a
15
+ database destination, as well.
16
+
17
+ Analogger depends on EventMachine (http://rubyforge.org/projects/eventmachine)
18
+ to provide the framework for the network communications, though EM is
19
+ not used for the client library.
@@ -0,0 +1,41 @@
1
+ #####
2
+ # Swiftcore Analogger
3
+ # http://analogger.swiftcore.org
4
+ # Copyright 2007 Kirk Haines
5
+ #
6
+ # Licensed under the Ruby License. See the README for details.
7
+ #
8
+ #####
9
+
10
+ spec = Gem::Specification.new do |s|
11
+ s.name = 'analogger'
12
+ s.author = %q(Kirk Haines)
13
+ s.email = %q(wyhaines@gmail.com)
14
+ s.version = '0.5.0'
15
+ s.summary = %q(A fast asynchronous logging service and client for Ruby.)
16
+ s.platform = Gem::Platform::RUBY
17
+
18
+ s.has_rdoc = true
19
+ s.rdoc_options = %w(--title Swiftcore::Analogger --main README --line-numbers)
20
+ s.extra_rdoc_files = %w(README)
21
+
22
+ s.files = Dir['**/*']
23
+ s.executables = %w(analogger)
24
+ s.require_paths = %w(src)
25
+
26
+ s.requirements << "Eventmachine 0.7.0 or higher."
27
+ s.add_dependency('eventmachine')
28
+ s.test_files = ['test/TC_Analogger.rb']
29
+
30
+ s.rubyforge_project = %q(analogger)
31
+ s.homepage = %q(http://analogger.swiftcore.org/)
32
+ description = []
33
+ File.open("README") do |file|
34
+ file.each do |line|
35
+ line.chomp!
36
+ break if line.empty?
37
+ description << "#{line.gsub(/\[\d\]/, '')}"
38
+ end
39
+ end
40
+ s.description = description[1..-1].join(" ")
41
+ end
@@ -0,0 +1,94 @@
1
+ #!ruby
2
+
3
+ require 'yaml'
4
+ require 'optparse'
5
+ begin
6
+ load_attempted ||= false
7
+ require 'swiftcore/Analogger'
8
+ rescue LoadError => e
9
+ unless load_attempted
10
+ load_attempted = true
11
+ require 'rubygems'
12
+ retry
13
+ end
14
+ raise e
15
+ end
16
+
17
+ #####
18
+ #
19
+ # Swiftcore Analogger
20
+ #
21
+ # The Swiftcore Analogger is an asyncronous logging service intended to
22
+ # provide a fast, flexible, centralized logging service for client
23
+ # applications.
24
+ #
25
+ # Clients connect using and instance of the Swiftcore::Analogger::Client
26
+ # class, and can then deliver logging messages to the service. The
27
+ # Analogger is configured using a file to define mappings of service
28
+ # labels to logging destinations, accepted severity levels, and
29
+ # and whether to cull repeated messages or not.
30
+ #
31
+ #####
32
+
33
+ module Swiftcore
34
+ class AnaloggerExec
35
+ def self.parse_options(config = {})
36
+ # Config file to read
37
+ #
38
+ # port: 12345
39
+ # secret: abcdef
40
+ # interval: 1
41
+ # default_log: /var/log/emlogger
42
+ # logs:
43
+ # - service: client1
44
+ # levels: info
45
+ # logfile: /foo/bar.txt
46
+ # cull: true
47
+ #
48
+ OptionParser.new do |opts|
49
+ opts.banner = 'Usage: analogger.rb [options]'
50
+ opts.separator ''
51
+ opts.on('-c','--config CONFFILE',"The configuration file to read.") do |conf|
52
+ config = YAML.load(File.read(conf))
53
+ end
54
+ opts.on('-p','--port [PORT]',Integer,"The port to receive connections on.") do |port|
55
+ config[Swiftcore::Analogger::Cport] = port
56
+ end
57
+ opts.on('-h','--host [HOST]',String,"The host to bind the connection to.") do |host|
58
+ config[Swiftcore::Analogger::Chost] = host
59
+ end
60
+ # opts.on('-r','--controlkey [KEY]',String,"The secret key that authenticates a control session.") do |secret|
61
+ # config[Swiftcore::Analogger::Csecret] = secret
62
+ # end
63
+ opts.on('-k','--key [KEY]',String,"The secret key that authenticates a valid client session.") do |secret|
64
+ config[Swiftcore::Analogger::Ckey] = secret
65
+ end
66
+ opts.on('-i','--interval [INTERVAL]',String,"The interval between queue writes. Defaults to 1 second.") do |interval|
67
+ config[Swiftcore::Analogger::Cinterval] = interval
68
+ end
69
+ opts.on('-s','--syncinterval [INTERVAL]',String,"The interval between queue syncs. Defaults to 60 seconds.") do |interval|
70
+ config[Swiftcore::Analogger::Csyncinterval] = interval
71
+ end
72
+ opts.on('-d','--default [PATH]',String,"The default log destination. Defaults to stdout.") do |default|
73
+ config[Swiftcore::Analogger::Cdefault_log] = default
74
+ end
75
+ opts.on('-x','--daemonize',"Tell the Analogger to daemonize itself.") do
76
+ config[Swiftcore::Analogger::Cdaemonize] = true
77
+ end
78
+ opts.on('-w','--writepid [FILENAME]',"The filename to write a PID file to.") do |pidfile|
79
+ config[Swiftcore::Analogger::Cpidfile] = pidfile || 'analogger.pid'
80
+ end
81
+ end.parse!
82
+ config
83
+ end
84
+
85
+ def self.run
86
+ Swiftcore::Analogger.start(parse_options)
87
+ end
88
+
89
+ end
90
+ end
91
+
92
+ loop do
93
+ catch(:hup) {Swiftcore::AnaloggerExec.run}
94
+ end
@@ -0,0 +1,672 @@
1
+ require 'rbconfig'
2
+ require 'fileutils'
3
+ require 'optparse'
4
+ require 'yaml'
5
+
6
+ class String
7
+ # This patch for win32 paths contributed by James Britt.
8
+ def w32
9
+ if self =~ /^\w:\/\w:/i
10
+ self.gsub(/^\w:\//i, '')
11
+ else
12
+ self
13
+ end
14
+ end
15
+ end
16
+
17
+ module Package
18
+
19
+ class SpecificationError < StandardError; end
20
+ class PackageSpecification_1_0; end
21
+
22
+ SEMANTICS = { "1.0" => PackageSpecification_1_0 }
23
+
24
+ KINDS = [
25
+ :bin, :lib, :ext, :data, :conf, :doc
26
+ ]
27
+
28
+ mapping = { '.' => '\.', '$' => '\$', '#' => '\#', '*' => '.*' }
29
+ ignore_files = %w[core RCSLOG tags TAGS .make.state .nse_depinfo .hg
30
+ #* .#* cvslog.* ,* .del-* *.olb *~ *.old *.bak *.BAK *.orig *.rej _$* *$
31
+ *.org *.in .* ]
32
+
33
+ IGNORE_FILES = ignore_files.map do |x|
34
+ Regexp.new('\A' + x.gsub(/[\.\$\#\*]/){|c| mapping[c]} + '\z')
35
+ end
36
+
37
+ def self.config(name)
38
+ # XXX use pathname
39
+ prefix = Regexp.quote(Config::CONFIG["prefix"])
40
+ exec_prefix = Regexp.quote(Config::CONFIG["exec_prefix"])
41
+ Config::CONFIG[name].gsub(/\A\/?(#{prefix}|#{exec_prefix})\/?/, '')
42
+ end
43
+
44
+ SITE_DIRS = {
45
+ :bin => config("bindir"),
46
+ :lib => config("sitelibdir"),
47
+ :ext => config("sitearchdir"),
48
+ :data => config("datadir"),
49
+ :conf => config("sysconfdir"),
50
+ :doc => File.join(config("datadir"), "doc"),
51
+ }
52
+
53
+ VENDOR_DIRS = {
54
+ :bin => config("bindir"),
55
+ :lib => config("rubylibdir"),
56
+ :ext => config("archdir"),
57
+ :data => config("datadir"),
58
+ :conf => config("sysconfdir"),
59
+ :doc => File.join(config("datadir"), "doc"),
60
+ }
61
+
62
+ MODES = {
63
+ :bin => 0755,
64
+ :lib => 0644,
65
+ :ext => 0755, # was: 0555,
66
+ :data => 0644,
67
+ :conf => 0644,
68
+ :doc => 0644,
69
+ }
70
+
71
+
72
+ SETUP_OPTIONS = {:parse_cmdline => true, :load_conf => true, :run_tasks => true}
73
+ RUBY_BIN = File.join(::Config::CONFIG['bindir'],::Config::CONFIG['ruby_install_name']) << ::Config::CONFIG['EXEEXT']
74
+
75
+ def self.setup(version, options = {}, &instructions)
76
+ prefixes = dirs = nil
77
+ options = SETUP_OPTIONS.dup.update(options)
78
+
79
+ if options[:load_conf] && File.exist?("config.save")
80
+ config = YAML.load_file "config.save"
81
+ prefixes = config[:prefixes]
82
+ dirs = config[:dirs]
83
+ end
84
+
85
+ pkg = package_specification_with_semantics(version).new(prefixes, dirs)
86
+ pkg.parse_command_line if options[:parse_cmdline]
87
+ pkg.instance_eval(&instructions)
88
+
89
+ pkg.run_tasks if options[:run_tasks]
90
+
91
+ # pkg.install
92
+ pkg
93
+ end
94
+
95
+ def self.package_specification_with_semantics(version)
96
+ #XXX: implement the full x.y(.z)? semantics
97
+ r = SEMANTICS[version]
98
+ raise SpecificationError, "Unknown version #{version}." unless r
99
+ r
100
+ end
101
+
102
+
103
+ module Actions
104
+
105
+ class InstallFile
106
+
107
+ attr_reader :source, :destination, :mode
108
+
109
+ def initialize(source, destination, mode, options)
110
+ @source = source
111
+ @destination = destination
112
+ @mode = mode
113
+ @options = options
114
+ end
115
+
116
+ def install
117
+ dirs = [@options.destdir.to_s, @destination].select {|x| x}
118
+ d = File.expand_path(File.join(dirs)).w32
119
+ FileUtils.install @source, d,
120
+ {:verbose => @options.verbose,
121
+ :noop => @options.noop, :mode => @mode }
122
+ end
123
+
124
+ def hash
125
+ [@source.hash, @destination.hash].hash
126
+ end
127
+
128
+ def eql?(other)
129
+ self.class == other.class &&
130
+ @source == other.source &&
131
+ @destination == other.destination &&
132
+ @mode == other.mode
133
+ end
134
+
135
+ def <=>(other)
136
+ FULL_ORDER[self, other] || self.destination <=> other.destination
137
+ end
138
+ end
139
+
140
+ class MkDir
141
+
142
+ attr_reader :directory
143
+
144
+ def initialize(directory, options)
145
+ @directory = directory
146
+ @options = options
147
+ end
148
+
149
+ def install
150
+ dirs = [@options.destdir.to_s, @directory].select {|x| x}
151
+ d = File.expand_path(File.join(dirs)).w32
152
+ FileUtils.mkdir_p d,
153
+ {:verbose => @options.verbose,
154
+ :noop => @options.noop }
155
+ end
156
+
157
+ def <=>(other)
158
+ FULL_ORDER[self, other] || self.directory <=> other.directory
159
+ end
160
+ end
161
+
162
+ class FixShebang
163
+
164
+ attr_reader :destination
165
+
166
+ def initialize(destination, options)
167
+ @options = options
168
+ @destination = destination
169
+ end
170
+
171
+ def install
172
+ dirs = [@options.destdir.to_s, @destination].select {|x| x}
173
+ d = File.expand_path(File.join(dirs)).w32
174
+ path = d
175
+ fix_shebang(path)
176
+ end
177
+
178
+ # taken from rpa-base, originally based on setup.rb's
179
+ # modify: #!/usr/bin/ruby
180
+ # modify: #! /usr/bin/ruby
181
+ # modify: #!ruby
182
+ # not modify: #!/usr/bin/env ruby
183
+ SHEBANG_RE = /\A\#!\s*\S*ruby\S*/
184
+
185
+ #TODO allow the ruby-prog to be placed in the shebang line to be passed as
186
+ # an option
187
+ def fix_shebang(path)
188
+ tmpfile = path + '.tmp'
189
+ begin
190
+ #XXX: needed at all?
191
+ # it seems that FileUtils doesn't expose its default output
192
+ # @fileutils_output = $stderr
193
+ # we might want to allow this to be redirected.
194
+ $stderr.puts "shebang:open #{tmpfile}" if @options.verbose
195
+ unless @options.noop
196
+ File.open(path) do |r|
197
+ File.open(tmpfile, 'w', 0755) do |w|
198
+ first = r.gets
199
+ return unless SHEBANG_RE =~ first
200
+ ruby = File.join(::Config::CONFIG['bindir'],::Config::CONFIG['ruby_install_name'])
201
+ ruby << ::Config::CONFIG['EXEEXT']
202
+ #w.print first.sub(SHEBANG_RE, '#!' + Config::CONFIG['ruby-prog'])
203
+ w.print first.sub(SHEBANG_RE, '#!' + ruby)
204
+ w.write r.read
205
+ end
206
+ end
207
+ end
208
+ FileUtils.mv(tmpfile, path, :verbose => @options.verbose,
209
+ :noop => @options.noop)
210
+ ensure
211
+ FileUtils.rm_f(tmpfile, :verbose => @options.verbose,
212
+ :noop => @options.noop)
213
+ end
214
+ end
215
+
216
+ def <=>(other)
217
+ FULL_ORDER[self, other] || self.destination <=> other.destination
218
+ end
219
+
220
+ def hash
221
+ @destination.hash
222
+ end
223
+
224
+ def eql?(other)
225
+ self.class == other.class && self.destination == other.destination
226
+ end
227
+ end
228
+
229
+ order = [MkDir, InstallFile, FixShebang]
230
+ FULL_ORDER = lambda do |me, other|
231
+ a, b = order.index(me.class), order.index(other.class)
232
+ if a && b
233
+ (r = a - b) == 0 ? nil : r
234
+ else
235
+ -1
236
+ end
237
+ end
238
+
239
+ class ActionList < Array
240
+
241
+ def directories!(options)
242
+ dirnames = []
243
+ map! do |d|
244
+ if d.kind_of?(InstallFile) && !dirnames.include?(File.dirname(d.destination))
245
+ dirnames << File.dirname(d.destination)
246
+ [MkDir.new(File.dirname(d.destination), options), d]
247
+ else
248
+ d
249
+ end
250
+ end
251
+ flatten!
252
+ end
253
+
254
+ def run(task)
255
+ each { |action| action.__send__ task }
256
+ end
257
+ end
258
+
259
+ end # module Actions
260
+
261
+ Options = Struct.new(:noop, :verbose, :destdir, :nodoc)
262
+
263
+ class PackageSpecification_1_0
264
+
265
+ TASKS = %w[config setup install test show]
266
+ # default options for translate(foo => bar)
267
+ TRANSLATE_DEFAULT_OPTIONS = { :inherit => true }
268
+
269
+ def self.declare_file_type(args, &handle_arg)
270
+ str_arr_p = lambda{|x| Array === x && x.all?{|y| String === y}}
271
+
272
+ # strict type checking --- we don't want this to be extended arbitrarily
273
+ unless args.size == 1 && Hash === args.first &&
274
+ args.first.all?{|f,r| [Proc, String, NilClass].include?(r.class) &&
275
+ (String === f || str_arr_p[f])} or
276
+ args.all?{|x| String === x || str_arr_p[x]}
277
+ raise SpecificationError,
278
+ "Unspecified semantics for the given arguments: #{args.inspect}"
279
+ end
280
+
281
+ if args.size == 1 && Hash === args.first
282
+ args.first.to_a.each do |file, rename_info|
283
+ if Array === file
284
+ # ignoring boring files
285
+ handle_arg.call(file, true, rename_info)
286
+ else
287
+ # we do want "boring" files given explicitly
288
+ handle_arg.call([file], false, rename_info)
289
+ end
290
+ end
291
+ else
292
+ args.each do |a|
293
+ if Array === a
294
+ a.each{|file| handle_arg.call(file, true, nil)}
295
+ else
296
+ handle_arg.call(a, false, nil)
297
+ end
298
+ end
299
+ end
300
+ end
301
+
302
+ #{{{ define the file tagging methods
303
+ KINDS.each do |kind|
304
+ define_method(kind) do |*args| # if this were 1.9 we could also take a block
305
+ bin_callback = lambda do |kind_, type, dest, options|
306
+ next if kind_ != :bin || type == :dir
307
+ @actions << Actions::FixShebang.new(dest, options)
308
+ end
309
+ #TODO: refactor
310
+ self.class.declare_file_type(args) do |files, ignore_p, opt_rename_info|
311
+ files.each do |file|
312
+ next if ignore_p && IGNORE_FILES.any?{|re| re.match(file)}
313
+ add_file(kind, file, opt_rename_info, &bin_callback)
314
+ end
315
+ end
316
+ end
317
+ end
318
+
319
+ def unit_test(*files)
320
+ if ARGV.length > 0
321
+ files.each do |x|
322
+ @unit_tests.concat([x]) if ARGV.include?(File.basename(x).sub(/\.\w+$/,''))
323
+ end
324
+ else
325
+ @unit_tests.concat files.flatten
326
+ end
327
+ end
328
+
329
+ def ri(*files)
330
+ @ri_files.concat files.flatten
331
+ end
332
+
333
+ def build_ext(path,opts = {:mkmf => true}, &blk)
334
+ @ext_targets << [path,opts,blk]
335
+ end
336
+
337
+ attr_accessor :actions, :options
338
+
339
+ def self.metadata(name)
340
+ define_method(name) do |*args|
341
+ if args.size == 1
342
+ @metadata[name] = args.first
343
+ end
344
+ @metadata[name]
345
+ end
346
+ end
347
+
348
+ metadata :name
349
+ metadata :version
350
+ metadata :author
351
+
352
+ def translate_dir(kind, dir)
353
+ replaced_dir_parts = dir.split(%r{/})
354
+ kept_dir_parts = []
355
+ loop do
356
+ replaced_path = replaced_dir_parts.join("/")
357
+ target, options = @translate[kind][replaced_path]
358
+ options ||= TRANSLATE_DEFAULT_OPTIONS
359
+ if target && (replaced_path == dir || options[:inherit])
360
+ dir = (target != '' ? File.join(target, *kept_dir_parts) :
361
+ File.join(*kept_dir_parts))
362
+ break
363
+ end
364
+ break if replaced_dir_parts.empty?
365
+ kept_dir_parts.unshift replaced_dir_parts.pop
366
+ end
367
+ dir
368
+ end
369
+
370
+ def add_file(kind, filename, new_filename_info, &callback)
371
+ #TODO: refactor!!!
372
+ if File.directory? filename #XXX setup.rb and rpa-base defined File.dir?
373
+ dir = filename.sub(/\A\.\//, "").sub(/\/\z/, "")
374
+ dest = File.join(@prefixes[kind], @dirs[kind], translate_dir(kind, dir))
375
+ @actions << Actions::MkDir.new(dest, @options)
376
+ callback.call(kind, :dir, dest, @options) if block_given?
377
+ else
378
+ if new_filename_info
379
+ case new_filename_info
380
+ when Proc
381
+ dest_name = new_filename_info.call(filename.dup)
382
+ else
383
+ dest_name = new_filename_info.dup
384
+ end
385
+ else
386
+ dest_name = filename.dup
387
+ end
388
+
389
+ dirname = File.dirname(dest_name)
390
+ dirname = "" if dirname == "."
391
+ dest_name = File.join(translate_dir(kind, dirname), File.basename(dest_name))
392
+
393
+ dest = File.join(@prefixes[kind], @dirs[kind], dest_name)
394
+ @actions << Actions::InstallFile.new(filename, dest, MODES[kind], @options)
395
+ callback.call(kind, :file, dest, @options) if block_given?
396
+ end
397
+ end
398
+
399
+ def initialize(prefixes = nil, dirs = nil)
400
+ @prefix = Config::CONFIG["prefix"].gsub(/\A\//, '')
401
+ @translate = {}
402
+ @prefixes = (prefixes || {}).dup
403
+ KINDS.each do |kind|
404
+ @prefixes[kind] = @prefix unless prefixes
405
+ @translate[kind] = {}
406
+ end
407
+
408
+ @dirs = (dirs || {}).dup
409
+ @dirs.update SITE_DIRS unless dirs
410
+
411
+ @actions = Actions::ActionList.new
412
+
413
+ @metadata = {}
414
+ @unit_tests = []
415
+ @ri_files = []
416
+ @ext_targets = []
417
+
418
+ @options = Options.new
419
+ @options.verbose = true
420
+ @options.noop = false # XXX for testing
421
+ @options.destdir = ''
422
+
423
+ @tasks = []
424
+ end
425
+
426
+ def aoki
427
+ (KINDS - [:ext]).each do |kind|
428
+ translate(kind, kind.to_s => "", :inherit => true)
429
+ __send__ kind, Dir["#{kind}/**/*"]
430
+ end
431
+ translate(:ext, "ext/*" => "", :inherit => true)
432
+ ext Dir["ext/**/*.#{Config::CONFIG['DLEXT']}"]
433
+ end
434
+
435
+ # Builds any needed extensions.
436
+
437
+ def setup
438
+ @ext_targets.each do |et|
439
+ path, options, block = et
440
+ path = expand_ext_path(path)
441
+ case
442
+ when options[:mkmf]
443
+ setup_mkmf(path, options, block)
444
+ end
445
+ end
446
+ end
447
+
448
+ def expand_ext_path(path)
449
+ if path =~ /#{File::SEPARATOR}/
450
+ extpath = File.expand_path(path)
451
+ else
452
+ extpath = File.expand_path(File.join('ext',path,'extconf.rb'))
453
+ end
454
+ unless extpath =~ /\.rb$/
455
+ extpath = File.join(extpath,'extconf.rb')
456
+ end
457
+ extpath
458
+ end
459
+
460
+ def setup_mkmf(execpath, options, block)
461
+ $stderr.puts "Building extension in #{File.dirname(execpath)} with #{options.inspect}."
462
+ cwd = Dir.pwd
463
+ begin
464
+ if block
465
+ block.call(options)
466
+ else
467
+ Dir.chdir(File.dirname(execpath))
468
+ system('make','clean') if FileTest.exist? "Makefile"
469
+ system(RUBY_BIN,execpath)
470
+ system('make')
471
+ end
472
+ ensure
473
+ Dir.chdir(cwd)
474
+ end
475
+ end
476
+
477
+ def install
478
+ $stderr.puts "Installing #{name || "unknown package"} #{version}..." if options.verbose
479
+
480
+ actions.uniq!
481
+ actions.sort!
482
+ actions.directories!(options)
483
+
484
+ actions.run :install
485
+
486
+ unless @ri_files.empty? or @options.nodoc
487
+ $stderr.puts "Generating ri documentation from #{@ri_files.join(',')}"
488
+ require 'rdoc/rdoc'
489
+ unless options.noop
490
+ begin
491
+ RDoc::RDoc.new.document(["--ri-site"].concat(@ri_files.flatten))
492
+ rescue Exception => e
493
+ $stderr.write("Installation of ri documentation failed: #{e.to_s} #{e.backtrace.join("\n")}")
494
+ end
495
+ end
496
+ end
497
+ end
498
+
499
+ def test
500
+ unless @unit_tests.empty?
501
+ puts "Testing #{name || "unknown package"} #{version}..." if options.verbose
502
+ require 'test/unit'
503
+ unless options.noop
504
+ t = Test::Unit::AutoRunner.new(true)
505
+ t.process_args(@unit_tests)
506
+ t.run
507
+ end
508
+ end
509
+ end
510
+
511
+ def config
512
+ File.open("config.save", "w") do |f|
513
+ YAML.dump({:prefixes => @prefixes, :dirs => @dirs}, f)
514
+ end
515
+ end
516
+
517
+ def show
518
+ KINDS.each do |kind|
519
+ puts "#{kind}\t#{File.join(options.destdir, @prefixes[kind], @dirs[kind])}"
520
+ end
521
+ end
522
+
523
+ def translate(kind, additional_translations)
524
+ default_opts = TRANSLATE_DEFAULT_OPTIONS.dup
525
+ key_val_pairs = additional_translations.to_a
526
+ option_pairs = key_val_pairs.select{|(k,v)| Symbol === k}
527
+ default_opts.update(Hash[*option_pairs.flatten])
528
+
529
+ (key_val_pairs - option_pairs).each do |key, val|
530
+ add_translation(kind, key, val, default_opts)
531
+ end
532
+ end
533
+
534
+ def add_translation(kind, src, dest, options)
535
+ if is_glob?(src)
536
+ dirs = expand_dir_glob(src)
537
+ else
538
+ dirs = [src]
539
+ end
540
+ dirs.each do |dirname|
541
+ dirname = dirname.sub(%r{\A\./}, "").sub(%r{/\z}, "")
542
+ @translate[kind].update({dirname => [dest, options]})
543
+ end
544
+ end
545
+
546
+ def is_glob?(x)
547
+ /(^|[^\\])[*?{\[]/.match(x)
548
+ end
549
+
550
+ def expand_dir_glob(src)
551
+ Dir[src].select{|x| File.directory?(x)}
552
+ end
553
+
554
+ def clean_path(path)
555
+ path.gsub(/\A\//, '').gsub(/\/+\Z/, '').squeeze("/")
556
+ end
557
+
558
+ def parse_command_line
559
+ opts = OptionParser.new(nil, 24, ' ') do |opts|
560
+ opts.banner = "Usage: setup.rb [options] [task]"
561
+
562
+ opts.separator ""
563
+ opts.separator "Tasks:"
564
+ opts.separator " config configures paths"
565
+ opts.separator " show shows paths"
566
+ opts.separator " setup compiles ruby extentions and others XXX"
567
+ opts.separator " install installs files"
568
+ opts.separator " test runs unit tests"
569
+
570
+
571
+ opts.separator ""
572
+ opts.separator "Specific options:"
573
+
574
+ opts.on "--prefix=PREFIX",
575
+ "path prefix of target environment [#@prefix]" do |prefix|
576
+ @prefix.replace clean_path(prefix) # Shared!
577
+ end
578
+
579
+ opts.separator ""
580
+
581
+ KINDS.each do |kind|
582
+ opts.on "--#{kind}prefix=PREFIX",
583
+ "path prefix for #{kind} files [#{@prefixes[kind]}]" do |prefix|
584
+ @prefixes[kind] = clean_path(prefix)
585
+ end
586
+ end
587
+
588
+ opts.separator ""
589
+
590
+ KINDS.each do |kind|
591
+ opts.on "--#{kind}dir=PREFIX",
592
+ "directory for #{kind} files [#{@dirs[kind]}]" do |prefix|
593
+ @dirs[kind] = clean_path(prefix)
594
+ end
595
+ end
596
+
597
+ opts.separator ""
598
+
599
+ KINDS.each do |kind|
600
+ opts.on "--#{kind}=PREFIX",
601
+ "absolute directory for #{kind} files [#{File.join(@prefixes[kind], @dirs[kind])}]" do |prefix|
602
+ @prefixes[kind] = clean_path(prefix)
603
+ end
604
+ end
605
+
606
+ opts.separator ""
607
+ opts.separator "Predefined path configurations:"
608
+ opts.on "--site", "install into site-local directories (default)" do
609
+ @dirs.update SITE_DIRS
610
+ end
611
+
612
+ opts.on "--vendor", "install into distribution directories (for packagers)" do
613
+ @dirs.update VENDOR_DIRS
614
+ end
615
+
616
+ opts.separator ""
617
+ opts.separator "General options:"
618
+
619
+ opts.on "--destdir=DESTDIR",
620
+ "install all files relative to DESTDIR (/)" do |destdir|
621
+ @options.destdir = destdir
622
+ end
623
+
624
+ opts.on "--dry-run", "only display what to do if given [#{@options.noop}]" do
625
+ @options.noop = true
626
+ end
627
+
628
+ opts.on "--no-harm", "only display what to do if given" do
629
+ @options.noop = true
630
+ end
631
+
632
+ opts.on "--no-doc","don't generate documentation" do
633
+ @options.nodoc = true
634
+ end
635
+
636
+ opts.on "--[no-]verbose", "output messages verbosely [#{@options.verbose}]" do |verbose|
637
+ @options.verbose = verbose
638
+ end
639
+
640
+ opts.on_tail("-h", "--help", "Show this message") do
641
+ puts opts
642
+ exit
643
+ end
644
+ end
645
+
646
+ opts.parse! ARGV
647
+
648
+ #if (ARGV - TASKS).empty? # Only existing tasks?
649
+ @tasks = ARGV.dup.select do |t|
650
+ if TASKS.include?(t)
651
+ ARGV.delete(t)
652
+ true
653
+ end
654
+ end
655
+
656
+ @tasks = ["setup","install"] if @tasks.empty?
657
+ #else
658
+ # abort "Unknown task(s) #{(ARGV-TASKS).join ", "}."
659
+ #end
660
+ end
661
+
662
+ def run_tasks
663
+ @tasks.each { |task| __send__ task }
664
+ end
665
+ end
666
+
667
+ end # module Package
668
+
669
+ require 'rbconfig'
670
+ def config(x)
671
+ Config::CONFIG[x]
672
+ end