unicorn-shopify 4.8.2.5.23
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.
- checksums.yaml +7 -0
- data/.CHANGELOG.old +25 -0
- data/.document +28 -0
- data/.gitignore +25 -0
- data/.mailmap +26 -0
- data/.olddoc.yml +15 -0
- data/Application_Timeouts +77 -0
- data/CONTRIBUTORS +35 -0
- data/COPYING +674 -0
- data/DESIGN +97 -0
- data/Documentation/.gitignore +5 -0
- data/Documentation/GNUmakefile +30 -0
- data/Documentation/unicorn.1.txt +185 -0
- data/Documentation/unicorn_rails.1.txt +175 -0
- data/FAQ +61 -0
- data/GIT-VERSION-GEN +39 -0
- data/GNUmakefile +252 -0
- data/HACKING +120 -0
- data/ISSUES +100 -0
- data/KNOWN_ISSUES +79 -0
- data/LICENSE +67 -0
- data/Links +59 -0
- data/PHILOSOPHY +145 -0
- data/README +145 -0
- data/Rakefile +16 -0
- data/SIGNALS +123 -0
- data/Sandbox +103 -0
- data/TODO +5 -0
- data/TUNING +101 -0
- data/archive/.gitignore +3 -0
- data/archive/slrnpull.conf +4 -0
- data/bin/unicorn +126 -0
- data/bin/unicorn_rails +209 -0
- data/examples/big_app_gc.rb +2 -0
- data/examples/echo.ru +27 -0
- data/examples/init.sh +74 -0
- data/examples/logger_mp_safe.rb +25 -0
- data/examples/logrotate.conf +29 -0
- data/examples/nginx.conf +156 -0
- data/examples/unicorn.conf.minimal.rb +13 -0
- data/examples/unicorn.conf.rb +113 -0
- data/ext/unicorn_http/CFLAGS +13 -0
- data/ext/unicorn_http/c_util.h +124 -0
- data/ext/unicorn_http/common_field_optimization.h +111 -0
- data/ext/unicorn_http/ext_help.h +82 -0
- data/ext/unicorn_http/extconf.rb +10 -0
- data/ext/unicorn_http/global_variables.h +97 -0
- data/ext/unicorn_http/httpdate.c +78 -0
- data/ext/unicorn_http/unicorn_http.rl +934 -0
- data/ext/unicorn_http/unicorn_http_common.rl +76 -0
- data/lib/unicorn.rb +112 -0
- data/lib/unicorn/app/old_rails.rb +35 -0
- data/lib/unicorn/app/old_rails/static.rb +59 -0
- data/lib/unicorn/cgi_wrapper.rb +147 -0
- data/lib/unicorn/configurator.rb +686 -0
- data/lib/unicorn/const.rb +21 -0
- data/lib/unicorn/http_request.rb +125 -0
- data/lib/unicorn/http_response.rb +73 -0
- data/lib/unicorn/http_server.rb +816 -0
- data/lib/unicorn/launcher.rb +62 -0
- data/lib/unicorn/oob_gc.rb +81 -0
- data/lib/unicorn/preread_input.rb +33 -0
- data/lib/unicorn/socket_helper.rb +197 -0
- data/lib/unicorn/stream_input.rb +146 -0
- data/lib/unicorn/tee_input.rb +133 -0
- data/lib/unicorn/tmpio.rb +27 -0
- data/lib/unicorn/util.rb +90 -0
- data/lib/unicorn/worker.rb +140 -0
- data/setup.rb +1586 -0
- data/t/.gitignore +4 -0
- data/t/GNUmakefile +74 -0
- data/t/README +42 -0
- data/t/before_murder.ru +7 -0
- data/t/bin/content-md5-put +36 -0
- data/t/bin/sha1sum.rb +17 -0
- data/t/bin/unused_listen +40 -0
- data/t/broken-app.ru +12 -0
- data/t/detach.ru +11 -0
- data/t/env.ru +3 -0
- data/t/fails-rack-lint.ru +5 -0
- data/t/heartbeat-timeout.ru +12 -0
- data/t/hijack.ru +42 -0
- data/t/listener_names.ru +4 -0
- data/t/my-tap-lib.sh +201 -0
- data/t/oob_gc.ru +20 -0
- data/t/oob_gc_path.ru +20 -0
- data/t/pid.ru +3 -0
- data/t/preread_input.ru +17 -0
- data/t/rack-input-tests.ru +21 -0
- data/t/t0000-http-basic.sh +50 -0
- data/t/t0001-reload-bad-config.sh +53 -0
- data/t/t0002-config-conflict.sh +49 -0
- data/t/t0002-parser-error.sh +94 -0
- data/t/t0003-working_directory.sh +51 -0
- data/t/t0004-heartbeat-timeout.sh +69 -0
- data/t/t0004-working_directory_broken.sh +24 -0
- data/t/t0005-working_directory_app.rb.sh +40 -0
- data/t/t0006-reopen-logs.sh +83 -0
- data/t/t0006.ru +13 -0
- data/t/t0007-working_directory_no_embed_cli.sh +44 -0
- data/t/t0008-back_out_of_upgrade.sh +110 -0
- data/t/t0009-broken-app.sh +56 -0
- data/t/t0009-winch_ttin.sh +59 -0
- data/t/t0010-reap-logging.sh +55 -0
- data/t/t0011-active-unix-socket.sh +79 -0
- data/t/t0012-reload-empty-config.sh +85 -0
- data/t/t0013-rewindable-input-false.sh +24 -0
- data/t/t0013.ru +12 -0
- data/t/t0014-rewindable-input-true.sh +24 -0
- data/t/t0014.ru +12 -0
- data/t/t0015-configurator-internals.sh +25 -0
- data/t/t0018-write-on-close.sh +23 -0
- data/t/t0019-max_header_len.sh +49 -0
- data/t/t0020-at_exit-handler.sh +49 -0
- data/t/t0021-process_detach.sh +29 -0
- data/t/t0022-listener_names-preload_app.sh +32 -0
- data/t/t0023-before-murder.sh +40 -0
- data/t/t0024-before-murder_once.sh +52 -0
- data/t/t0100-rack-input-tests.sh +124 -0
- data/t/t0116-client_body_buffer_size.sh +80 -0
- data/t/t0116.ru +16 -0
- data/t/t0200-rack-hijack.sh +27 -0
- data/t/t0300-no-default-middleware.sh +20 -0
- data/t/t9000-preread-input.sh +48 -0
- data/t/t9001-oob_gc.sh +47 -0
- data/t/t9002-oob_gc-path.sh +75 -0
- data/t/test-lib.sh +128 -0
- data/t/write-on-close.ru +11 -0
- data/test/aggregate.rb +15 -0
- data/test/benchmark/README +50 -0
- data/test/benchmark/dd.ru +18 -0
- data/test/benchmark/stack.ru +8 -0
- data/test/exec/README +5 -0
- data/test/exec/test_exec.rb +1047 -0
- data/test/test_helper.rb +297 -0
- data/test/unit/test_configurator.rb +175 -0
- data/test/unit/test_droplet.rb +28 -0
- data/test/unit/test_http_parser.rb +854 -0
- data/test/unit/test_http_parser_ng.rb +622 -0
- data/test/unit/test_request.rb +182 -0
- data/test/unit/test_response.rb +93 -0
- data/test/unit/test_server.rb +268 -0
- data/test/unit/test_signals.rb +188 -0
- data/test/unit/test_socket_helper.rb +197 -0
- data/test/unit/test_stream_input.rb +203 -0
- data/test/unit/test_tee_input.rb +304 -0
- data/test/unit/test_upload.rb +306 -0
- data/test/unit/test_util.rb +105 -0
- data/unicorn.gemspec +41 -0
- metadata +311 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# -*- encoding: binary -*-
|
|
2
|
+
|
|
3
|
+
# acts like tee(1) on an input input to provide a input-like stream
|
|
4
|
+
# while providing rewindable semantics through a File/StringIO backing
|
|
5
|
+
# store. On the first pass, the input is only read on demand so your
|
|
6
|
+
# Rack application can use input notification (upload progress and
|
|
7
|
+
# like). This should fully conform to the Rack::Lint::InputWrapper
|
|
8
|
+
# specification on the public API. This class is intended to be a
|
|
9
|
+
# strict interpretation of Rack::Lint::InputWrapper functionality and
|
|
10
|
+
# will not support any deviations from it.
|
|
11
|
+
#
|
|
12
|
+
# When processing uploads, Unicorn exposes a TeeInput object under
|
|
13
|
+
# "rack.input" of the Rack environment.
|
|
14
|
+
class Unicorn::TeeInput < Unicorn::StreamInput
|
|
15
|
+
# The maximum size (in +bytes+) to buffer in memory before
|
|
16
|
+
# resorting to a temporary file. Default is 112 kilobytes.
|
|
17
|
+
@@client_body_buffer_size = Unicorn::Const::MAX_BODY
|
|
18
|
+
|
|
19
|
+
# sets the maximum size of request bodies to buffer in memory,
|
|
20
|
+
# amounts larger than this are buffered to the filesystem
|
|
21
|
+
def self.client_body_buffer_size=(bytes)
|
|
22
|
+
@@client_body_buffer_size = bytes
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# returns the maximum size of request bodies to buffer in memory,
|
|
26
|
+
# amounts larger than this are buffered to the filesystem
|
|
27
|
+
def self.client_body_buffer_size
|
|
28
|
+
@@client_body_buffer_size
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# for Rack::TempfileReaper in rack 1.6+
|
|
32
|
+
def new_tmpio # :nodoc:
|
|
33
|
+
tmpio = Unicorn::TmpIO.new
|
|
34
|
+
(@parser.env['rack.tempfiles'] ||= []) << tmpio
|
|
35
|
+
tmpio
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Initializes a new TeeInput object. You normally do not have to call
|
|
39
|
+
# this unless you are writing an HTTP server.
|
|
40
|
+
def initialize(socket, request)
|
|
41
|
+
@len = request.content_length
|
|
42
|
+
super
|
|
43
|
+
@tmp = @len && @len <= @@client_body_buffer_size ?
|
|
44
|
+
StringIO.new("") : new_tmpio
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# :call-seq:
|
|
48
|
+
# ios.size => Integer
|
|
49
|
+
#
|
|
50
|
+
# Returns the size of the input. For requests with a Content-Length
|
|
51
|
+
# header value, this will not read data off the socket and just return
|
|
52
|
+
# the value of the Content-Length header as an Integer.
|
|
53
|
+
#
|
|
54
|
+
# For Transfer-Encoding:chunked requests, this requires consuming
|
|
55
|
+
# all of the input stream before returning since there's no other
|
|
56
|
+
# way to determine the size of the request body beforehand.
|
|
57
|
+
#
|
|
58
|
+
# This method is no longer part of the Rack specification as of
|
|
59
|
+
# Rack 1.2, so its use is not recommended. This method only exists
|
|
60
|
+
# for compatibility with Rack applications designed for Rack 1.1 and
|
|
61
|
+
# earlier. Most applications should only need to call +read+ with a
|
|
62
|
+
# specified +length+ in a loop until it returns +nil+.
|
|
63
|
+
def size
|
|
64
|
+
@len and return @len
|
|
65
|
+
pos = @tmp.pos
|
|
66
|
+
consume!
|
|
67
|
+
@tmp.pos = pos
|
|
68
|
+
@len = @tmp.size
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# :call-seq:
|
|
72
|
+
# ios.read([length [, buffer ]]) => string, buffer, or nil
|
|
73
|
+
#
|
|
74
|
+
# Reads at most length bytes from the I/O stream, or to the end of
|
|
75
|
+
# file if length is omitted or is nil. length must be a non-negative
|
|
76
|
+
# integer or nil. If the optional buffer argument is present, it
|
|
77
|
+
# must reference a String, which will receive the data.
|
|
78
|
+
#
|
|
79
|
+
# At end of file, it returns nil or "" depend on length.
|
|
80
|
+
# ios.read() and ios.read(nil) returns "".
|
|
81
|
+
# ios.read(length [, buffer]) returns nil.
|
|
82
|
+
#
|
|
83
|
+
# If the Content-Length of the HTTP request is known (as is the common
|
|
84
|
+
# case for POST requests), then ios.read(length [, buffer]) will block
|
|
85
|
+
# until the specified length is read (or it is the last chunk).
|
|
86
|
+
# Otherwise, for uncommon "Transfer-Encoding: chunked" requests,
|
|
87
|
+
# ios.read(length [, buffer]) will return immediately if there is
|
|
88
|
+
# any data and only block when nothing is available (providing
|
|
89
|
+
# IO#readpartial semantics).
|
|
90
|
+
def read(*args)
|
|
91
|
+
@socket ? tee(super) : @tmp.read(*args)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# :call-seq:
|
|
95
|
+
# ios.gets => string or nil
|
|
96
|
+
#
|
|
97
|
+
# Reads the next ``line'' from the I/O stream; lines are separated
|
|
98
|
+
# by the global record separator ($/, typically "\n"). A global
|
|
99
|
+
# record separator of nil reads the entire unread contents of ios.
|
|
100
|
+
# Returns nil if called at the end of file.
|
|
101
|
+
# This takes zero arguments for strict Rack::Lint compatibility,
|
|
102
|
+
# unlike IO#gets.
|
|
103
|
+
def gets
|
|
104
|
+
@socket ? tee(super) : @tmp.gets
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# :call-seq:
|
|
108
|
+
# ios.rewind => 0
|
|
109
|
+
#
|
|
110
|
+
# Positions the *ios* pointer to the beginning of input, returns
|
|
111
|
+
# the offset (zero) of the +ios+ pointer. Subsequent reads will
|
|
112
|
+
# start from the beginning of the previously-buffered input.
|
|
113
|
+
def rewind
|
|
114
|
+
return 0 if 0 == @tmp.size
|
|
115
|
+
consume! if @socket
|
|
116
|
+
@tmp.rewind # Rack does not specify what the return value is here
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
private
|
|
120
|
+
|
|
121
|
+
# consumes the stream of the socket
|
|
122
|
+
def consume!
|
|
123
|
+
junk = ""
|
|
124
|
+
nil while read(@@io_chunk_size, junk)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def tee(buffer)
|
|
128
|
+
if buffer && buffer.size > 0
|
|
129
|
+
@tmp.write(buffer)
|
|
130
|
+
end
|
|
131
|
+
buffer
|
|
132
|
+
end
|
|
133
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# -*- encoding: binary -*-
|
|
2
|
+
# :stopdoc:
|
|
3
|
+
require 'tmpdir'
|
|
4
|
+
|
|
5
|
+
# some versions of Ruby had a broken Tempfile which didn't work
|
|
6
|
+
# well with unlinked files. This one is much shorter, easier
|
|
7
|
+
# to understand, and slightly faster.
|
|
8
|
+
class Unicorn::TmpIO < File
|
|
9
|
+
|
|
10
|
+
# creates and returns a new File object. The File is unlinked
|
|
11
|
+
# immediately, switched to binary mode, and userspace output
|
|
12
|
+
# buffering is disabled
|
|
13
|
+
def self.new
|
|
14
|
+
fp = begin
|
|
15
|
+
super("#{Dir::tmpdir}/#{rand}", RDWR|CREAT|EXCL, 0600)
|
|
16
|
+
rescue Errno::EEXIST
|
|
17
|
+
retry
|
|
18
|
+
end
|
|
19
|
+
unlink(fp.path)
|
|
20
|
+
fp.binmode
|
|
21
|
+
fp.sync = true
|
|
22
|
+
fp
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# pretend we're Tempfile for Rack::TempfileReaper
|
|
26
|
+
alias close! close
|
|
27
|
+
end
|
data/lib/unicorn/util.rb
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# -*- encoding: binary -*-
|
|
2
|
+
|
|
3
|
+
require 'fcntl'
|
|
4
|
+
module Unicorn::Util
|
|
5
|
+
|
|
6
|
+
# :stopdoc:
|
|
7
|
+
def self.is_log?(fp)
|
|
8
|
+
append_flags = File::WRONLY | File::APPEND
|
|
9
|
+
|
|
10
|
+
! fp.closed? &&
|
|
11
|
+
fp.stat.file? &&
|
|
12
|
+
fp.sync &&
|
|
13
|
+
(fp.fcntl(Fcntl::F_GETFL) & append_flags) == append_flags
|
|
14
|
+
rescue IOError, Errno::EBADF
|
|
15
|
+
false
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.chown_logs(uid, gid)
|
|
19
|
+
ObjectSpace.each_object(File) do |fp|
|
|
20
|
+
fp.chown(uid, gid) if is_log?(fp)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
# :startdoc:
|
|
24
|
+
|
|
25
|
+
# This reopens ALL logfiles in the process that have been rotated
|
|
26
|
+
# using logrotate(8) (without copytruncate) or similar tools.
|
|
27
|
+
# A +File+ object is considered for reopening if it is:
|
|
28
|
+
# 1) opened with the O_APPEND and O_WRONLY flags
|
|
29
|
+
# 2) the current open file handle does not match its original open path
|
|
30
|
+
# 3) unbuffered (as far as userspace buffering goes, not O_SYNC)
|
|
31
|
+
# Returns the number of files reopened
|
|
32
|
+
#
|
|
33
|
+
# In Unicorn 3.5.x and earlier, files must be opened with an absolute
|
|
34
|
+
# path to be considered a log file.
|
|
35
|
+
def self.reopen_logs
|
|
36
|
+
to_reopen = []
|
|
37
|
+
nr = 0
|
|
38
|
+
ObjectSpace.each_object(File) { |fp| is_log?(fp) and to_reopen << fp }
|
|
39
|
+
|
|
40
|
+
to_reopen.each do |fp|
|
|
41
|
+
orig_st = begin
|
|
42
|
+
fp.stat
|
|
43
|
+
rescue IOError, Errno::EBADF # race
|
|
44
|
+
next
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
begin
|
|
48
|
+
b = File.stat(fp.path)
|
|
49
|
+
next if orig_st.ino == b.ino && orig_st.dev == b.dev
|
|
50
|
+
rescue Errno::ENOENT
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
begin
|
|
54
|
+
# stdin, stdout, stderr are special. The following dance should
|
|
55
|
+
# guarantee there is no window where `fp' is unwritable in MRI
|
|
56
|
+
# (or any correct Ruby implementation).
|
|
57
|
+
#
|
|
58
|
+
# Fwiw, GVL has zero bearing here. This is tricky because of
|
|
59
|
+
# the unavoidable existence of stdio FILE * pointers for
|
|
60
|
+
# std{in,out,err} in all programs which may use the standard C library
|
|
61
|
+
if fp.fileno <= 2
|
|
62
|
+
# We do not want to hit fclose(3)->dup(2) window for std{in,out,err}
|
|
63
|
+
# MRI will use freopen(3) here internally on std{in,out,err}
|
|
64
|
+
fp.reopen(fp.path, "a")
|
|
65
|
+
else
|
|
66
|
+
# We should not need this workaround, Ruby can be fixed:
|
|
67
|
+
# http://bugs.ruby-lang.org/issues/9036
|
|
68
|
+
# MRI will not call call fclose(3) or freopen(3) here
|
|
69
|
+
# since there's no associated std{in,out,err} FILE * pointer
|
|
70
|
+
# This should atomically use dup3(2) (or dup2(2)) syscall
|
|
71
|
+
File.open(fp.path, "a") { |tmpfp| fp.reopen(tmpfp) }
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
fp.sync = true
|
|
75
|
+
fp.flush # IO#sync=true may not implicitly flush
|
|
76
|
+
new_st = fp.stat
|
|
77
|
+
|
|
78
|
+
# this should only happen in the master:
|
|
79
|
+
if orig_st.uid != new_st.uid || orig_st.gid != new_st.gid
|
|
80
|
+
fp.chown(orig_st.uid, orig_st.gid)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
nr += 1
|
|
84
|
+
rescue IOError, Errno::EBADF
|
|
85
|
+
# not much we can do...
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
nr
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# -*- encoding: binary -*-
|
|
2
|
+
require "raindrops"
|
|
3
|
+
|
|
4
|
+
# This class and its members can be considered a stable interface
|
|
5
|
+
# and will not change in a backwards-incompatible fashion between
|
|
6
|
+
# releases of \Unicorn. Knowledge of this class is generally not
|
|
7
|
+
# not needed for most users of \Unicorn.
|
|
8
|
+
#
|
|
9
|
+
# Some users may want to access it in the before_fork/after_fork hooks.
|
|
10
|
+
# See the Unicorn::Configurator RDoc for examples.
|
|
11
|
+
class Unicorn::Worker
|
|
12
|
+
# :stopdoc:
|
|
13
|
+
attr_accessor :nr, :switched
|
|
14
|
+
attr_reader :to_io # IO.select-compatible
|
|
15
|
+
|
|
16
|
+
PER_DROP = Raindrops::PAGE_SIZE / Raindrops::SIZE
|
|
17
|
+
DROPS = []
|
|
18
|
+
|
|
19
|
+
def initialize(nr)
|
|
20
|
+
drop_index = nr / PER_DROP
|
|
21
|
+
@raindrop = DROPS[drop_index] ||= Raindrops.new(PER_DROP)
|
|
22
|
+
@offset = nr % PER_DROP
|
|
23
|
+
@raindrop[@offset] = 0
|
|
24
|
+
@nr = nr
|
|
25
|
+
@switched = false
|
|
26
|
+
@to_io, @master = Unicorn.pipe
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def atfork_child # :nodoc:
|
|
30
|
+
# we _must_ close in child, parent just holds this open to signal
|
|
31
|
+
@master = @master.close
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# master fakes SIGQUIT using this
|
|
35
|
+
def quit # :nodoc:
|
|
36
|
+
@master = @master.close if @master
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# parent does not read
|
|
40
|
+
def atfork_parent # :nodoc:
|
|
41
|
+
@to_io = @to_io.close
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# call a signal handler immediately without triggering EINTR
|
|
45
|
+
# We do not use the more obvious Process.kill(sig, $$) here since
|
|
46
|
+
# that signal delivery may be deferred. We want to avoid signal delivery
|
|
47
|
+
# while the Rack app.call is running because some database drivers
|
|
48
|
+
# (e.g. ruby-pg) may cancel pending requests.
|
|
49
|
+
def fake_sig(sig) # :nodoc:
|
|
50
|
+
old_cb = trap(sig, "IGNORE")
|
|
51
|
+
old_cb.call
|
|
52
|
+
ensure
|
|
53
|
+
trap(sig, old_cb)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# master sends fake signals to children
|
|
57
|
+
def soft_kill(sig) # :nodoc:
|
|
58
|
+
case sig
|
|
59
|
+
when Integer
|
|
60
|
+
signum = sig
|
|
61
|
+
else
|
|
62
|
+
signum = Signal.list[sig.to_s] or
|
|
63
|
+
raise ArgumentError, "BUG: bad signal: #{sig.inspect}"
|
|
64
|
+
end
|
|
65
|
+
# writing and reading 4 bytes on a pipe is atomic on all POSIX platforms
|
|
66
|
+
# Do not care in the odd case the buffer is full, here.
|
|
67
|
+
@master.kgio_trywrite([signum].pack('l'))
|
|
68
|
+
rescue Errno::EPIPE
|
|
69
|
+
# worker will be reaped soon
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# this only runs when the Rack app.call is not running
|
|
73
|
+
# act like a listener
|
|
74
|
+
def kgio_tryaccept # :nodoc:
|
|
75
|
+
case buf = @to_io.kgio_tryread(4)
|
|
76
|
+
when String
|
|
77
|
+
# unpack the buffer and trigger the signal handler
|
|
78
|
+
signum = buf.unpack('l')
|
|
79
|
+
fake_sig(signum[0])
|
|
80
|
+
# keep looping, more signals may be queued
|
|
81
|
+
when nil # EOF: master died, but we are at a safe place to exit
|
|
82
|
+
fake_sig(:QUIT)
|
|
83
|
+
when :wait_readable # keep waiting
|
|
84
|
+
return false
|
|
85
|
+
end while true # loop, as multiple signals may be sent
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# worker objects may be compared to just plain Integers
|
|
89
|
+
def ==(other_nr) # :nodoc:
|
|
90
|
+
@nr == other_nr
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# called in the worker process
|
|
94
|
+
def tick=(value) # :nodoc:
|
|
95
|
+
@raindrop[@offset] = value
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# called in the master process
|
|
99
|
+
def tick # :nodoc:
|
|
100
|
+
@raindrop[@offset]
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# called in both the master (reaping worker) and worker (SIGQUIT handler)
|
|
104
|
+
def close # :nodoc:
|
|
105
|
+
@master.close if @master
|
|
106
|
+
@to_io.close if @to_io
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# :startdoc:
|
|
110
|
+
|
|
111
|
+
# In most cases, you should be using the Unicorn::Configurator#user
|
|
112
|
+
# directive instead. This method should only be used if you need
|
|
113
|
+
# fine-grained control of exactly when you want to change permissions
|
|
114
|
+
# in your after_fork hooks.
|
|
115
|
+
#
|
|
116
|
+
# Changes the worker process to the specified +user+ and +group+
|
|
117
|
+
# This is only intended to be called from within the worker
|
|
118
|
+
# process from the +after_fork+ hook. This should be called in
|
|
119
|
+
# the +after_fork+ hook after any privileged functions need to be
|
|
120
|
+
# run (e.g. to set per-worker CPU affinity, niceness, etc)
|
|
121
|
+
#
|
|
122
|
+
# Any and all errors raised within this method will be propagated
|
|
123
|
+
# directly back to the caller (usually the +after_fork+ hook.
|
|
124
|
+
# These errors commonly include ArgumentError for specifying an
|
|
125
|
+
# invalid user/group and Errno::EPERM for insufficient privileges
|
|
126
|
+
def user(user, group = nil)
|
|
127
|
+
# we do not protect the caller, checking Process.euid == 0 is
|
|
128
|
+
# insufficient because modern systems have fine-grained
|
|
129
|
+
# capabilities. Let the caller handle any and all errors.
|
|
130
|
+
uid = Etc.getpwnam(user).uid
|
|
131
|
+
gid = Etc.getgrnam(group).gid if group
|
|
132
|
+
Unicorn::Util.chown_logs(uid, gid)
|
|
133
|
+
if gid && Process.egid != gid
|
|
134
|
+
Process.initgroups(user, gid)
|
|
135
|
+
Process::GID.change_privilege(gid)
|
|
136
|
+
end
|
|
137
|
+
Process.euid != uid and Process::UID.change_privilege(uid)
|
|
138
|
+
@switched = true
|
|
139
|
+
end
|
|
140
|
+
end
|
data/setup.rb
ADDED
|
@@ -0,0 +1,1586 @@
|
|
|
1
|
+
# -*- encoding: binary -*-
|
|
2
|
+
#
|
|
3
|
+
# setup.rb
|
|
4
|
+
#
|
|
5
|
+
# Copyright (c) 2000-2005 Minero Aoki
|
|
6
|
+
#
|
|
7
|
+
# This program is free software.
|
|
8
|
+
# You can distribute/modify this program under the terms of
|
|
9
|
+
# the GNU LGPL, Lesser General Public License version 2.1.
|
|
10
|
+
#
|
|
11
|
+
|
|
12
|
+
unless Enumerable.method_defined?(:map) # Ruby 1.4.6
|
|
13
|
+
module Enumerable
|
|
14
|
+
alias map collect
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
unless File.respond_to?(:read) # Ruby 1.6
|
|
19
|
+
def File.read(fname)
|
|
20
|
+
open(fname) {|f|
|
|
21
|
+
return f.read
|
|
22
|
+
}
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
unless Errno.const_defined?(:ENOTEMPTY) # Windows?
|
|
27
|
+
module Errno
|
|
28
|
+
class ENOTEMPTY
|
|
29
|
+
# We do not raise this exception, implementation is not needed.
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def File.binread(fname)
|
|
35
|
+
open(fname, 'rb') {|f|
|
|
36
|
+
return f.read
|
|
37
|
+
}
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# for corrupted Windows' stat(2)
|
|
41
|
+
def File.dir?(path)
|
|
42
|
+
File.directory?((path[-1,1] == '/') ? path : path + '/')
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class ConfigTable
|
|
47
|
+
|
|
48
|
+
include Enumerable
|
|
49
|
+
|
|
50
|
+
def initialize(rbconfig)
|
|
51
|
+
@rbconfig = rbconfig
|
|
52
|
+
@items = []
|
|
53
|
+
@table = {}
|
|
54
|
+
# options
|
|
55
|
+
@install_prefix = nil
|
|
56
|
+
@config_opt = nil
|
|
57
|
+
@verbose = true
|
|
58
|
+
@no_harm = false
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
attr_accessor :install_prefix
|
|
62
|
+
attr_accessor :config_opt
|
|
63
|
+
|
|
64
|
+
attr_writer :verbose
|
|
65
|
+
|
|
66
|
+
def verbose?
|
|
67
|
+
@verbose
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
attr_writer :no_harm
|
|
71
|
+
|
|
72
|
+
def no_harm?
|
|
73
|
+
@no_harm
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def [](key)
|
|
77
|
+
lookup(key).resolve(self)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def []=(key, val)
|
|
81
|
+
lookup(key).set val
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def names
|
|
85
|
+
@items.map {|i| i.name }
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def each(&block)
|
|
89
|
+
@items.each(&block)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def key?(name)
|
|
93
|
+
@table.key?(name)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def lookup(name)
|
|
97
|
+
@table[name] or setup_rb_error "no such config item: #{name}"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def add(item)
|
|
101
|
+
@items.push item
|
|
102
|
+
@table[item.name] = item
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def remove(name)
|
|
106
|
+
item = lookup(name)
|
|
107
|
+
@items.delete_if {|i| i.name == name }
|
|
108
|
+
@table.delete_if {|name, i| i.name == name }
|
|
109
|
+
item
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def load_script(path, inst = nil)
|
|
113
|
+
if File.file?(path)
|
|
114
|
+
MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def savefile
|
|
119
|
+
'.config'
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def load_savefile
|
|
123
|
+
begin
|
|
124
|
+
File.foreach(savefile()) do |line|
|
|
125
|
+
k, v = *line.split(/=/, 2)
|
|
126
|
+
self[k] = v.strip
|
|
127
|
+
end
|
|
128
|
+
rescue Errno::ENOENT
|
|
129
|
+
setup_rb_error $!.message + "\n#{File.basename($0)} config first"
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def save
|
|
134
|
+
@items.each {|i| i.value }
|
|
135
|
+
File.open(savefile(), 'w') {|f|
|
|
136
|
+
@items.each do |i|
|
|
137
|
+
f.printf "%s=%s\n", i.name, i.value if i.value? and i.value
|
|
138
|
+
end
|
|
139
|
+
}
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def load_standard_entries
|
|
143
|
+
standard_entries(@rbconfig).each do |ent|
|
|
144
|
+
add ent
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def standard_entries(rbconfig)
|
|
149
|
+
c = rbconfig
|
|
150
|
+
|
|
151
|
+
rubypath = File.join(c['bindir'], c['ruby_install_name'] + c['EXEEXT'])
|
|
152
|
+
|
|
153
|
+
major = c['MAJOR'].to_i
|
|
154
|
+
minor = c['MINOR'].to_i
|
|
155
|
+
teeny = c['TEENY'].to_i
|
|
156
|
+
version = "#{major}.#{minor}"
|
|
157
|
+
|
|
158
|
+
# ruby ver. >= 1.4.4?
|
|
159
|
+
newpath_p = ((major >= 2) or
|
|
160
|
+
((major == 1) and
|
|
161
|
+
((minor >= 5) or
|
|
162
|
+
((minor == 4) and (teeny >= 4)))))
|
|
163
|
+
|
|
164
|
+
if c['rubylibdir']
|
|
165
|
+
# V > 1.6.3
|
|
166
|
+
libruby = "#{c['prefix']}/lib/ruby"
|
|
167
|
+
librubyver = c['rubylibdir']
|
|
168
|
+
librubyverarch = c['archdir']
|
|
169
|
+
siteruby = c['sitedir']
|
|
170
|
+
siterubyver = c['sitelibdir']
|
|
171
|
+
siterubyverarch = c['sitearchdir']
|
|
172
|
+
elsif newpath_p
|
|
173
|
+
# 1.4.4 <= V <= 1.6.3
|
|
174
|
+
libruby = "#{c['prefix']}/lib/ruby"
|
|
175
|
+
librubyver = "#{c['prefix']}/lib/ruby/#{version}"
|
|
176
|
+
librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
|
|
177
|
+
siteruby = c['sitedir']
|
|
178
|
+
siterubyver = "$siteruby/#{version}"
|
|
179
|
+
siterubyverarch = "$siterubyver/#{c['arch']}"
|
|
180
|
+
else
|
|
181
|
+
# V < 1.4.4
|
|
182
|
+
libruby = "#{c['prefix']}/lib/ruby"
|
|
183
|
+
librubyver = "#{c['prefix']}/lib/ruby/#{version}"
|
|
184
|
+
librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
|
|
185
|
+
siteruby = "#{c['prefix']}/lib/ruby/#{version}/site_ruby"
|
|
186
|
+
siterubyver = siteruby
|
|
187
|
+
siterubyverarch = "$siterubyver/#{c['arch']}"
|
|
188
|
+
end
|
|
189
|
+
parameterize = lambda {|path|
|
|
190
|
+
path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
|
|
194
|
+
makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
|
|
195
|
+
else
|
|
196
|
+
makeprog = 'make'
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
[
|
|
200
|
+
ExecItem.new('installdirs', 'std/site/home',
|
|
201
|
+
'std: install under libruby; site: install under site_ruby; home: install under $HOME')\
|
|
202
|
+
{|val, table|
|
|
203
|
+
case val
|
|
204
|
+
when 'std'
|
|
205
|
+
table['rbdir'] = '$librubyver'
|
|
206
|
+
table['sodir'] = '$librubyverarch'
|
|
207
|
+
when 'site'
|
|
208
|
+
table['rbdir'] = '$siterubyver'
|
|
209
|
+
table['sodir'] = '$siterubyverarch'
|
|
210
|
+
when 'home'
|
|
211
|
+
setup_rb_error '$HOME was not set' unless ENV['HOME']
|
|
212
|
+
table['prefix'] = ENV['HOME']
|
|
213
|
+
table['rbdir'] = '$libdir/ruby'
|
|
214
|
+
table['sodir'] = '$libdir/ruby'
|
|
215
|
+
end
|
|
216
|
+
},
|
|
217
|
+
PathItem.new('prefix', 'path', c['prefix'],
|
|
218
|
+
'path prefix of target environment'),
|
|
219
|
+
PathItem.new('bindir', 'path', parameterize.call(c['bindir']),
|
|
220
|
+
'the directory for commands'),
|
|
221
|
+
PathItem.new('libdir', 'path', parameterize.call(c['libdir']),
|
|
222
|
+
'the directory for libraries'),
|
|
223
|
+
PathItem.new('datadir', 'path', parameterize.call(c['datadir']),
|
|
224
|
+
'the directory for shared data'),
|
|
225
|
+
PathItem.new('mandir', 'path', parameterize.call(c['mandir']),
|
|
226
|
+
'the directory for man pages'),
|
|
227
|
+
PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']),
|
|
228
|
+
'the directory for system configuration files'),
|
|
229
|
+
PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']),
|
|
230
|
+
'the directory for local state data'),
|
|
231
|
+
PathItem.new('libruby', 'path', libruby,
|
|
232
|
+
'the directory for ruby libraries'),
|
|
233
|
+
PathItem.new('librubyver', 'path', librubyver,
|
|
234
|
+
'the directory for standard ruby libraries'),
|
|
235
|
+
PathItem.new('librubyverarch', 'path', librubyverarch,
|
|
236
|
+
'the directory for standard ruby extensions'),
|
|
237
|
+
PathItem.new('siteruby', 'path', siteruby,
|
|
238
|
+
'the directory for version-independent aux ruby libraries'),
|
|
239
|
+
PathItem.new('siterubyver', 'path', siterubyver,
|
|
240
|
+
'the directory for aux ruby libraries'),
|
|
241
|
+
PathItem.new('siterubyverarch', 'path', siterubyverarch,
|
|
242
|
+
'the directory for aux ruby binaries'),
|
|
243
|
+
PathItem.new('rbdir', 'path', '$siterubyver',
|
|
244
|
+
'the directory for ruby scripts'),
|
|
245
|
+
PathItem.new('sodir', 'path', '$siterubyverarch',
|
|
246
|
+
'the directory for ruby extentions'),
|
|
247
|
+
PathItem.new('rubypath', 'path', rubypath,
|
|
248
|
+
'the path to set to #! line'),
|
|
249
|
+
ProgramItem.new('rubyprog', 'name', rubypath,
|
|
250
|
+
'the ruby program using for installation'),
|
|
251
|
+
ProgramItem.new('makeprog', 'name', makeprog,
|
|
252
|
+
'the make program to compile ruby extentions'),
|
|
253
|
+
SelectItem.new('shebang', 'all/ruby/never', 'ruby',
|
|
254
|
+
'shebang line (#!) editing mode'),
|
|
255
|
+
BoolItem.new('without-ext', 'yes/no', 'no',
|
|
256
|
+
'does not compile/install ruby extentions')
|
|
257
|
+
]
|
|
258
|
+
end
|
|
259
|
+
private :standard_entries
|
|
260
|
+
|
|
261
|
+
def load_multipackage_entries
|
|
262
|
+
multipackage_entries().each do |ent|
|
|
263
|
+
add ent
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def multipackage_entries
|
|
268
|
+
[
|
|
269
|
+
PackageSelectionItem.new('with', 'name,name...', '', 'ALL',
|
|
270
|
+
'package names that you want to install'),
|
|
271
|
+
PackageSelectionItem.new('without', 'name,name...', '', 'NONE',
|
|
272
|
+
'package names that you do not want to install')
|
|
273
|
+
]
|
|
274
|
+
end
|
|
275
|
+
private :multipackage_entries
|
|
276
|
+
|
|
277
|
+
ALIASES = {
|
|
278
|
+
'std-ruby' => 'librubyver',
|
|
279
|
+
'stdruby' => 'librubyver',
|
|
280
|
+
'rubylibdir' => 'librubyver',
|
|
281
|
+
'archdir' => 'librubyverarch',
|
|
282
|
+
'site-ruby-common' => 'siteruby', # For backward compatibility
|
|
283
|
+
'site-ruby' => 'siterubyver', # For backward compatibility
|
|
284
|
+
'bin-dir' => 'bindir',
|
|
285
|
+
'bin-dir' => 'bindir',
|
|
286
|
+
'rb-dir' => 'rbdir',
|
|
287
|
+
'so-dir' => 'sodir',
|
|
288
|
+
'data-dir' => 'datadir',
|
|
289
|
+
'ruby-path' => 'rubypath',
|
|
290
|
+
'ruby-prog' => 'rubyprog',
|
|
291
|
+
'ruby' => 'rubyprog',
|
|
292
|
+
'make-prog' => 'makeprog',
|
|
293
|
+
'make' => 'makeprog'
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
def fixup
|
|
297
|
+
ALIASES.each do |ali, name|
|
|
298
|
+
@table[ali] = @table[name]
|
|
299
|
+
end
|
|
300
|
+
@items.freeze
|
|
301
|
+
@table.freeze
|
|
302
|
+
@options_re = /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
def parse_opt(opt)
|
|
306
|
+
m = @options_re.match(opt) or setup_rb_error "config: unknown option #{opt}"
|
|
307
|
+
m.to_a[1,2]
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def dllext
|
|
311
|
+
@rbconfig['DLEXT']
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
def value_config?(name)
|
|
315
|
+
lookup(name).value?
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
class Item
|
|
319
|
+
def initialize(name, template, default, desc)
|
|
320
|
+
@name = name.freeze
|
|
321
|
+
@template = template
|
|
322
|
+
@value = default
|
|
323
|
+
@default = default
|
|
324
|
+
@description = desc
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
attr_reader :name
|
|
328
|
+
attr_reader :description
|
|
329
|
+
|
|
330
|
+
attr_accessor :default
|
|
331
|
+
alias help_default default
|
|
332
|
+
|
|
333
|
+
def help_opt
|
|
334
|
+
"--#{@name}=#{@template}"
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
def value?
|
|
338
|
+
true
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
def value
|
|
342
|
+
@value
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
def resolve(table)
|
|
346
|
+
@value.gsub(%r<\$([^/]+)>) { table[$1] }
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
def set(val)
|
|
350
|
+
@value = check(val)
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
private
|
|
354
|
+
|
|
355
|
+
def check(val)
|
|
356
|
+
setup_rb_error "config: --#{name} requires argument" unless val
|
|
357
|
+
val
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
class BoolItem < Item
|
|
362
|
+
def config_type
|
|
363
|
+
'bool'
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
def help_opt
|
|
367
|
+
"--#{@name}"
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
private
|
|
371
|
+
|
|
372
|
+
def check(val)
|
|
373
|
+
return 'yes' unless val
|
|
374
|
+
case val
|
|
375
|
+
when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes'
|
|
376
|
+
when /\An(o)?\z/i, /\Af(alse)\z/i then 'no'
|
|
377
|
+
else
|
|
378
|
+
setup_rb_error "config: --#{@name} accepts only yes/no for argument"
|
|
379
|
+
end
|
|
380
|
+
end
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
class PathItem < Item
|
|
384
|
+
def config_type
|
|
385
|
+
'path'
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
private
|
|
389
|
+
|
|
390
|
+
def check(path)
|
|
391
|
+
setup_rb_error "config: --#{@name} requires argument" unless path
|
|
392
|
+
path[0,1] == '$' ? path : File.expand_path(path)
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
class ProgramItem < Item
|
|
397
|
+
def config_type
|
|
398
|
+
'program'
|
|
399
|
+
end
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
class SelectItem < Item
|
|
403
|
+
def initialize(name, selection, default, desc)
|
|
404
|
+
super
|
|
405
|
+
@ok = selection.split('/')
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
def config_type
|
|
409
|
+
'select'
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
private
|
|
413
|
+
|
|
414
|
+
def check(val)
|
|
415
|
+
unless @ok.include?(val.strip)
|
|
416
|
+
setup_rb_error "config: use --#{@name}=#{@template} (#{val})"
|
|
417
|
+
end
|
|
418
|
+
val.strip
|
|
419
|
+
end
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
class ExecItem < Item
|
|
423
|
+
def initialize(name, selection, desc, &block)
|
|
424
|
+
super name, selection, nil, desc
|
|
425
|
+
@ok = selection.split('/')
|
|
426
|
+
@action = block
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
def config_type
|
|
430
|
+
'exec'
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
def value?
|
|
434
|
+
false
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
def resolve(table)
|
|
438
|
+
setup_rb_error "$#{name()} wrongly used as option value"
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
undef set
|
|
442
|
+
|
|
443
|
+
def evaluate(val, table)
|
|
444
|
+
v = val.strip.downcase
|
|
445
|
+
unless @ok.include?(v)
|
|
446
|
+
setup_rb_error "invalid option --#{@name}=#{val} (use #{@template})"
|
|
447
|
+
end
|
|
448
|
+
@action.call v, table
|
|
449
|
+
end
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
class PackageSelectionItem < Item
|
|
453
|
+
def initialize(name, template, default, help_default, desc)
|
|
454
|
+
super name, template, default, desc
|
|
455
|
+
@help_default = help_default
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
attr_reader :help_default
|
|
459
|
+
|
|
460
|
+
def config_type
|
|
461
|
+
'package'
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
private
|
|
465
|
+
|
|
466
|
+
def check(val)
|
|
467
|
+
unless File.dir?("packages/#{val}")
|
|
468
|
+
setup_rb_error "config: no such package: #{val}"
|
|
469
|
+
end
|
|
470
|
+
val
|
|
471
|
+
end
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
class MetaConfigEnvironment
|
|
475
|
+
def initialize(config, installer)
|
|
476
|
+
@config = config
|
|
477
|
+
@installer = installer
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
def config_names
|
|
481
|
+
@config.names
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
def config?(name)
|
|
485
|
+
@config.key?(name)
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
def bool_config?(name)
|
|
489
|
+
@config.lookup(name).config_type == 'bool'
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
def path_config?(name)
|
|
493
|
+
@config.lookup(name).config_type == 'path'
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
def value_config?(name)
|
|
497
|
+
@config.lookup(name).config_type != 'exec'
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
def add_config(item)
|
|
501
|
+
@config.add item
|
|
502
|
+
end
|
|
503
|
+
|
|
504
|
+
def add_bool_config(name, default, desc)
|
|
505
|
+
@config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc)
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
def add_path_config(name, default, desc)
|
|
509
|
+
@config.add PathItem.new(name, 'path', default, desc)
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
def set_config_default(name, default)
|
|
513
|
+
@config.lookup(name).default = default
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
def remove_config(name)
|
|
517
|
+
@config.remove(name)
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
# For only multipackage
|
|
521
|
+
def packages
|
|
522
|
+
raise '[setup.rb fatal] multi-package metaconfig API packages() called for single-package; contact application package vendor' unless @installer
|
|
523
|
+
@installer.packages
|
|
524
|
+
end
|
|
525
|
+
|
|
526
|
+
# For only multipackage
|
|
527
|
+
def declare_packages(list)
|
|
528
|
+
raise '[setup.rb fatal] multi-package metaconfig API declare_packages() called for single-package; contact application package vendor' unless @installer
|
|
529
|
+
@installer.packages = list
|
|
530
|
+
end
|
|
531
|
+
end
|
|
532
|
+
|
|
533
|
+
end # class ConfigTable
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
# This module requires: #verbose?, #no_harm?
|
|
537
|
+
module FileOperations
|
|
538
|
+
|
|
539
|
+
def mkdir_p(dirname, prefix = nil)
|
|
540
|
+
dirname = prefix + File.expand_path(dirname) if prefix
|
|
541
|
+
$stderr.puts "mkdir -p #{dirname}" if verbose?
|
|
542
|
+
return if no_harm?
|
|
543
|
+
|
|
544
|
+
# Does not check '/', it's too abnormal.
|
|
545
|
+
dirs = File.expand_path(dirname).split(%r<(?=/)>)
|
|
546
|
+
if /\A[a-z]:\z/i =~ dirs[0]
|
|
547
|
+
disk = dirs.shift
|
|
548
|
+
dirs[0] = disk + dirs[0]
|
|
549
|
+
end
|
|
550
|
+
dirs.each_index do |idx|
|
|
551
|
+
path = dirs[0..idx].join('')
|
|
552
|
+
Dir.mkdir path unless File.dir?(path)
|
|
553
|
+
end
|
|
554
|
+
end
|
|
555
|
+
|
|
556
|
+
def rm_f(path)
|
|
557
|
+
$stderr.puts "rm -f #{path}" if verbose?
|
|
558
|
+
return if no_harm?
|
|
559
|
+
force_remove_file path
|
|
560
|
+
end
|
|
561
|
+
|
|
562
|
+
def rm_rf(path)
|
|
563
|
+
$stderr.puts "rm -rf #{path}" if verbose?
|
|
564
|
+
return if no_harm?
|
|
565
|
+
remove_tree path
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
def remove_tree(path)
|
|
569
|
+
if File.symlink?(path)
|
|
570
|
+
remove_file path
|
|
571
|
+
elsif File.dir?(path)
|
|
572
|
+
remove_tree0 path
|
|
573
|
+
else
|
|
574
|
+
force_remove_file path
|
|
575
|
+
end
|
|
576
|
+
end
|
|
577
|
+
|
|
578
|
+
def remove_tree0(path)
|
|
579
|
+
Dir.foreach(path) do |ent|
|
|
580
|
+
next if ent == '.'
|
|
581
|
+
next if ent == '..'
|
|
582
|
+
entpath = "#{path}/#{ent}"
|
|
583
|
+
if File.symlink?(entpath)
|
|
584
|
+
remove_file entpath
|
|
585
|
+
elsif File.dir?(entpath)
|
|
586
|
+
remove_tree0 entpath
|
|
587
|
+
else
|
|
588
|
+
force_remove_file entpath
|
|
589
|
+
end
|
|
590
|
+
end
|
|
591
|
+
begin
|
|
592
|
+
Dir.rmdir path
|
|
593
|
+
rescue Errno::ENOTEMPTY
|
|
594
|
+
# directory may not be empty
|
|
595
|
+
end
|
|
596
|
+
end
|
|
597
|
+
|
|
598
|
+
def move_file(src, dest)
|
|
599
|
+
force_remove_file dest
|
|
600
|
+
begin
|
|
601
|
+
File.rename src, dest
|
|
602
|
+
rescue
|
|
603
|
+
File.open(dest, 'wb') {|f|
|
|
604
|
+
f.write File.binread(src)
|
|
605
|
+
}
|
|
606
|
+
File.chmod File.stat(src).mode, dest
|
|
607
|
+
File.unlink src
|
|
608
|
+
end
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
def force_remove_file(path)
|
|
612
|
+
begin
|
|
613
|
+
remove_file path
|
|
614
|
+
rescue
|
|
615
|
+
end
|
|
616
|
+
end
|
|
617
|
+
|
|
618
|
+
def remove_file(path)
|
|
619
|
+
File.chmod 0777, path
|
|
620
|
+
File.unlink path
|
|
621
|
+
end
|
|
622
|
+
|
|
623
|
+
def install(from, dest, mode, prefix = nil)
|
|
624
|
+
$stderr.puts "install #{from} #{dest}" if verbose?
|
|
625
|
+
return if no_harm?
|
|
626
|
+
|
|
627
|
+
realdest = prefix ? prefix + File.expand_path(dest) : dest
|
|
628
|
+
realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)
|
|
629
|
+
str = File.binread(from)
|
|
630
|
+
if diff?(str, realdest)
|
|
631
|
+
verbose_off {
|
|
632
|
+
rm_f realdest if File.exist?(realdest)
|
|
633
|
+
}
|
|
634
|
+
File.open(realdest, 'wb') {|f|
|
|
635
|
+
f.write str
|
|
636
|
+
}
|
|
637
|
+
File.chmod mode, realdest
|
|
638
|
+
|
|
639
|
+
File.open("#{objdir_root()}/InstalledFiles", 'a') {|f|
|
|
640
|
+
if prefix
|
|
641
|
+
f.puts realdest.sub(prefix, '')
|
|
642
|
+
else
|
|
643
|
+
f.puts realdest
|
|
644
|
+
end
|
|
645
|
+
}
|
|
646
|
+
end
|
|
647
|
+
end
|
|
648
|
+
|
|
649
|
+
def diff?(new_content, path)
|
|
650
|
+
return true unless File.exist?(path)
|
|
651
|
+
new_content != File.binread(path)
|
|
652
|
+
end
|
|
653
|
+
|
|
654
|
+
def command(*args)
|
|
655
|
+
$stderr.puts args.join(' ') if verbose?
|
|
656
|
+
system(*args) or raise RuntimeError,
|
|
657
|
+
"system(#{args.map{|a| a.inspect }.join(' ')}) failed"
|
|
658
|
+
end
|
|
659
|
+
|
|
660
|
+
def ruby(*args)
|
|
661
|
+
command config('rubyprog'), *args
|
|
662
|
+
end
|
|
663
|
+
|
|
664
|
+
def make(task = nil)
|
|
665
|
+
command(*[config('makeprog'), task].compact)
|
|
666
|
+
end
|
|
667
|
+
|
|
668
|
+
def extdir?(dir)
|
|
669
|
+
File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb")
|
|
670
|
+
end
|
|
671
|
+
|
|
672
|
+
def files_of(dir)
|
|
673
|
+
Dir.open(dir) {|d|
|
|
674
|
+
return d.select {|ent| File.file?("#{dir}/#{ent}") }
|
|
675
|
+
}
|
|
676
|
+
end
|
|
677
|
+
|
|
678
|
+
DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn )
|
|
679
|
+
|
|
680
|
+
def directories_of(dir)
|
|
681
|
+
Dir.open(dir) {|d|
|
|
682
|
+
return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT
|
|
683
|
+
}
|
|
684
|
+
end
|
|
685
|
+
|
|
686
|
+
end
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
# This module requires: #srcdir_root, #objdir_root, #relpath
|
|
690
|
+
module HookScriptAPI
|
|
691
|
+
|
|
692
|
+
def get_config(key)
|
|
693
|
+
@config[key]
|
|
694
|
+
end
|
|
695
|
+
|
|
696
|
+
alias config get_config
|
|
697
|
+
|
|
698
|
+
# obsolete: use metaconfig to change configuration
|
|
699
|
+
def set_config(key, val)
|
|
700
|
+
@config[key] = val
|
|
701
|
+
end
|
|
702
|
+
|
|
703
|
+
#
|
|
704
|
+
# srcdir/objdir (works only in the package directory)
|
|
705
|
+
#
|
|
706
|
+
|
|
707
|
+
def curr_srcdir
|
|
708
|
+
"#{srcdir_root()}/#{relpath()}"
|
|
709
|
+
end
|
|
710
|
+
|
|
711
|
+
def curr_objdir
|
|
712
|
+
"#{objdir_root()}/#{relpath()}"
|
|
713
|
+
end
|
|
714
|
+
|
|
715
|
+
def srcfile(path)
|
|
716
|
+
"#{curr_srcdir()}/#{path}"
|
|
717
|
+
end
|
|
718
|
+
|
|
719
|
+
def srcexist?(path)
|
|
720
|
+
File.exist?(srcfile(path))
|
|
721
|
+
end
|
|
722
|
+
|
|
723
|
+
def srcdirectory?(path)
|
|
724
|
+
File.dir?(srcfile(path))
|
|
725
|
+
end
|
|
726
|
+
|
|
727
|
+
def srcfile?(path)
|
|
728
|
+
File.file?(srcfile(path))
|
|
729
|
+
end
|
|
730
|
+
|
|
731
|
+
def srcentries(path = '.')
|
|
732
|
+
Dir.open("#{curr_srcdir()}/#{path}") {|d|
|
|
733
|
+
return d.to_a - %w(. ..)
|
|
734
|
+
}
|
|
735
|
+
end
|
|
736
|
+
|
|
737
|
+
def srcfiles(path = '.')
|
|
738
|
+
srcentries(path).select {|fname|
|
|
739
|
+
File.file?(File.join(curr_srcdir(), path, fname))
|
|
740
|
+
}
|
|
741
|
+
end
|
|
742
|
+
|
|
743
|
+
def srcdirectories(path = '.')
|
|
744
|
+
srcentries(path).select {|fname|
|
|
745
|
+
File.dir?(File.join(curr_srcdir(), path, fname))
|
|
746
|
+
}
|
|
747
|
+
end
|
|
748
|
+
|
|
749
|
+
end
|
|
750
|
+
|
|
751
|
+
|
|
752
|
+
class ToplevelInstaller
|
|
753
|
+
|
|
754
|
+
Version = '3.4.1'
|
|
755
|
+
Copyright = 'Copyright (c) 2000-2005 Minero Aoki'
|
|
756
|
+
|
|
757
|
+
TASKS = [
|
|
758
|
+
[ 'all', 'do config, setup, then install' ],
|
|
759
|
+
[ 'config', 'saves your configurations' ],
|
|
760
|
+
[ 'show', 'shows current configuration' ],
|
|
761
|
+
[ 'setup', 'compiles ruby extentions and others' ],
|
|
762
|
+
[ 'install', 'installs files' ],
|
|
763
|
+
[ 'test', 'run all tests in test/' ],
|
|
764
|
+
[ 'clean', "does `make clean' for each extention" ],
|
|
765
|
+
[ 'distclean',"does `make distclean' for each extention" ]
|
|
766
|
+
]
|
|
767
|
+
|
|
768
|
+
def ToplevelInstaller.invoke
|
|
769
|
+
config = ConfigTable.new(load_rbconfig())
|
|
770
|
+
config.load_standard_entries
|
|
771
|
+
config.load_multipackage_entries if multipackage?
|
|
772
|
+
config.fixup
|
|
773
|
+
klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller)
|
|
774
|
+
klass.new(File.dirname($0), config).invoke
|
|
775
|
+
end
|
|
776
|
+
|
|
777
|
+
def ToplevelInstaller.multipackage?
|
|
778
|
+
File.dir?(File.dirname($0) + '/packages')
|
|
779
|
+
end
|
|
780
|
+
|
|
781
|
+
def ToplevelInstaller.load_rbconfig
|
|
782
|
+
if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }
|
|
783
|
+
ARGV.delete(arg)
|
|
784
|
+
load File.expand_path(arg.split(/=/, 2)[1])
|
|
785
|
+
$".push 'rbconfig.rb'
|
|
786
|
+
else
|
|
787
|
+
require 'rbconfig'
|
|
788
|
+
end
|
|
789
|
+
::Config::CONFIG
|
|
790
|
+
end
|
|
791
|
+
|
|
792
|
+
def initialize(ardir_root, config)
|
|
793
|
+
@ardir = File.expand_path(ardir_root)
|
|
794
|
+
@config = config
|
|
795
|
+
# cache
|
|
796
|
+
@valid_task_re = nil
|
|
797
|
+
end
|
|
798
|
+
|
|
799
|
+
def config(key)
|
|
800
|
+
@config[key]
|
|
801
|
+
end
|
|
802
|
+
|
|
803
|
+
def inspect
|
|
804
|
+
"#<#{self.class} #{__id__()}>"
|
|
805
|
+
end
|
|
806
|
+
|
|
807
|
+
def invoke
|
|
808
|
+
run_metaconfigs
|
|
809
|
+
case task = parsearg_global()
|
|
810
|
+
when nil, 'all'
|
|
811
|
+
parsearg_config
|
|
812
|
+
init_installers
|
|
813
|
+
exec_config
|
|
814
|
+
exec_setup
|
|
815
|
+
exec_install
|
|
816
|
+
else
|
|
817
|
+
case task
|
|
818
|
+
when 'config', 'test'
|
|
819
|
+
;
|
|
820
|
+
when 'clean', 'distclean'
|
|
821
|
+
@config.load_savefile if File.exist?(@config.savefile)
|
|
822
|
+
else
|
|
823
|
+
@config.load_savefile
|
|
824
|
+
end
|
|
825
|
+
__send__ "parsearg_#{task}"
|
|
826
|
+
init_installers
|
|
827
|
+
__send__ "exec_#{task}"
|
|
828
|
+
end
|
|
829
|
+
end
|
|
830
|
+
|
|
831
|
+
def run_metaconfigs
|
|
832
|
+
@config.load_script "#{@ardir}/metaconfig"
|
|
833
|
+
end
|
|
834
|
+
|
|
835
|
+
def init_installers
|
|
836
|
+
@installer = Installer.new(@config, @ardir, File.expand_path('.'))
|
|
837
|
+
end
|
|
838
|
+
|
|
839
|
+
#
|
|
840
|
+
# Hook Script API bases
|
|
841
|
+
#
|
|
842
|
+
|
|
843
|
+
def srcdir_root
|
|
844
|
+
@ardir
|
|
845
|
+
end
|
|
846
|
+
|
|
847
|
+
def objdir_root
|
|
848
|
+
'.'
|
|
849
|
+
end
|
|
850
|
+
|
|
851
|
+
def relpath
|
|
852
|
+
'.'
|
|
853
|
+
end
|
|
854
|
+
|
|
855
|
+
#
|
|
856
|
+
# Option Parsing
|
|
857
|
+
#
|
|
858
|
+
|
|
859
|
+
def parsearg_global
|
|
860
|
+
while arg = ARGV.shift
|
|
861
|
+
case arg
|
|
862
|
+
when /\A\w+\z/
|
|
863
|
+
setup_rb_error "invalid task: #{arg}" unless valid_task?(arg)
|
|
864
|
+
return arg
|
|
865
|
+
when '-q', '--quiet'
|
|
866
|
+
@config.verbose = false
|
|
867
|
+
when '--verbose'
|
|
868
|
+
@config.verbose = true
|
|
869
|
+
when '--help'
|
|
870
|
+
print_usage $stdout
|
|
871
|
+
exit 0
|
|
872
|
+
when '--version'
|
|
873
|
+
puts "#{File.basename($0)} version #{Version}"
|
|
874
|
+
exit 0
|
|
875
|
+
when '--copyright'
|
|
876
|
+
puts Copyright
|
|
877
|
+
exit 0
|
|
878
|
+
else
|
|
879
|
+
setup_rb_error "unknown global option '#{arg}'"
|
|
880
|
+
end
|
|
881
|
+
end
|
|
882
|
+
nil
|
|
883
|
+
end
|
|
884
|
+
|
|
885
|
+
def valid_task?(t)
|
|
886
|
+
valid_task_re() =~ t
|
|
887
|
+
end
|
|
888
|
+
|
|
889
|
+
def valid_task_re
|
|
890
|
+
@valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/
|
|
891
|
+
end
|
|
892
|
+
|
|
893
|
+
def parsearg_no_options
|
|
894
|
+
unless ARGV.empty?
|
|
895
|
+
task = caller(0).first.slice(%r<`parsearg_(\w+)'>, 1)
|
|
896
|
+
setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}"
|
|
897
|
+
end
|
|
898
|
+
end
|
|
899
|
+
|
|
900
|
+
alias parsearg_show parsearg_no_options
|
|
901
|
+
alias parsearg_setup parsearg_no_options
|
|
902
|
+
alias parsearg_test parsearg_no_options
|
|
903
|
+
alias parsearg_clean parsearg_no_options
|
|
904
|
+
alias parsearg_distclean parsearg_no_options
|
|
905
|
+
|
|
906
|
+
def parsearg_config
|
|
907
|
+
evalopt = []
|
|
908
|
+
set = []
|
|
909
|
+
@config.config_opt = []
|
|
910
|
+
while i = ARGV.shift
|
|
911
|
+
if /\A--?\z/ =~ i
|
|
912
|
+
@config.config_opt = ARGV.dup
|
|
913
|
+
break
|
|
914
|
+
end
|
|
915
|
+
name, value = *@config.parse_opt(i)
|
|
916
|
+
if @config.value_config?(name)
|
|
917
|
+
@config[name] = value
|
|
918
|
+
else
|
|
919
|
+
evalopt.push [name, value]
|
|
920
|
+
end
|
|
921
|
+
set.push name
|
|
922
|
+
end
|
|
923
|
+
evalopt.each do |name, value|
|
|
924
|
+
@config.lookup(name).evaluate value, @config
|
|
925
|
+
end
|
|
926
|
+
# Check if configuration is valid
|
|
927
|
+
set.each do |n|
|
|
928
|
+
@config[n] if @config.value_config?(n)
|
|
929
|
+
end
|
|
930
|
+
end
|
|
931
|
+
|
|
932
|
+
def parsearg_install
|
|
933
|
+
@config.no_harm = false
|
|
934
|
+
@config.install_prefix = ''
|
|
935
|
+
while a = ARGV.shift
|
|
936
|
+
case a
|
|
937
|
+
when '--no-harm'
|
|
938
|
+
@config.no_harm = true
|
|
939
|
+
when /\A--prefix=/
|
|
940
|
+
path = a.split(/=/, 2)[1]
|
|
941
|
+
path = File.expand_path(path) unless path[0,1] == '/'
|
|
942
|
+
@config.install_prefix = path
|
|
943
|
+
else
|
|
944
|
+
setup_rb_error "install: unknown option #{a}"
|
|
945
|
+
end
|
|
946
|
+
end
|
|
947
|
+
end
|
|
948
|
+
|
|
949
|
+
def print_usage(out)
|
|
950
|
+
out.puts 'Typical Installation Procedure:'
|
|
951
|
+
out.puts " $ ruby #{File.basename $0} config"
|
|
952
|
+
out.puts " $ ruby #{File.basename $0} setup"
|
|
953
|
+
out.puts " # ruby #{File.basename $0} install (may require root privilege)"
|
|
954
|
+
out.puts
|
|
955
|
+
out.puts 'Detailed Usage:'
|
|
956
|
+
out.puts " ruby #{File.basename $0} <global option>"
|
|
957
|
+
out.puts " ruby #{File.basename $0} [<global options>] <task> [<task options>]"
|
|
958
|
+
|
|
959
|
+
fmt = " %-24s %s\n"
|
|
960
|
+
out.puts
|
|
961
|
+
out.puts 'Global options:'
|
|
962
|
+
out.printf fmt, '-q,--quiet', 'suppress message outputs'
|
|
963
|
+
out.printf fmt, ' --verbose', 'output messages verbosely'
|
|
964
|
+
out.printf fmt, ' --help', 'print this message'
|
|
965
|
+
out.printf fmt, ' --version', 'print version and quit'
|
|
966
|
+
out.printf fmt, ' --copyright', 'print copyright and quit'
|
|
967
|
+
out.puts
|
|
968
|
+
out.puts 'Tasks:'
|
|
969
|
+
TASKS.each do |name, desc|
|
|
970
|
+
out.printf fmt, name, desc
|
|
971
|
+
end
|
|
972
|
+
|
|
973
|
+
fmt = " %-24s %s [%s]\n"
|
|
974
|
+
out.puts
|
|
975
|
+
out.puts 'Options for CONFIG or ALL:'
|
|
976
|
+
@config.each do |item|
|
|
977
|
+
out.printf fmt, item.help_opt, item.description, item.help_default
|
|
978
|
+
end
|
|
979
|
+
out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's"
|
|
980
|
+
out.puts
|
|
981
|
+
out.puts 'Options for INSTALL:'
|
|
982
|
+
out.printf fmt, '--no-harm', 'only display what to do if given', 'off'
|
|
983
|
+
out.printf fmt, '--prefix=path', 'install path prefix', ''
|
|
984
|
+
out.puts
|
|
985
|
+
end
|
|
986
|
+
|
|
987
|
+
#
|
|
988
|
+
# Task Handlers
|
|
989
|
+
#
|
|
990
|
+
|
|
991
|
+
def exec_config
|
|
992
|
+
@installer.exec_config
|
|
993
|
+
@config.save # must be final
|
|
994
|
+
end
|
|
995
|
+
|
|
996
|
+
def exec_setup
|
|
997
|
+
@installer.exec_setup
|
|
998
|
+
end
|
|
999
|
+
|
|
1000
|
+
def exec_install
|
|
1001
|
+
@installer.exec_install
|
|
1002
|
+
end
|
|
1003
|
+
|
|
1004
|
+
def exec_test
|
|
1005
|
+
@installer.exec_test
|
|
1006
|
+
end
|
|
1007
|
+
|
|
1008
|
+
def exec_show
|
|
1009
|
+
@config.each do |i|
|
|
1010
|
+
printf "%-20s %s\n", i.name, i.value if i.value?
|
|
1011
|
+
end
|
|
1012
|
+
end
|
|
1013
|
+
|
|
1014
|
+
def exec_clean
|
|
1015
|
+
@installer.exec_clean
|
|
1016
|
+
end
|
|
1017
|
+
|
|
1018
|
+
def exec_distclean
|
|
1019
|
+
@installer.exec_distclean
|
|
1020
|
+
end
|
|
1021
|
+
|
|
1022
|
+
end # class ToplevelInstaller
|
|
1023
|
+
|
|
1024
|
+
|
|
1025
|
+
class ToplevelInstallerMulti < ToplevelInstaller
|
|
1026
|
+
|
|
1027
|
+
include FileOperations
|
|
1028
|
+
|
|
1029
|
+
def initialize(ardir_root, config)
|
|
1030
|
+
super
|
|
1031
|
+
@packages = directories_of("#{@ardir}/packages")
|
|
1032
|
+
raise 'no package exists' if @packages.empty?
|
|
1033
|
+
@root_installer = Installer.new(@config, @ardir, File.expand_path('.'))
|
|
1034
|
+
end
|
|
1035
|
+
|
|
1036
|
+
def run_metaconfigs
|
|
1037
|
+
@config.load_script "#{@ardir}/metaconfig", self
|
|
1038
|
+
@packages.each do |name|
|
|
1039
|
+
@config.load_script "#{@ardir}/packages/#{name}/metaconfig"
|
|
1040
|
+
end
|
|
1041
|
+
end
|
|
1042
|
+
|
|
1043
|
+
attr_reader :packages
|
|
1044
|
+
|
|
1045
|
+
def packages=(list)
|
|
1046
|
+
raise 'package list is empty' if list.empty?
|
|
1047
|
+
list.each do |name|
|
|
1048
|
+
raise "directory packages/#{name} does not exist"\
|
|
1049
|
+
unless File.dir?("#{@ardir}/packages/#{name}")
|
|
1050
|
+
end
|
|
1051
|
+
@packages = list
|
|
1052
|
+
end
|
|
1053
|
+
|
|
1054
|
+
def init_installers
|
|
1055
|
+
@installers = {}
|
|
1056
|
+
@packages.each do |pack|
|
|
1057
|
+
@installers[pack] = Installer.new(@config,
|
|
1058
|
+
"#{@ardir}/packages/#{pack}",
|
|
1059
|
+
"packages/#{pack}")
|
|
1060
|
+
end
|
|
1061
|
+
with = extract_selection(config('with'))
|
|
1062
|
+
without = extract_selection(config('without'))
|
|
1063
|
+
@selected = @installers.keys.select {|name|
|
|
1064
|
+
(with.empty? or with.include?(name)) \
|
|
1065
|
+
and not without.include?(name)
|
|
1066
|
+
}
|
|
1067
|
+
end
|
|
1068
|
+
|
|
1069
|
+
def extract_selection(list)
|
|
1070
|
+
a = list.split(/,/)
|
|
1071
|
+
a.each do |name|
|
|
1072
|
+
setup_rb_error "no such package: #{name}" unless @installers.key?(name)
|
|
1073
|
+
end
|
|
1074
|
+
a
|
|
1075
|
+
end
|
|
1076
|
+
|
|
1077
|
+
def print_usage(f)
|
|
1078
|
+
super
|
|
1079
|
+
f.puts 'Inluded packages:'
|
|
1080
|
+
f.puts ' ' + @packages.sort.join(' ')
|
|
1081
|
+
f.puts
|
|
1082
|
+
end
|
|
1083
|
+
|
|
1084
|
+
#
|
|
1085
|
+
# Task Handlers
|
|
1086
|
+
#
|
|
1087
|
+
|
|
1088
|
+
def exec_config
|
|
1089
|
+
run_hook 'pre-config'
|
|
1090
|
+
each_selected_installers {|inst| inst.exec_config }
|
|
1091
|
+
run_hook 'post-config'
|
|
1092
|
+
@config.save # must be final
|
|
1093
|
+
end
|
|
1094
|
+
|
|
1095
|
+
def exec_setup
|
|
1096
|
+
run_hook 'pre-setup'
|
|
1097
|
+
each_selected_installers {|inst| inst.exec_setup }
|
|
1098
|
+
run_hook 'post-setup'
|
|
1099
|
+
end
|
|
1100
|
+
|
|
1101
|
+
def exec_install
|
|
1102
|
+
run_hook 'pre-install'
|
|
1103
|
+
each_selected_installers {|inst| inst.exec_install }
|
|
1104
|
+
run_hook 'post-install'
|
|
1105
|
+
end
|
|
1106
|
+
|
|
1107
|
+
def exec_test
|
|
1108
|
+
run_hook 'pre-test'
|
|
1109
|
+
each_selected_installers {|inst| inst.exec_test }
|
|
1110
|
+
run_hook 'post-test'
|
|
1111
|
+
end
|
|
1112
|
+
|
|
1113
|
+
def exec_clean
|
|
1114
|
+
rm_f @config.savefile
|
|
1115
|
+
run_hook 'pre-clean'
|
|
1116
|
+
each_selected_installers {|inst| inst.exec_clean }
|
|
1117
|
+
run_hook 'post-clean'
|
|
1118
|
+
end
|
|
1119
|
+
|
|
1120
|
+
def exec_distclean
|
|
1121
|
+
rm_f @config.savefile
|
|
1122
|
+
run_hook 'pre-distclean'
|
|
1123
|
+
each_selected_installers {|inst| inst.exec_distclean }
|
|
1124
|
+
run_hook 'post-distclean'
|
|
1125
|
+
end
|
|
1126
|
+
|
|
1127
|
+
#
|
|
1128
|
+
# lib
|
|
1129
|
+
#
|
|
1130
|
+
|
|
1131
|
+
def each_selected_installers
|
|
1132
|
+
Dir.mkdir 'packages' unless File.dir?('packages')
|
|
1133
|
+
@selected.each do |pack|
|
|
1134
|
+
$stderr.puts "Processing the package `#{pack}' ..." if verbose?
|
|
1135
|
+
Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")
|
|
1136
|
+
Dir.chdir "packages/#{pack}"
|
|
1137
|
+
yield @installers[pack]
|
|
1138
|
+
Dir.chdir '../..'
|
|
1139
|
+
end
|
|
1140
|
+
end
|
|
1141
|
+
|
|
1142
|
+
def run_hook(id)
|
|
1143
|
+
@root_installer.run_hook id
|
|
1144
|
+
end
|
|
1145
|
+
|
|
1146
|
+
# module FileOperations requires this
|
|
1147
|
+
def verbose?
|
|
1148
|
+
@config.verbose?
|
|
1149
|
+
end
|
|
1150
|
+
|
|
1151
|
+
# module FileOperations requires this
|
|
1152
|
+
def no_harm?
|
|
1153
|
+
@config.no_harm?
|
|
1154
|
+
end
|
|
1155
|
+
|
|
1156
|
+
end # class ToplevelInstallerMulti
|
|
1157
|
+
|
|
1158
|
+
|
|
1159
|
+
class Installer
|
|
1160
|
+
|
|
1161
|
+
FILETYPES = %w( bin lib ext data conf man )
|
|
1162
|
+
|
|
1163
|
+
include FileOperations
|
|
1164
|
+
include HookScriptAPI
|
|
1165
|
+
|
|
1166
|
+
def initialize(config, srcroot, objroot)
|
|
1167
|
+
@config = config
|
|
1168
|
+
@srcdir = File.expand_path(srcroot)
|
|
1169
|
+
@objdir = File.expand_path(objroot)
|
|
1170
|
+
@currdir = '.'
|
|
1171
|
+
end
|
|
1172
|
+
|
|
1173
|
+
def inspect
|
|
1174
|
+
"#<#{self.class} #{File.basename(@srcdir)}>"
|
|
1175
|
+
end
|
|
1176
|
+
|
|
1177
|
+
def noop(rel)
|
|
1178
|
+
end
|
|
1179
|
+
|
|
1180
|
+
#
|
|
1181
|
+
# Hook Script API base methods
|
|
1182
|
+
#
|
|
1183
|
+
|
|
1184
|
+
def srcdir_root
|
|
1185
|
+
@srcdir
|
|
1186
|
+
end
|
|
1187
|
+
|
|
1188
|
+
def objdir_root
|
|
1189
|
+
@objdir
|
|
1190
|
+
end
|
|
1191
|
+
|
|
1192
|
+
def relpath
|
|
1193
|
+
@currdir
|
|
1194
|
+
end
|
|
1195
|
+
|
|
1196
|
+
#
|
|
1197
|
+
# Config Access
|
|
1198
|
+
#
|
|
1199
|
+
|
|
1200
|
+
# module FileOperations requires this
|
|
1201
|
+
def verbose?
|
|
1202
|
+
@config.verbose?
|
|
1203
|
+
end
|
|
1204
|
+
|
|
1205
|
+
# module FileOperations requires this
|
|
1206
|
+
def no_harm?
|
|
1207
|
+
@config.no_harm?
|
|
1208
|
+
end
|
|
1209
|
+
|
|
1210
|
+
def verbose_off
|
|
1211
|
+
begin
|
|
1212
|
+
save, @config.verbose = @config.verbose?, false
|
|
1213
|
+
yield
|
|
1214
|
+
ensure
|
|
1215
|
+
@config.verbose = save
|
|
1216
|
+
end
|
|
1217
|
+
end
|
|
1218
|
+
|
|
1219
|
+
#
|
|
1220
|
+
# TASK config
|
|
1221
|
+
#
|
|
1222
|
+
|
|
1223
|
+
def exec_config
|
|
1224
|
+
exec_task_traverse 'config'
|
|
1225
|
+
end
|
|
1226
|
+
|
|
1227
|
+
alias config_dir_bin noop
|
|
1228
|
+
alias config_dir_lib noop
|
|
1229
|
+
|
|
1230
|
+
def config_dir_ext(rel)
|
|
1231
|
+
extconf if extdir?(curr_srcdir())
|
|
1232
|
+
end
|
|
1233
|
+
|
|
1234
|
+
alias config_dir_data noop
|
|
1235
|
+
alias config_dir_conf noop
|
|
1236
|
+
alias config_dir_man noop
|
|
1237
|
+
|
|
1238
|
+
def extconf
|
|
1239
|
+
ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt
|
|
1240
|
+
end
|
|
1241
|
+
|
|
1242
|
+
#
|
|
1243
|
+
# TASK setup
|
|
1244
|
+
#
|
|
1245
|
+
|
|
1246
|
+
def exec_setup
|
|
1247
|
+
exec_task_traverse 'setup'
|
|
1248
|
+
end
|
|
1249
|
+
|
|
1250
|
+
def setup_dir_bin(rel)
|
|
1251
|
+
files_of(curr_srcdir()).each do |fname|
|
|
1252
|
+
update_shebang_line "#{curr_srcdir()}/#{fname}"
|
|
1253
|
+
end
|
|
1254
|
+
end
|
|
1255
|
+
|
|
1256
|
+
alias setup_dir_lib noop
|
|
1257
|
+
|
|
1258
|
+
def setup_dir_ext(rel)
|
|
1259
|
+
make if extdir?(curr_srcdir())
|
|
1260
|
+
end
|
|
1261
|
+
|
|
1262
|
+
alias setup_dir_data noop
|
|
1263
|
+
alias setup_dir_conf noop
|
|
1264
|
+
alias setup_dir_man noop
|
|
1265
|
+
|
|
1266
|
+
def update_shebang_line(path)
|
|
1267
|
+
return if no_harm?
|
|
1268
|
+
return if config('shebang') == 'never'
|
|
1269
|
+
old = Shebang.load(path)
|
|
1270
|
+
if old
|
|
1271
|
+
$stderr.puts "warning: #{path}: Shebang line includes too many args. It is not portable and your program may not work." if old.args.size > 1
|
|
1272
|
+
new = new_shebang(old)
|
|
1273
|
+
return if new.to_s == old.to_s
|
|
1274
|
+
else
|
|
1275
|
+
return unless config('shebang') == 'all'
|
|
1276
|
+
new = Shebang.new(config('rubypath'))
|
|
1277
|
+
end
|
|
1278
|
+
$stderr.puts "updating shebang: #{File.basename(path)}" if verbose?
|
|
1279
|
+
open_atomic_writer(path) {|output|
|
|
1280
|
+
File.open(path, 'rb') {|f|
|
|
1281
|
+
f.gets if old # discard
|
|
1282
|
+
output.puts new.to_s
|
|
1283
|
+
output.print f.read
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
end
|
|
1287
|
+
|
|
1288
|
+
def new_shebang(old)
|
|
1289
|
+
if /\Aruby/ =~ File.basename(old.cmd)
|
|
1290
|
+
Shebang.new(config('rubypath'), old.args)
|
|
1291
|
+
elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby'
|
|
1292
|
+
Shebang.new(config('rubypath'), old.args[1..-1])
|
|
1293
|
+
else
|
|
1294
|
+
return old unless config('shebang') == 'all'
|
|
1295
|
+
Shebang.new(config('rubypath'))
|
|
1296
|
+
end
|
|
1297
|
+
end
|
|
1298
|
+
|
|
1299
|
+
def open_atomic_writer(path, &block)
|
|
1300
|
+
tmpfile = File.basename(path) + '.tmp'
|
|
1301
|
+
begin
|
|
1302
|
+
File.open(tmpfile, 'wb', &block)
|
|
1303
|
+
File.rename tmpfile, File.basename(path)
|
|
1304
|
+
ensure
|
|
1305
|
+
File.unlink tmpfile if File.exist?(tmpfile)
|
|
1306
|
+
end
|
|
1307
|
+
end
|
|
1308
|
+
|
|
1309
|
+
class Shebang
|
|
1310
|
+
def Shebang.load(path)
|
|
1311
|
+
line = nil
|
|
1312
|
+
File.open(path) {|f|
|
|
1313
|
+
line = f.gets
|
|
1314
|
+
}
|
|
1315
|
+
return nil unless /\A#!/ =~ line
|
|
1316
|
+
parse(line)
|
|
1317
|
+
end
|
|
1318
|
+
|
|
1319
|
+
def Shebang.parse(line)
|
|
1320
|
+
cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ')
|
|
1321
|
+
new(cmd, args)
|
|
1322
|
+
end
|
|
1323
|
+
|
|
1324
|
+
def initialize(cmd, args = [])
|
|
1325
|
+
@cmd = cmd
|
|
1326
|
+
@args = args
|
|
1327
|
+
end
|
|
1328
|
+
|
|
1329
|
+
attr_reader :cmd
|
|
1330
|
+
attr_reader :args
|
|
1331
|
+
|
|
1332
|
+
def to_s
|
|
1333
|
+
"#! #{@cmd}" + (@args.empty? ? '' : " #{@args.join(' ')}")
|
|
1334
|
+
end
|
|
1335
|
+
end
|
|
1336
|
+
|
|
1337
|
+
#
|
|
1338
|
+
# TASK install
|
|
1339
|
+
#
|
|
1340
|
+
|
|
1341
|
+
def exec_install
|
|
1342
|
+
rm_f 'InstalledFiles'
|
|
1343
|
+
exec_task_traverse 'install'
|
|
1344
|
+
end
|
|
1345
|
+
|
|
1346
|
+
def install_dir_bin(rel)
|
|
1347
|
+
install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755
|
|
1348
|
+
end
|
|
1349
|
+
|
|
1350
|
+
def install_dir_lib(rel)
|
|
1351
|
+
install_files libfiles(), "#{config('rbdir')}/#{rel}", 0644
|
|
1352
|
+
end
|
|
1353
|
+
|
|
1354
|
+
def install_dir_ext(rel)
|
|
1355
|
+
return unless extdir?(curr_srcdir())
|
|
1356
|
+
install_files rubyextentions('.'),
|
|
1357
|
+
"#{config('sodir')}/#{File.dirname(rel)}",
|
|
1358
|
+
0555
|
|
1359
|
+
end
|
|
1360
|
+
|
|
1361
|
+
def install_dir_data(rel)
|
|
1362
|
+
install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644
|
|
1363
|
+
end
|
|
1364
|
+
|
|
1365
|
+
def install_dir_conf(rel)
|
|
1366
|
+
# FIXME: should not remove current config files
|
|
1367
|
+
# (rename previous file to .old/.org)
|
|
1368
|
+
install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644
|
|
1369
|
+
end
|
|
1370
|
+
|
|
1371
|
+
def install_dir_man(rel)
|
|
1372
|
+
install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644
|
|
1373
|
+
end
|
|
1374
|
+
|
|
1375
|
+
def install_files(list, dest, mode)
|
|
1376
|
+
mkdir_p dest, @config.install_prefix
|
|
1377
|
+
list.each do |fname|
|
|
1378
|
+
install fname, dest, mode, @config.install_prefix
|
|
1379
|
+
end
|
|
1380
|
+
end
|
|
1381
|
+
|
|
1382
|
+
def libfiles
|
|
1383
|
+
glob_reject(%w(*.y *.output), targetfiles())
|
|
1384
|
+
end
|
|
1385
|
+
|
|
1386
|
+
def rubyextentions(dir)
|
|
1387
|
+
ents = glob_select("*.#{@config.dllext}", targetfiles())
|
|
1388
|
+
if ents.empty?
|
|
1389
|
+
setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first"
|
|
1390
|
+
end
|
|
1391
|
+
ents
|
|
1392
|
+
end
|
|
1393
|
+
|
|
1394
|
+
def targetfiles
|
|
1395
|
+
mapdir(existfiles() - hookfiles())
|
|
1396
|
+
end
|
|
1397
|
+
|
|
1398
|
+
def mapdir(ents)
|
|
1399
|
+
ents.map {|ent|
|
|
1400
|
+
if File.exist?(ent)
|
|
1401
|
+
then ent # objdir
|
|
1402
|
+
else "#{curr_srcdir()}/#{ent}" # srcdir
|
|
1403
|
+
end
|
|
1404
|
+
}
|
|
1405
|
+
end
|
|
1406
|
+
|
|
1407
|
+
# picked up many entries from cvs-1.11.1/src/ignore.c
|
|
1408
|
+
JUNK_FILES = %w(
|
|
1409
|
+
core RCSLOG tags TAGS .make.state
|
|
1410
|
+
.nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
|
|
1411
|
+
*~ *.old *.bak *.BAK *.orig *.rej _$* *$
|
|
1412
|
+
|
|
1413
|
+
*.org *.in .*
|
|
1414
|
+
)
|
|
1415
|
+
|
|
1416
|
+
def existfiles
|
|
1417
|
+
glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.')))
|
|
1418
|
+
end
|
|
1419
|
+
|
|
1420
|
+
def hookfiles
|
|
1421
|
+
%w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|
|
|
1422
|
+
%w( config setup install clean ).map {|t| sprintf(fmt, t) }
|
|
1423
|
+
}.flatten
|
|
1424
|
+
end
|
|
1425
|
+
|
|
1426
|
+
def glob_select(pat, ents)
|
|
1427
|
+
re = globs2re([pat])
|
|
1428
|
+
ents.select {|ent| re =~ ent }
|
|
1429
|
+
end
|
|
1430
|
+
|
|
1431
|
+
def glob_reject(pats, ents)
|
|
1432
|
+
re = globs2re(pats)
|
|
1433
|
+
ents.reject {|ent| re =~ ent }
|
|
1434
|
+
end
|
|
1435
|
+
|
|
1436
|
+
GLOB2REGEX = {
|
|
1437
|
+
'.' => '\.',
|
|
1438
|
+
'$' => '\$',
|
|
1439
|
+
'#' => '\#',
|
|
1440
|
+
'*' => '.*'
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
def globs2re(pats)
|
|
1444
|
+
/\A(?:#{
|
|
1445
|
+
pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|')
|
|
1446
|
+
})\z/
|
|
1447
|
+
end
|
|
1448
|
+
|
|
1449
|
+
#
|
|
1450
|
+
# TASK test
|
|
1451
|
+
#
|
|
1452
|
+
|
|
1453
|
+
TESTDIR = 'test'
|
|
1454
|
+
|
|
1455
|
+
def exec_test
|
|
1456
|
+
unless File.directory?('test')
|
|
1457
|
+
$stderr.puts 'no test in this package' if verbose?
|
|
1458
|
+
return
|
|
1459
|
+
end
|
|
1460
|
+
$stderr.puts 'Running tests...' if verbose?
|
|
1461
|
+
begin
|
|
1462
|
+
require 'test/unit'
|
|
1463
|
+
rescue LoadError
|
|
1464
|
+
setup_rb_error 'test/unit cannot loaded. You need Ruby 1.8 or later to invoke this task.'
|
|
1465
|
+
end
|
|
1466
|
+
runner = Test::Unit::AutoRunner.new(true)
|
|
1467
|
+
runner.to_run << TESTDIR
|
|
1468
|
+
runner.run
|
|
1469
|
+
end
|
|
1470
|
+
|
|
1471
|
+
#
|
|
1472
|
+
# TASK clean
|
|
1473
|
+
#
|
|
1474
|
+
|
|
1475
|
+
def exec_clean
|
|
1476
|
+
exec_task_traverse 'clean'
|
|
1477
|
+
rm_f @config.savefile
|
|
1478
|
+
rm_f 'InstalledFiles'
|
|
1479
|
+
end
|
|
1480
|
+
|
|
1481
|
+
alias clean_dir_bin noop
|
|
1482
|
+
alias clean_dir_lib noop
|
|
1483
|
+
alias clean_dir_data noop
|
|
1484
|
+
alias clean_dir_conf noop
|
|
1485
|
+
alias clean_dir_man noop
|
|
1486
|
+
|
|
1487
|
+
def clean_dir_ext(rel)
|
|
1488
|
+
return unless extdir?(curr_srcdir())
|
|
1489
|
+
make 'clean' if File.file?('Makefile')
|
|
1490
|
+
end
|
|
1491
|
+
|
|
1492
|
+
#
|
|
1493
|
+
# TASK distclean
|
|
1494
|
+
#
|
|
1495
|
+
|
|
1496
|
+
def exec_distclean
|
|
1497
|
+
exec_task_traverse 'distclean'
|
|
1498
|
+
rm_f @config.savefile
|
|
1499
|
+
rm_f 'InstalledFiles'
|
|
1500
|
+
end
|
|
1501
|
+
|
|
1502
|
+
alias distclean_dir_bin noop
|
|
1503
|
+
alias distclean_dir_lib noop
|
|
1504
|
+
|
|
1505
|
+
def distclean_dir_ext(rel)
|
|
1506
|
+
return unless extdir?(curr_srcdir())
|
|
1507
|
+
make 'distclean' if File.file?('Makefile')
|
|
1508
|
+
end
|
|
1509
|
+
|
|
1510
|
+
alias distclean_dir_data noop
|
|
1511
|
+
alias distclean_dir_conf noop
|
|
1512
|
+
alias distclean_dir_man noop
|
|
1513
|
+
|
|
1514
|
+
#
|
|
1515
|
+
# Traversing
|
|
1516
|
+
#
|
|
1517
|
+
|
|
1518
|
+
def exec_task_traverse(task)
|
|
1519
|
+
run_hook "pre-#{task}"
|
|
1520
|
+
FILETYPES.each do |type|
|
|
1521
|
+
if type == 'ext' and config('without-ext') == 'yes'
|
|
1522
|
+
$stderr.puts 'skipping ext/* by user option' if verbose?
|
|
1523
|
+
next
|
|
1524
|
+
end
|
|
1525
|
+
traverse task, type, "#{task}_dir_#{type}"
|
|
1526
|
+
end
|
|
1527
|
+
run_hook "post-#{task}"
|
|
1528
|
+
end
|
|
1529
|
+
|
|
1530
|
+
def traverse(task, rel, mid)
|
|
1531
|
+
dive_into(rel) {
|
|
1532
|
+
run_hook "pre-#{task}"
|
|
1533
|
+
__send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
|
|
1534
|
+
directories_of(curr_srcdir()).each do |d|
|
|
1535
|
+
traverse task, "#{rel}/#{d}", mid
|
|
1536
|
+
end
|
|
1537
|
+
run_hook "post-#{task}"
|
|
1538
|
+
}
|
|
1539
|
+
end
|
|
1540
|
+
|
|
1541
|
+
def dive_into(rel)
|
|
1542
|
+
return unless File.dir?("#{@srcdir}/#{rel}")
|
|
1543
|
+
|
|
1544
|
+
dir = File.basename(rel)
|
|
1545
|
+
Dir.mkdir dir unless File.dir?(dir)
|
|
1546
|
+
prevdir = Dir.pwd
|
|
1547
|
+
Dir.chdir dir
|
|
1548
|
+
$stderr.puts '---> ' + rel if verbose?
|
|
1549
|
+
@currdir = rel
|
|
1550
|
+
yield
|
|
1551
|
+
Dir.chdir prevdir
|
|
1552
|
+
$stderr.puts '<--- ' + rel if verbose?
|
|
1553
|
+
@currdir = File.dirname(rel)
|
|
1554
|
+
end
|
|
1555
|
+
|
|
1556
|
+
def run_hook(id)
|
|
1557
|
+
path = [ "#{curr_srcdir()}/#{id}",
|
|
1558
|
+
"#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) }
|
|
1559
|
+
return unless path
|
|
1560
|
+
begin
|
|
1561
|
+
instance_eval File.read(path), path, 1
|
|
1562
|
+
rescue
|
|
1563
|
+
raise if $DEBUG
|
|
1564
|
+
setup_rb_error "hook #{path} failed:\n" + $!.message
|
|
1565
|
+
end
|
|
1566
|
+
end
|
|
1567
|
+
|
|
1568
|
+
end # class Installer
|
|
1569
|
+
|
|
1570
|
+
|
|
1571
|
+
class SetupError < StandardError; end
|
|
1572
|
+
|
|
1573
|
+
def setup_rb_error(msg)
|
|
1574
|
+
raise SetupError, msg
|
|
1575
|
+
end
|
|
1576
|
+
|
|
1577
|
+
if $0 == __FILE__
|
|
1578
|
+
begin
|
|
1579
|
+
ToplevelInstaller.invoke
|
|
1580
|
+
rescue SetupError
|
|
1581
|
+
raise if $DEBUG
|
|
1582
|
+
$stderr.puts $!.message
|
|
1583
|
+
$stderr.puts "Try 'ruby #{$0} --help' for detailed usage."
|
|
1584
|
+
exit 1
|
|
1585
|
+
end
|
|
1586
|
+
end
|