analogger 0.5.0
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.
- data/INSTALL +60 -0
- data/README +19 -0
- data/analogger.gemspec +41 -0
- data/bin/analogger +94 -0
- data/external/package.rb +672 -0
- data/external/test_support.rb +58 -0
- data/setup.rb +26 -0
- data/src/swiftcore/Analogger.rb +293 -0
- data/src/swiftcore/Analogger/Client.rb +103 -0
- data/test/TC_Analogger.rb +151 -0
- data/test/analogger.cnf +22 -0
- data/test/analogger2.cnf +9 -0
- data/test/tc_template.rb +15 -0
- metadata +78 -0
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.
|
data/analogger.gemspec
ADDED
@@ -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
|
data/bin/analogger
ADDED
@@ -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
|
data/external/package.rb
ADDED
@@ -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
|