rprogram 0.2.3 → 0.3.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/ChangeLog.md +33 -11
- data/README.md +7 -3
- data/lib/rprogram/program.rb +214 -24
- data/lib/rprogram/sudo.rb +14 -0
- data/lib/rprogram/sudo_task.rb +68 -0
- data/lib/rprogram/system.rb +316 -0
- data/lib/rprogram/task.rb +144 -16
- data/lib/rprogram/version.rb +1 -1
- data/rprogram.gemspec +1 -1
- data/spec/classes/aliased_program.rb +1 -5
- data/spec/classes/named_program.rb +1 -5
- data/spec/program_spec.rb +84 -1
- data/spec/scripts/echo.rb +3 -0
- data/spec/scripts/fail.rb +3 -0
- data/spec/scripts/print.rb +3 -0
- data/spec/scripts/success.rb +3 -0
- data/spec/system_spec.rb +80 -0
- data/spec/task_spec.rb +0 -15
- metadata +15 -20
- data/lib/rprogram/compat.rb +0 -124
- data/lib/rprogram/extensions.rb +0 -1
- data/lib/rprogram/nameable.rb +0 -2
- data/lib/rprogram/nameable/class_methods.rb +0 -84
- data/lib/rprogram/nameable/nameable.rb +0 -34
- data/lib/rprogram/options.rb +0 -2
- data/lib/rprogram/options/class_methods.rb +0 -112
- data/lib/rprogram/options/options.rb +0 -39
- data/spec/compat_spec.rb +0 -21
- data/spec/nameable_spec.rb +0 -83
@@ -0,0 +1,316 @@
|
|
1
|
+
require 'rprogram/exceptions/program_not_found'
|
2
|
+
require 'rprogram/rprogram'
|
3
|
+
|
4
|
+
require 'env/variables'
|
5
|
+
|
6
|
+
module RProgram
|
7
|
+
#
|
8
|
+
# @since 0.3.0
|
9
|
+
#
|
10
|
+
module System
|
11
|
+
extend Env::Variables
|
12
|
+
|
13
|
+
@arch, @platform = RUBY_PLATFORM.split('-',2)
|
14
|
+
@platform ||= @arch
|
15
|
+
|
16
|
+
#
|
17
|
+
# Determines the native architecture.
|
18
|
+
#
|
19
|
+
# @return [String]
|
20
|
+
# The native architecture.
|
21
|
+
#
|
22
|
+
# @example
|
23
|
+
# System.arch
|
24
|
+
# # => "x86-64"
|
25
|
+
#
|
26
|
+
# @since 0.3.0
|
27
|
+
#
|
28
|
+
def System.arch
|
29
|
+
@arch
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# Determines the native platform.
|
34
|
+
#
|
35
|
+
# @return [String]
|
36
|
+
# The native platform.
|
37
|
+
#
|
38
|
+
# @example
|
39
|
+
# System.platform
|
40
|
+
# # => "linux"
|
41
|
+
#
|
42
|
+
def System.platform
|
43
|
+
@platform
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# Determines if the platform is Windows.
|
48
|
+
#
|
49
|
+
# @return [Boolean]
|
50
|
+
# Specifies whether the platform is Windows.
|
51
|
+
#
|
52
|
+
# @since 0.3.0
|
53
|
+
#
|
54
|
+
def System.windows?
|
55
|
+
if @platform
|
56
|
+
@platform.include?('mingw') || @platform.include?('mswin')
|
57
|
+
else
|
58
|
+
false
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
# Determines if the current Ruby VM is from the 1.8.x family.
|
64
|
+
#
|
65
|
+
# @return [Boolean]
|
66
|
+
# Specifies if the current Ruby VM is from the 1.8.x family.
|
67
|
+
#
|
68
|
+
# @since 0.3.0
|
69
|
+
#
|
70
|
+
def System.ruby_1_8?
|
71
|
+
RUBY_VERSION[0,4] == '1.8.'
|
72
|
+
end
|
73
|
+
|
74
|
+
#
|
75
|
+
# Determines if the current Ruby VM is JRuby.
|
76
|
+
#
|
77
|
+
# @return [Boolean]
|
78
|
+
# Specifies whether the Ruby VM is JRuby.
|
79
|
+
#
|
80
|
+
# @since 0.3.0
|
81
|
+
#
|
82
|
+
def System.jruby?
|
83
|
+
Object.const_defined?(:RUBY_ENGINE) && \
|
84
|
+
Object.const_get(:RUBY_ENGINE) == 'jruby'
|
85
|
+
end
|
86
|
+
|
87
|
+
#
|
88
|
+
# Finds the full-path of the program with the matching name.
|
89
|
+
#
|
90
|
+
# @param [String] name
|
91
|
+
# The name of the program to find.
|
92
|
+
#
|
93
|
+
# @return [Pathname, nil]
|
94
|
+
# The full-path of the desired program.
|
95
|
+
#
|
96
|
+
# @example
|
97
|
+
# System.find_program('as')
|
98
|
+
# #=> #<Pathname:/usr/bin/as>
|
99
|
+
#
|
100
|
+
def System.find_program(name)
|
101
|
+
# add the `.exe` suffix to the name, if running on Windows
|
102
|
+
if windows?
|
103
|
+
name = "#{name}.exe"
|
104
|
+
end
|
105
|
+
|
106
|
+
paths.each do |dir|
|
107
|
+
full_path = dir.join(name).expand_path
|
108
|
+
|
109
|
+
return full_path if full_path.file?
|
110
|
+
end
|
111
|
+
|
112
|
+
return nil
|
113
|
+
end
|
114
|
+
|
115
|
+
#
|
116
|
+
# Finds the program matching one of the matching names.
|
117
|
+
#
|
118
|
+
# @param [Array] names
|
119
|
+
# The names of the program to use while searching for the program.
|
120
|
+
#
|
121
|
+
# @return [Pathname, nil]
|
122
|
+
# The first full-path for the program.
|
123
|
+
#
|
124
|
+
# @example
|
125
|
+
# System.find_program_by_names("gas","as")
|
126
|
+
# # => #<Pathname:/usr/bin/as>
|
127
|
+
#
|
128
|
+
def System.find_program_by_names(*names)
|
129
|
+
names.each do |name|
|
130
|
+
if (path = find_program(name))
|
131
|
+
return path
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
return nil
|
136
|
+
end
|
137
|
+
|
138
|
+
#
|
139
|
+
# Runs a program.
|
140
|
+
#
|
141
|
+
# @overload run(path,*arguments)
|
142
|
+
# Run the program with the given arguments.
|
143
|
+
#
|
144
|
+
# @param [Pathname, String] path
|
145
|
+
# The path of the program to run.
|
146
|
+
#
|
147
|
+
# @param [Array] arguments
|
148
|
+
# Additional arguments to run the program with.
|
149
|
+
#
|
150
|
+
# @overload run(path,*arguments,options)
|
151
|
+
# Run the program with the given arguments and options.
|
152
|
+
#
|
153
|
+
# @param [Pathname, String] path
|
154
|
+
# The path of the program to run.
|
155
|
+
#
|
156
|
+
# @param [Array] arguments
|
157
|
+
# Additional arguments to run the program with.
|
158
|
+
#
|
159
|
+
# @param [Hash] options
|
160
|
+
# Additional options to execute the program with.
|
161
|
+
#
|
162
|
+
# @option options [Hash{String => String}] :env
|
163
|
+
# Environment variables to execute the program with.
|
164
|
+
#
|
165
|
+
# @option options [String] :popen
|
166
|
+
# Specifies to run the program using `IO.popen` with the given
|
167
|
+
# IO mode.
|
168
|
+
#
|
169
|
+
# @return [Boolean]
|
170
|
+
# Specifies whether the program exited successfully.
|
171
|
+
#
|
172
|
+
# @raise [RuntimeError]
|
173
|
+
# Passing `:popen`, `:env` or exec options is not supported
|
174
|
+
# before Ruby 1.9.1.
|
175
|
+
#
|
176
|
+
# @see http://rubydoc.info/stdlib/core/1.9.2/Kernel#spawn-instance_method
|
177
|
+
# For acceptable options.
|
178
|
+
#
|
179
|
+
def System.run(*arguments)
|
180
|
+
# extra tailing options and ENV variables from arguments
|
181
|
+
if arguments.last.kind_of?(Hash)
|
182
|
+
options = arguments.pop
|
183
|
+
env = (options.delete(:env) || {})
|
184
|
+
popen = options.delete(:popen)
|
185
|
+
else
|
186
|
+
options = {}
|
187
|
+
env = {}
|
188
|
+
end
|
189
|
+
|
190
|
+
# all arguments must be Strings
|
191
|
+
arguments = arguments.map { |arg| arg.to_s }
|
192
|
+
|
193
|
+
# print debugging information
|
194
|
+
if RProgram.debug
|
195
|
+
command = ''
|
196
|
+
|
197
|
+
env.each do |name,value|
|
198
|
+
command << "#{name}=#{value} "
|
199
|
+
end
|
200
|
+
|
201
|
+
command << arguments.join(' ')
|
202
|
+
command << " #{options.inspect}" unless options.empty?
|
203
|
+
|
204
|
+
STDERR.puts ">>> #{command}"
|
205
|
+
end
|
206
|
+
|
207
|
+
# passing ENV variables or exec options is not supported before 1.9.1
|
208
|
+
if (!options.empty? && ruby_1_8?)
|
209
|
+
raise("cannot pass exec options to Kernel.system in #{RUBY_VERSION}")
|
210
|
+
end
|
211
|
+
|
212
|
+
if popen
|
213
|
+
# IO.popen does not accept multiple arguments on Ruby 1.8.x.
|
214
|
+
if ruby_1_8?
|
215
|
+
raise("cannot use :popen on #{RUBY_VERSION}, please use 1.9.x")
|
216
|
+
end
|
217
|
+
|
218
|
+
# :popen can only be used on Unix, or on Windows with JRuby
|
219
|
+
if (windows? && !jruby?)
|
220
|
+
raise("cannot use :popen on Windows, unless under JRuby")
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
# re-add ENV variables and exec options
|
225
|
+
arguments.unshift(env) unless env.empty?
|
226
|
+
arguments.push(options) unless options.empty?
|
227
|
+
|
228
|
+
if popen
|
229
|
+
IO.popen(arguments,popen)
|
230
|
+
else
|
231
|
+
Kernel.system(*arguments)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
#
|
236
|
+
# The path to the `sudo` program.
|
237
|
+
#
|
238
|
+
# @return [Pathname, nil]
|
239
|
+
# The path to the `sudo` program.
|
240
|
+
#
|
241
|
+
# @since 0.3.0
|
242
|
+
#
|
243
|
+
def System.sudo_path
|
244
|
+
@sudo ||= find_program('sudo')
|
245
|
+
end
|
246
|
+
|
247
|
+
#
|
248
|
+
# Sets the path to the `sudo` program.
|
249
|
+
#
|
250
|
+
# @param [String, Pathname] path
|
251
|
+
# The new path to use.
|
252
|
+
#
|
253
|
+
# @return [Pathanme]
|
254
|
+
# The new path to the `sudo` program.
|
255
|
+
#
|
256
|
+
# @since 0.3.0
|
257
|
+
#
|
258
|
+
def System.sudo_path=(path)
|
259
|
+
@sudo = Pathname.new(path)
|
260
|
+
end
|
261
|
+
|
262
|
+
#
|
263
|
+
# Determines whether `sudo` is available on the system.
|
264
|
+
#
|
265
|
+
# @return [Boolean]
|
266
|
+
# Specifies whether the `sudo` program is installed on the system.
|
267
|
+
#
|
268
|
+
# @since 0.3.0
|
269
|
+
#
|
270
|
+
def System.sudo?
|
271
|
+
!sudo_path.nil?
|
272
|
+
end
|
273
|
+
|
274
|
+
#
|
275
|
+
# Runs a program under sudo.
|
276
|
+
#
|
277
|
+
# @overload run(path,*arguments)
|
278
|
+
# Run the program with the given arguments.
|
279
|
+
#
|
280
|
+
# @param [Pathname, String] path
|
281
|
+
# The path of the program to run.
|
282
|
+
#
|
283
|
+
# @param [Array] arguments
|
284
|
+
# Additional arguments to run the program with.
|
285
|
+
#
|
286
|
+
# @overload run(path,*arguments,options)
|
287
|
+
# Run the program with the given arguments and options.
|
288
|
+
#
|
289
|
+
# @param [Pathname, String] path
|
290
|
+
# The path of the program to run.
|
291
|
+
#
|
292
|
+
# @param [Array] arguments
|
293
|
+
# Additional arguments to run the program with.
|
294
|
+
#
|
295
|
+
# @param [Hash] options
|
296
|
+
# Additional options to execute the program with.
|
297
|
+
#
|
298
|
+
# @return [Boolean]
|
299
|
+
# Specifies whether the program exited successfully.
|
300
|
+
#
|
301
|
+
# @raise [ProgramNotFound]
|
302
|
+
# Indicates that the `sudo` program could not be located.
|
303
|
+
#
|
304
|
+
# @since 0.1.8
|
305
|
+
#
|
306
|
+
# @see run
|
307
|
+
#
|
308
|
+
def System.sudo(*arguments)
|
309
|
+
unless sudo?
|
310
|
+
raise(ProgramNotFound,'could not find the "sudo" program')
|
311
|
+
end
|
312
|
+
|
313
|
+
return run(sudo_path,*arguments)
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
data/lib/rprogram/task.rb
CHANGED
@@ -1,14 +1,10 @@
|
|
1
|
-
require 'rprogram/
|
1
|
+
require 'rprogram/option'
|
2
2
|
require 'rprogram/option_list'
|
3
|
+
require 'rprogram/non_option'
|
3
4
|
|
4
5
|
module RProgram
|
5
6
|
class Task
|
6
7
|
|
7
|
-
include Options
|
8
|
-
|
9
|
-
# Specifies whether the task will be run under sudo
|
10
|
-
attr_accessor :sudo
|
11
|
-
|
12
8
|
#
|
13
9
|
# Creates a new Task object.
|
14
10
|
#
|
@@ -30,13 +26,119 @@ module RProgram
|
|
30
26
|
# end
|
31
27
|
#
|
32
28
|
def initialize(options={},&block)
|
33
|
-
@sudo = (options.delete(:sudo) || false)
|
34
29
|
@subtasks = {}
|
35
30
|
@options = options
|
36
31
|
|
37
32
|
block.call(self) if block
|
38
33
|
end
|
39
34
|
|
35
|
+
#
|
36
|
+
# @return [Hash]
|
37
|
+
# All defined non-options of the class.
|
38
|
+
#
|
39
|
+
def self.non_options
|
40
|
+
@non_options ||= {}
|
41
|
+
end
|
42
|
+
|
43
|
+
#
|
44
|
+
# Searches for the non-option with the matching name in the class
|
45
|
+
# and it's ancestors.
|
46
|
+
#
|
47
|
+
# @param [Symbol, String] name
|
48
|
+
# The name to search for.
|
49
|
+
#
|
50
|
+
# @return [true, false]
|
51
|
+
# Specifies whether the non-option with the matching name was
|
52
|
+
# defined.
|
53
|
+
#
|
54
|
+
def self.has_non_option?(name)
|
55
|
+
name = name.to_sym
|
56
|
+
|
57
|
+
ancestors.each do |base|
|
58
|
+
if base < RProgram::Task
|
59
|
+
return true if base.non_options.include?(name)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
return false
|
64
|
+
end
|
65
|
+
|
66
|
+
#
|
67
|
+
# Searches for the non-option with the matching name in the class
|
68
|
+
# and it's ancestors.
|
69
|
+
#
|
70
|
+
# @param [Symbol, String] name
|
71
|
+
# The name to search for.
|
72
|
+
#
|
73
|
+
# @return [NonOption]
|
74
|
+
# The non-option with the matching name.
|
75
|
+
#
|
76
|
+
def self.get_non_option(name)
|
77
|
+
name = name.to_sym
|
78
|
+
|
79
|
+
ancestors.each do |base|
|
80
|
+
if base < RProgram::Task
|
81
|
+
if base.non_options.has_key?(name)
|
82
|
+
return base.non_options[name]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
return nil
|
88
|
+
end
|
89
|
+
|
90
|
+
#
|
91
|
+
# @return [Hash]
|
92
|
+
# All defined options for the class.
|
93
|
+
#
|
94
|
+
def self.options
|
95
|
+
@options ||= {}
|
96
|
+
end
|
97
|
+
|
98
|
+
#
|
99
|
+
# Searches for the option with the matching name in the class and
|
100
|
+
# it's ancestors.
|
101
|
+
#
|
102
|
+
# @param [Symbol, String] name
|
103
|
+
# The name to search for.
|
104
|
+
#
|
105
|
+
# @return [true, false]
|
106
|
+
# Specifies whether the option with the matching name was defined.
|
107
|
+
#
|
108
|
+
def self.has_option?(name)
|
109
|
+
name = name.to_sym
|
110
|
+
|
111
|
+
ancestors.each do |base|
|
112
|
+
if base < RProgram::Task
|
113
|
+
return true if base.options.has_key?(name)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
return false
|
118
|
+
end
|
119
|
+
|
120
|
+
#
|
121
|
+
# Searches for the option with the matching name in the class and
|
122
|
+
# it's ancestors.
|
123
|
+
#
|
124
|
+
# @param [Symbol, String] name
|
125
|
+
# The name to search for.
|
126
|
+
#
|
127
|
+
# @return [Option]
|
128
|
+
# The option with the matching name.
|
129
|
+
#
|
130
|
+
def self.get_option(name)
|
131
|
+
name = name.to_sym
|
132
|
+
|
133
|
+
ancestors.each do |base|
|
134
|
+
if base < RProgram::Task
|
135
|
+
return base.options[name] if base.options.has_key?(name)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
return nil
|
140
|
+
end
|
141
|
+
|
40
142
|
#
|
41
143
|
# Creates a new Task object, then formats command-line arguments
|
42
144
|
# using the Task object.
|
@@ -69,15 +171,31 @@ module RProgram
|
|
69
171
|
end
|
70
172
|
|
71
173
|
#
|
72
|
-
#
|
174
|
+
# @see has_non_option?
|
175
|
+
#
|
176
|
+
def has_non_option?(name)
|
177
|
+
self.class.has_non_option?(name)
|
178
|
+
end
|
179
|
+
|
180
|
+
#
|
181
|
+
# @see get_non_option
|
182
|
+
#
|
183
|
+
def get_non_option(name)
|
184
|
+
self.class.get_non_option(name)
|
185
|
+
end
|
186
|
+
|
187
|
+
#
|
188
|
+
# @see has_option?
|
73
189
|
#
|
74
|
-
|
75
|
-
|
190
|
+
def has_option?(name)
|
191
|
+
self.class.has_option?(name)
|
192
|
+
end
|
193
|
+
|
76
194
|
#
|
77
|
-
# @
|
195
|
+
# @see get_option
|
78
196
|
#
|
79
|
-
def
|
80
|
-
|
197
|
+
def get_option(name)
|
198
|
+
self.class.get_option(name)
|
81
199
|
end
|
82
200
|
|
83
201
|
#
|
@@ -166,9 +284,17 @@ module RProgram
|
|
166
284
|
# options and tailing non-options of the task and it's sub-tasks.
|
167
285
|
#
|
168
286
|
def arguments
|
169
|
-
|
287
|
+
tailing_args = tailing_non_options
|
288
|
+
|
289
|
+
if tailing_args.any? { |arg| arg[0,1] == '-' }
|
290
|
+
tailing_args.unshift('--')
|
291
|
+
end
|
292
|
+
|
293
|
+
return leading_non_options + options + tailing_args
|
170
294
|
end
|
171
295
|
|
296
|
+
alias to_a arguments
|
297
|
+
|
172
298
|
protected
|
173
299
|
|
174
300
|
#
|
@@ -185,20 +311,22 @@ module RProgram
|
|
185
311
|
#
|
186
312
|
def self.subtask(name,task)
|
187
313
|
name = name.to_s
|
314
|
+
file = __FILE__
|
315
|
+
line = __LINE__ + 3
|
188
316
|
|
189
317
|
class_eval %{
|
190
318
|
def #{name}(options={},&block)
|
191
319
|
if @subtasks[#{name.dump}]
|
192
320
|
@subtasks[#{name.dump}].options.merge!(options)
|
193
321
|
|
194
|
-
|
322
|
+
yield(@subtasks[#{name.dump}]) if block_given?
|
195
323
|
else
|
196
324
|
@subtasks[#{name.dump}] = #{task}.new(options,&block)
|
197
325
|
end
|
198
326
|
|
199
327
|
return @subtasks[#{name.dump}]
|
200
328
|
end
|
201
|
-
}
|
329
|
+
}, file, line
|
202
330
|
end
|
203
331
|
|
204
332
|
#
|