rub 0.4.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.
@@ -0,0 +1,163 @@
1
+ # Copyright 2013 Kevin Cox
2
+
3
+ ################################################################################
4
+ # #
5
+ # This software is provided 'as-is', without any express or implied #
6
+ # warranty. In no event will the authors be held liable for any damages #
7
+ # arising from the use of this software. #
8
+ # #
9
+ # Permission is granted to anyone to use this software for any purpose, #
10
+ # including commercial applications, and to alter it and redistribute it #
11
+ # freely, subject to the following restrictions: #
12
+ # #
13
+ # 1. The origin of this software must not be misrepresented; you must not #
14
+ # claim that you wrote the original software. If you use this software in #
15
+ # a product, an acknowledgment in the product documentation would be #
16
+ # appreciated but is not required. #
17
+ # #
18
+ # 2. Altered source versions must be plainly marked as such, and must not be #
19
+ # misrepresented as being the original software. #
20
+ # #
21
+ # 3. This notice may not be removed or altered from any source distribution. #
22
+ # #
23
+ ################################################################################
24
+
25
+ require 'singleton'
26
+
27
+ D.resolve_path :pefix
28
+
29
+ # General purpose build tools.
30
+ module L::Util
31
+ class TargetUninstall < C::TargetTag
32
+ include Singleton
33
+
34
+ def initialize
35
+ super :uninstall
36
+ @files = Set.new
37
+
38
+ register
39
+ end
40
+
41
+ def add(f)
42
+ f = R::Tool.make_set_paths f
43
+
44
+ @files.merge f
45
+
46
+ f
47
+ end
48
+
49
+ def build
50
+ R::run(['rm', '-fv']+@files.to_a, "Removing installed files.", importance: :med)
51
+ end
52
+ end
53
+ TargetUninstall.instance.description = 'Remove installed targets.' # Make the target.
54
+
55
+ # Uninstall a file.
56
+ #
57
+ # Adds the file to the :uninstall tag.
58
+ #
59
+ # @param what [Set<Pathname,String>,Array<Pathname,String>,Pathname,String]
60
+ # The files to remove.
61
+ # @return [void]
62
+ def self.uninstall(what)
63
+ what = R::Tool.make_set_paths what
64
+
65
+ what.each do |f|
66
+ TargetUninstall.instance.add f
67
+ end
68
+ end
69
+
70
+ # Create a link
71
+ #
72
+ # @param target [Pathname,String] The file the link points to.
73
+ # @param name [Pathname,String] The location of the link.
74
+ # @param type [:sym,:hard] The type of link to create.
75
+ # @param expand_target [true,false] Whether or not to make target an
76
+ # absolute path. This allows the creation
77
+ # of relative links.
78
+ # @return [Pathname] The path of the link.
79
+ def self.link(target, name, type = :sym, expand_target: true)
80
+ target = expand_target ? C.path(target) : target
81
+ name = C.path name
82
+
83
+ C.generator(
84
+ [], #target,
85
+ ['ln', '-f', (type == :sym ? '-s' : nil), target, name].compact,
86
+ name,
87
+ desc: 'Linking'
88
+ )
89
+ name
90
+ end
91
+
92
+ # Install a file to a specific location.
93
+ #
94
+ # @see #install
95
+ #
96
+ # Similar to {#install} but the basename is not used as the name in the new
97
+ # location. If from is a dirtectory it is installed and renamed but it's
98
+ # contents are preserved with the same names.
99
+ #
100
+ # @param from [Set<Pathname,String>,Array<Pathname,String>,Pathname,String]
101
+ # The files to install.
102
+ # @param to [Pathname,String] Where to install them. If not
103
+ # absolute it is relative to +D:prefix+
104
+ # @param mode [Numeric] The permissions (specified in base ten) for the
105
+ # file. If nil the current permissions are kept.
106
+ # @param require [Set<Pathname,String>,Array<Pathname,String>,Pathname,String]
107
+ # Files that must be present before installing the file.
108
+ # @return [Array<Pathname>] The installed files.
109
+ def self.install_to(from, to, mode: nil, require: [])
110
+ from = C.path(from)
111
+ to = Pathname.new(to).expand_path(D :prefix)
112
+
113
+ if from.directory?
114
+ return install(from.children, to)
115
+ end
116
+
117
+ C.generator(
118
+ Set[from].merge(require),
119
+ ['install', "-D#{mode!=nil ? "m#{mode}" : "" }", from, to],
120
+ to,
121
+ desc: 'Installing'
122
+ ).each do |o|
123
+ C.tag(:all ).require(from)
124
+ C.tag(:install).require(to)
125
+ end
126
+ uninstall to
127
+
128
+ Set[from]
129
+ end
130
+
131
+ # Install a file
132
+ #
133
+ # Installs +what+ into the directory +where+. Source files are added to
134
+ # the +=all+ tag and installed files are added to the +=install+ tag.
135
+ #
136
+ # @param what [Set<Pathname,String>,Array<Pathname,String>,Pathname,String]
137
+ # The files to install.
138
+ # @param where [Pathname,String] The directory to install them to. If not
139
+ # absolute it is relative to +D:prefix+
140
+ # @param mode [Numeric] The permissions (specified in base ten) for the
141
+ # file. If nil the current permissions are kept.
142
+ # @param require [Set<Pathname,String>,Array<Pathname,String>,Pathname,String]
143
+ # Files that must be present before installing the file.
144
+ # @return [Array<Pathname>] The installed files.
145
+ #
146
+ # @example
147
+ # exe = L::C.program(srcs, ['pthread'], 'bitmonitor-test')
148
+ # L::Util.install exe, 'bin/', mode: 755
149
+ #
150
+ def self.install(what, where, mode: nil, require: [])
151
+ what = R::Tool.make_set_paths what
152
+ where = Pathname.new(where)
153
+ require = R::Tool.make_set_paths require
154
+
155
+ what.map do |f|
156
+ if f.directory?
157
+ install(f.children, where+f.basename)
158
+ else
159
+ install_to(f, where+f.basename)
160
+ end
161
+ end.flatten.to_set
162
+ end
163
+ end
@@ -0,0 +1,42 @@
1
+ # Copyright 2013 Kevin Cox
2
+
3
+ ################################################################################
4
+ # #
5
+ # This software is provided 'as-is', without any express or implied #
6
+ # warranty. In no event will the authors be held liable for any damages #
7
+ # arising from the use of this software. #
8
+ # #
9
+ # Permission is granted to anyone to use this software for any purpose, #
10
+ # including commercial applications, and to alter it and redistribute it #
11
+ # freely, subject to the following restrictions: #
12
+ # #
13
+ # 1. The origin of this software must not be misrepresented; you must not #
14
+ # claim that you wrote the original software. If you use this software in #
15
+ # a product, an acknowledgment in the product documentation would be #
16
+ # appreciated but is not required. #
17
+ # #
18
+ # 2. Altered source versions must be plainly marked as such, and must not be #
19
+ # misrepresented as being the original software. #
20
+ # #
21
+ # 3. This notice may not be removed or altered from any source distribution. #
22
+ # #
23
+ ################################################################################
24
+
25
+ # Rub internals.
26
+ #
27
+ # Internal functions intended for library developers only.
28
+ module R; end
29
+ module R::I; end
30
+
31
+ require_relative 'r/tool'
32
+ require_relative 'r/env'
33
+ require_relative 'r/version'
34
+ require_relative 'r/command'
35
+
36
+ require_relative 'r/i/runner'
37
+ require_relative 'r/i/commandline'
38
+
39
+ require_relative 'r/persist'
40
+
41
+ require_relative 'r/target'
42
+ require_relative 'r/targetgenerator'
@@ -0,0 +1,293 @@
1
+ # Copyright 2013 Kevin Cox
2
+
3
+ ################################################################################
4
+ # #
5
+ # This software is provided 'as-is', without any express or implied #
6
+ # warranty. In no event will the authors be held liable for any damages #
7
+ # arising from the use of this software. #
8
+ # #
9
+ # Permission is granted to anyone to use this software for any purpose, #
10
+ # including commercial applications, and to alter it and redistribute it #
11
+ # freely, subject to the following restrictions: #
12
+ # #
13
+ # 1. The origin of this software must not be misrepresented; you must not #
14
+ # claim that you wrote the original software. If you use this software in #
15
+ # a product, an acknowledgment in the product documentation would be #
16
+ # appreciated but is not required. #
17
+ # #
18
+ # 2. Altered source versions must be plainly marked as such, and must not be #
19
+ # misrepresented as being the original software. #
20
+ # #
21
+ # 3. This notice may not be removed or altered from any source distribution. #
22
+ # #
23
+ ################################################################################
24
+
25
+ # A high level interface for executing commands.
26
+ class R::Command
27
+ # @!attribute [r] cmd
28
+ # @return [Array<String>] The command to run.
29
+ attr_reader :cmd
30
+
31
+ # @!attribute [r] env
32
+ #
33
+ # Control the environment of the spawned process. Values from this
34
+ # hash will be added to the processes environment. If a value is
35
+ # nil the key will be removed from the processes environment. These
36
+ # values are overlaid on the existing environment unless {#clearenv}
37
+ # is set.
38
+ #
39
+ # @return [Hash{String=>String,nil}] The environment variables.
40
+ attr_reader :env
41
+
42
+ # @!attribute [rw] stdin
43
+ # @return [String] The string to use as input to the command.
44
+ attr_accessor :stdin
45
+ # @!attribute [r] stdout
46
+ # @return [String] The output produced by the command.
47
+ # @!attribute [r] stderr
48
+ # @return [String] The error output produced by the command.
49
+ attr_reader :stdout, :stderr
50
+
51
+ # @!attribute [r] status
52
+ #
53
+ # Available after {#block} has returned.
54
+ #
55
+ # @return [Process::Status,nil] The processes exit status.
56
+ attr_reader :status
57
+
58
+ # @!attribute [rw] clearenv
59
+ # If set, the executed command will not inherit environment variables
60
+ # from Rub. Only the values in {#env} will be present in the
61
+ # environment.
62
+ #
63
+ # Defaults to false.
64
+ #
65
+ # @return [true,false] If true don't inherit the environment.
66
+ attr_accessor :clearenv
67
+ # @!attribute [rw] mergeouts
68
+ # @return [true,false] If true merge {#stdout} into {#stderr}.
69
+ attr_accessor :mergeouts
70
+
71
+ # Create a new command to run.
72
+ #
73
+ # @param cmd [Array<String,#to_s>] The command that will be run.
74
+ def initialize(cmd)
75
+ @env = {}
76
+
77
+ @clearenv = false
78
+ @mergeouts = false
79
+
80
+ @cmd = cmd.map{|a| a.to_s}
81
+ end
82
+
83
+ # Start the command.
84
+ #
85
+ # Executes the command. The command will run in the background. If you
86
+ # want to wait for the command to complete call {#block}.
87
+ #
88
+ # @note Calling this command a second time before {#block} returns
89
+ # produces undefined behaviour. A {Command} can be run multiple
90
+ # times but it must finish before being run again.
91
+ def start
92
+ @status = nil
93
+
94
+ @stdinr, @stdinw = IO.pipe
95
+ @stdoutr, @stdoutw = IO.pipe
96
+ @stderrr, @stderrw = IO.pipe
97
+
98
+ @stdout = ""
99
+ @stderr = ""
100
+ @status = nil
101
+
102
+ args = [
103
+ @env,
104
+ *@cmd.map{|a| a.to_s},
105
+ :unsetenv_others=>@clearenv,
106
+ :in =>@stdinr,
107
+ :out=>@stdoutw,
108
+ :err=>(@mergeouts?@stdoutw:@stderrw),
109
+ ]
110
+
111
+ #p "args: #{args}"
112
+ @pid = spawn(*args)
113
+
114
+ @stdinr.close
115
+
116
+ @stdinw.write @stdin
117
+
118
+ @stdinw.close
119
+ @stdoutw.close
120
+ @stderrw.close
121
+
122
+ @pid
123
+ end
124
+
125
+ # Run the command and block until completion.
126
+ #
127
+ # Equivalent to calling {#start} then {#block}.
128
+ #
129
+ # @return [true,false] Whether the command completed successfully.
130
+ def run
131
+ start
132
+ block
133
+ end
134
+
135
+ # Wait for a command to finish.
136
+ #
137
+ # Block until a currently running process finishes. Behaviour is
138
+ # undefined if the command is not currently running ({#start} has been
139
+ # called since the last {#block}).
140
+ #
141
+ # After this call returns {#status} will be available.
142
+ #
143
+ # @return [true,false] Whether the command completed successfully.
144
+ def block
145
+ @stdout = @stdoutr.read
146
+ @stderr = @stderrr.read
147
+
148
+ #puts "Blocking on #{@pid} #{@cmd.join' '}"
149
+ pid, @status = Process.wait2 @pid
150
+ #puts "Done #{@cmd.join' '}"
151
+
152
+ @stdoutr.close
153
+ @stderrr.close
154
+
155
+ success?
156
+ end
157
+
158
+ # Check if the command was successful
159
+ #
160
+ # @return [true,false] true if the command executed successfully
161
+ # otherwise false.
162
+ # @note If the command has not been executed or has not completed this
163
+ # returns false.
164
+ def success?
165
+ !!( @status and @status.exitstatus == 0 )
166
+ end
167
+ end
168
+
169
+ # Run a command as part of the build.
170
+ #
171
+ # The command will be run and status will be printed.
172
+ #
173
+ # @param cmd [Array<String,#to_s>] The command to execute.
174
+ # @param desc [String] The verb describing what the command is doing.
175
+ # @param importance [Symbol] The importance of this step. Affects printing.
176
+ # @return [true,false] true if the command was successful.
177
+ def R.run(cmd, desc, importance: :med)
178
+ cmd = cmd.dup
179
+
180
+ bs = R::BuildStep.new
181
+ bs.desc = desc
182
+ bs.cmd = cmd
183
+ bs.importance = importance
184
+
185
+ cpath = C.find_command cmd[0]
186
+ if not cpath
187
+ raise "Could not find #{cmd[0]}. Please install it or add it to your path."
188
+ end
189
+ cmd[0] = cpath
190
+ cmd.map!{|a| a.to_s}
191
+
192
+ c = R::Command.new(cmd)
193
+ c.mergeouts = true
194
+
195
+ c.run
196
+
197
+ bs.out = c.stdout
198
+ bs.status = c.status.exitstatus
199
+
200
+ bs.print
201
+
202
+ c.success?
203
+ end
204
+
205
+ # Manages reporting build progress.
206
+ class R::BuildStep
207
+ # The verb describing this step.
208
+ # @return [String]
209
+ attr_accessor :desc
210
+
211
+ # The command to execute.
212
+ # @return [Array<String>]
213
+ attr_accessor :cmd
214
+
215
+ # The command output.
216
+ # @return [String]
217
+ attr_accessor :out
218
+
219
+ # The exit status of the command.
220
+ # @return [Process::Status]
221
+ attr_accessor :status
222
+
223
+ # The command's importance.
224
+ # @return [Symbol] :low, :medium or :high
225
+ attr_reader :importance
226
+
227
+ # Set the command's importance.
228
+ # @param i [Symbol] :low, :medium or :high
229
+ # @return [Symbol] +i+
230
+ def importance=(i)
231
+ @importance = i
232
+ case i
233
+ when :low
234
+ @importancei = 1
235
+ when :med
236
+ @importancei = 2
237
+ when :high
238
+ @importancei = 3
239
+ else
240
+ $stderr.puts "Unknown importance #{i.inspect}, defaulting to :high."
241
+ @importancei = 3
242
+ end
243
+ end
244
+
245
+ # Constructor
246
+ #
247
+ # @param cmd [Array<String>] The command executed.
248
+ # @param out [String] The output generated.
249
+ # @param desc [String] A verb describing the event.
250
+ def initialize(cmd=[], out="", desc="", status=0)
251
+ @cmd = cmd
252
+ @out = out
253
+ @desc = desc
254
+ @status = status
255
+
256
+ # For some reason self is needed.
257
+ self.importance = :high
258
+ end
259
+
260
+ # Format the command.
261
+ #
262
+ # Format's the command in the prettiest way possible. Theoretically
263
+ # this command could be pasted into a shell and execute the desired
264
+ # command.
265
+ #
266
+ # @param cmd [Array<String>] The command.
267
+ def format_cmd(cmd)
268
+ cmd.map do |e|
269
+ if /[~`!#$&*(){};'"]/ =~ e
270
+ "'#{e.sub(/['"]/, '\\\1')}'"
271
+ else
272
+ e
273
+ end
274
+ end.join(' ')
275
+ end
276
+
277
+ # Print the result.
278
+ def print
279
+ @importancei < 2 and return
280
+
281
+ puts "\e[#{status==0 ? '' : '31;'}1m#{@desc}\e[0m"
282
+ puts format_cmd @cmd unless @cmd.empty?
283
+ Kernel::print @out
284
+
285
+ if status != 0
286
+ if cmd.empty?
287
+ puts "\e[31;1mProcess failed.\e[0m"
288
+ else
289
+ puts "\e[31;1mProcess returned status #{status}\e[0m"
290
+ end
291
+ end
292
+ end
293
+ end