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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b101f8d3dc57411ac38315bbac0d36045585a1c0
4
+ data.tar.gz: 2b81673c121797dc7cc56e3b01667f3d3b3f39b2
5
+ SHA512:
6
+ metadata.gz: 3c088de47f8f8fb3e2509d5931db5b0692443e14d25bb3101c00286d839b0ed68dcc5e4106eaf95d0bf83e31a52f0cfbf82a73931ddd507dc51d7a3f75ad4c03
7
+ data.tar.gz: ae33cc23df67aff256c24c0a0787c675bab9e4cc1f20c509792993e3f53a57a70ec0ab1c6d475c36a456ee79ac9e36c9187d057df30cef03a2f92a0287c21c3a
@@ -0,0 +1,276 @@
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 'date'
26
+
27
+ # Core Module
28
+ module C
29
+ # Expand a path.
30
+ #
31
+ # @return [Pathname]
32
+ def self.path(p)
33
+ p.is_a? Symbol and return p
34
+ p.is_a? Pathname and return p.expand_path
35
+ p = p.to_s
36
+
37
+ #p = case p[0]
38
+ # when '!'
39
+ # Pathname.new(p[1..-1])
40
+ # when '>'
41
+ # R::Env.out_dir + p[1..-1]
42
+ # when '<'
43
+ # R::Env.src_dir + p[1..-1]
44
+ # else
45
+ # Pathname.new(p)
46
+ #end
47
+
48
+ Pathname.new(p).expand_path
49
+ end
50
+
51
+ # Glob pathnames.
52
+ #
53
+ # @see Dir.glob
54
+ #
55
+ # @param glob [String]
56
+ # @return [Set<Pathname>]
57
+ def self.glob(glob)
58
+ Set.new Dir.glob(glob).map{|e| C.path(e) }
59
+ end
60
+
61
+ # Get a consistant hash of an object.
62
+ def self.chash(o)
63
+ if o.is_a? Array
64
+ return o.map{|i| chash i}.join
65
+ end
66
+
67
+ # Super hacky, strip out object-ids, because they change every
68
+ # invocation, but use inspect. It works alright.
69
+ r = o.inspect.gsub(/(?<!:):0x[0-9a-f]*/, '')
70
+
71
+ # Modules don't print themselfs meaningfully.
72
+ if o.is_a? Module
73
+ r << o.pretty_print_instance_variables.map{|k| [k, o.instance_variable_get(k)] }.inspect
74
+ end
75
+
76
+ r
77
+ end
78
+
79
+ # Create a probably unique path segment.
80
+ #
81
+ # Creates a string in the form '$stuff/' that will probably be unique.
82
+ #
83
+ # @param seed [Object] A value to use for the folder name, keeping this the
84
+ # same across invocations allows predictable names,
85
+ # preventing unnecessary rebuilds.
86
+ def self.unique_segment(*seed)
87
+ seed ||= caller_locations(1,1)
88
+
89
+ return Digest::SHA1.hexdigest(chash(seed))
90
+ end
91
+
92
+ # Return a probably unique file name.
93
+ #
94
+ # This file can be used as a build target.
95
+ #
96
+ # @param base [String] The basename of the file.
97
+ # @param seed [Object] A value to use for the folder name, keeping this the
98
+ # same across invocations allows predictable names,
99
+ # preventing unnecessary rebuilds.
100
+ def self.unique_path(base, seed)
101
+ R::Env.out_dir + 'c/unique/' + unique_segment(seed) + base
102
+ end
103
+
104
+ # Tag Target
105
+ #
106
+ # This is the target used for tags.
107
+ class TargetTag < R::Target
108
+ attr_reader :output, :input
109
+ attr_reader :tag
110
+
111
+ attr_accessor :description
112
+
113
+ # Create a TargetTag
114
+ #
115
+ # @param t [Symbol] the name of the tag.
116
+ def initialize(t)
117
+ t.is_a? Symbol or raise 'Targets can be created with symbols only.'
118
+
119
+ @tag = t
120
+ @output = Set[t]
121
+ @input = Set[]
122
+ end
123
+
124
+ # Add a target to this tag.
125
+ #
126
+ # @param f [Pathname,String] The path of the target.
127
+ def require(f)
128
+ f = R::Tool.make_set f
129
+ f.map!{|e| C.path(e)}
130
+
131
+ input.merge(f)
132
+ end
133
+
134
+ def build
135
+ input.each{|i| R::get_target(i).build }
136
+ end
137
+ end
138
+
139
+ # Tag class
140
+ #
141
+ # Manages a tag. This should not be created buy the user but retrieved from
142
+ # {C.tag}.
143
+ class Tag
144
+ # The tag's name.
145
+ # @return [Symbol]
146
+ attr_accessor :name
147
+
148
+ def description
149
+ @target.description
150
+ end
151
+ def description=(d)
152
+ @target.description = d
153
+ end
154
+
155
+ # Create a Tag
156
+ #
157
+ # @param t [Symbol] the name of the tag.
158
+ def initialize(t)
159
+ @name = t
160
+ @target = TargetTag.new(t)
161
+ @target.register
162
+ end
163
+
164
+ # Add a target to this tag.
165
+ #
166
+ # @param f [Pathname,String] The path of the target.
167
+ def require(f)
168
+ @target.require f
169
+ end
170
+ end
171
+
172
+ # Get a tag.
173
+ #
174
+ # If the tag already exists it returns the existing {Tag} object otherwise
175
+ # it creates and returns a new {Tag} instance.
176
+ #
177
+ # @param t [Symbol] The tag name.
178
+ # @return [Tag] The tag object.
179
+ def self.tag(t)
180
+ R.find_target(t) || Tag.new(t)
181
+ end
182
+
183
+ ##### Create default tags.
184
+ tag(:all) .description = 'All targets.'
185
+ tag(:install).description = 'Install the project.'
186
+ tag(:none) .description = 'Do nothing.'
187
+
188
+ # Add a generator to the build
189
+ #
190
+ # This function provides a simple api for creating {R::TargetGenerator}
191
+ # targets. It creates a target that simply runs one or more commands to
192
+ # transform it's inputs into outputs. This interface handles all build
193
+ # caching and parallelization.
194
+ #
195
+ # @param src [Array<Pathname,String>,Pathname,String]
196
+ # The source file or list of source files.
197
+ # @param cmd [Array<Array<Pathname,String>>,Array<Pathname,String>]
198
+ # The command or list of commands to run. Commands will run in
199
+ # order.
200
+ # @param out [Array<Pathname,String>,Pathname,String]
201
+ # The output files of the command.
202
+ # @param desc The verb for this step in the process. (See
203
+ # {R::TargetGenerator#action})
204
+ # @return [Array<Pathname>] The output files. This will represent the same
205
+ # values passed in to the +out+ parameter but it
206
+ # will be a new Array and all the values will be
207
+ # Pathnames.
208
+ def self.generator(src, cmd, out, desc: nil)
209
+ t = R::TargetGenerator.new
210
+
211
+ desc and t.action = desc
212
+
213
+ src = R::Tool.make_set_paths(src)
214
+ out = R::Tool.make_set_paths(out)
215
+ cmd[0].is_a?(Array) or cmd = [cmd]
216
+
217
+ t.input .merge(src)
218
+ t.output.merge(out)
219
+ t.add_cmds cmd
220
+
221
+ t.register
222
+
223
+ out
224
+ end
225
+
226
+ # Find an executable on the system.
227
+ #
228
+ # This searches the system for execrable in the appropriate locations
229
+ # (example $PATH on UNIX).
230
+ #
231
+ # This function caches its result both in memory and between Rub runs. Feel
232
+ # free to call it often.
233
+ #
234
+ # @param cmd [String] The name of the command (basename only).
235
+ # @return [Pathname,nil] Pathname, or nil if not found.
236
+ #
237
+ # @example
238
+ # C::find_command 'true' #=> #<Pathname:/usr/bin/true>
239
+ # C::find_command 'cc' #=> #<Pathname:/home/kevincox/.local/bin/cc>
240
+ # C::find_command 'sl' #=> #<Pathname:/usr/bin/sl>
241
+ # C::find_command 'python' #=> #<Pathname:/usr/bin/python>
242
+ # C::find_command 'explode' #=> nil
243
+ def self.find_command(cmd)
244
+ pn = Pathname.new(cmd)
245
+ if pn.absolute?
246
+ #return pn.executable? ? pn : nil
247
+ return pn
248
+ end
249
+
250
+ exe = R.spersistant["C.find_command.#{cmd}"]
251
+
252
+ exe and exe.executable? and return exe
253
+
254
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
255
+ names = exts.map{|e| cmd+e}
256
+ ENV['PATH'].split(File::PATH_SEPARATOR)
257
+ .map{|d|Pathname.new(d)}
258
+ .each do |d|
259
+ names.each do |n|
260
+ e = d + n
261
+ #p e
262
+
263
+ if e.executable?
264
+ exe = e
265
+ break
266
+ end
267
+ end
268
+
269
+ exe and break
270
+ end
271
+
272
+ R.spersistant["C.find_command.#{cmd}"] = exe
273
+ end
274
+ end
275
+
276
+ D.resolve_path(:prefix, "/usr/local/")
@@ -0,0 +1,252 @@
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
+ # Definitions Namespace.
26
+ module D
27
+ @@map = {}
28
+
29
+ # Define a configuration option.
30
+ #
31
+ # @param k [String] The key, or if +v+ is nil a string to parse.
32
+ # @param v [String,nil] The value
33
+ # @return [String] The value.
34
+ #
35
+ # If +v+ is non-nil +k+ is the key and +v+ is the value. If +v+ is nil
36
+ # k must be a string and it is parsed to find the key and value.
37
+ #
38
+ # - If there is an '=' in +k+ the first one is used.
39
+ # - If the '=' is proceeded by a '+' everything before the "+=" is used as
40
+ # the key and everything after is used as the value. These are then
41
+ # passed are passed onto #push.
42
+ # - Otherwise everything before the '=' is used as the key and everything
43
+ # after as the value.
44
+ # - If there is no '=' all of +k+ is used as the key and the value is
45
+ # +true+.
46
+ #
47
+ # The key will be converted into a symbol and the k/v pair will be added to
48
+ # the configuration options.
49
+ #
50
+ # @example
51
+ # D.define('k1', 'v1')
52
+ # D[:k1] #=> "v1"
53
+ # D.define('k2=v2')
54
+ # D[:k2] #=> "v2"
55
+ # D.define('k3')
56
+ # D[:k3] #=> true
57
+ # D.define('k4')
58
+ # D[:k4] #=> true
59
+ # D.define('k4=')
60
+ # D[:k4] #=> ""
61
+ # D.define('k5+=v5')
62
+ # D.define('k5+=v6')
63
+ # D[:k5] #=> ["v5", "v6"]
64
+ #
65
+ # D.define('w1=v1=v2')
66
+ # D[:w1] #=> "v1=v2"
67
+ # D.define('w2=v2+=v3')
68
+ # D[:w2] #=> "v2+=v3"
69
+ def self.define(k, v=nil)
70
+ if v == nil
71
+
72
+ k, f, v = k.partition '='
73
+
74
+ if k.end_with?('+') and not f.empty?
75
+ return append(k[0..-2], v)
76
+ elsif k.end_with?('^') and not f.empty?
77
+ return prepend(k[0..-2], v)
78
+ end
79
+ end
80
+
81
+ k = k.to_sym
82
+
83
+ if f == ""
84
+ v = true
85
+ end
86
+
87
+ @@map[k] = v
88
+ end
89
+ class << self
90
+ alias_method '[]=', :define
91
+ end
92
+
93
+ # Append a configuration option onto a value.
94
+ #
95
+ # @param k [String] The key, or if +v+ is nil a string to parse.
96
+ # @param v [String,nil] The value
97
+ # @return [String] The value.
98
+ #
99
+ # If +v+ is non-nil +k+ is the key and +v+ is the value. If +v+ is nil
100
+ # k must be a string and it is parsed to find the key and value.
101
+ #
102
+ # If there is a '=' in the string everything before the first '=' is used as
103
+ # the key and everything after the value. If the key ends is '+' it is
104
+ # dropped. If there is no '=' +k+ is used as the key and +true+ as the
105
+ # value.
106
+ #
107
+ # @example
108
+ # D.append('k1', 'v1')
109
+ # D[:k1] #=> ["v1"]
110
+ # D.append('k1', 'v2')
111
+ # D[:k1] #=> ["v1", "v2"]
112
+ # D.append('k2=v3')
113
+ # D.append('k2+=v4')
114
+ # D.append('k2+=')
115
+ # D[:k2] #=> ["v3", "v4", ""]
116
+ #
117
+ # D.append('w1+')
118
+ # D[:w1] #=> nil
119
+ # D['w1+'] #=> [true]
120
+ def self.append(k, v=nil)
121
+ if v == nil
122
+ k, f, v = k.partition '='
123
+
124
+ if k.end_with?('+') and not f.empty?
125
+ k = k[0..-2]
126
+ end
127
+ end
128
+
129
+ k = k.to_sym
130
+
131
+ if f == ''
132
+ v = true
133
+ end
134
+
135
+ @@map[k].is_a?(Array) or @@map[k] = []
136
+
137
+ @@map[k].push(v)
138
+ end
139
+
140
+ # Prepend a configuration option onto a value.
141
+ #
142
+ # @param k [String] The key, or if +v+ is nil a string to parse.
143
+ # @param v [String,nil] The value
144
+ # @return [String] The value.
145
+ #
146
+ # If +v+ is non-nil +k+ is the key and +v+ is the value. If +v+ is nil
147
+ # k must be a string and it is parsed to find the key and value.
148
+ #
149
+ # If there is a '=' in the string everything before the first '=' is used as
150
+ # the key and everything after the value. If the key ends is '^' it is
151
+ # dropped. If there is no '=' +k+ is used as the key and +true+ as the
152
+ # value.
153
+ #
154
+ # @example
155
+ # D.prepend('k1', 'v1')
156
+ # D[:k1] #=> ["v1"]
157
+ # D.prepend('k1', 'v2')
158
+ # D[:k1] #=> ["v2", "v1"]
159
+ # D.prepend('k2=v3')
160
+ # D.prepend('k2^=v4')
161
+ # D.prepend('k2+=')
162
+ # D[:k2] #=> ["", "v4", "v3",]
163
+ #
164
+ # D.prepend('w1^')
165
+ # D[:w1] #=> nil
166
+ # D['w1^'] #=> [true]
167
+ def self.prepend(k, v=nil)
168
+ if v == nil
169
+ k, f, v = k.partition '='
170
+
171
+ if k.end_with?('^') and not f.empty?
172
+ k = k[0..-2]
173
+ end
174
+ end
175
+
176
+ k = k.to_sym
177
+
178
+ if f == ''
179
+ v = true
180
+ end
181
+
182
+ @@map[k].is_a?(Array) or @@map[k] = []
183
+
184
+ @@map[k].unshift(v)
185
+ end
186
+
187
+ # Retrieve a defined value.
188
+ #
189
+ # @param k [Symbol,String] The key.
190
+ def self.[] (k)
191
+ @@map[k.to_sym]
192
+ end
193
+
194
+ # Return the configuration map.
195
+ #
196
+ # This is intended for debugging only and may be removed/made private any
197
+ # time.
198
+ #
199
+ # See: #pp
200
+ def self.map
201
+ return @@map
202
+ end
203
+
204
+ # Pretty Print the configuration options.
205
+ #
206
+ # Useful for debugging.
207
+ def self.pp
208
+ pp map
209
+ end
210
+
211
+ # Read definitions from a file.
212
+ #
213
+ # @deprecated
214
+ #
215
+ # These are read one-per-line and passed to #define (as one argument).
216
+ def self.fromFile(fn)
217
+ File.open(fn) {|f| f.each_line {|l| define(l.chomp) } }
218
+ end
219
+
220
+ # Resolve a path.
221
+ #
222
+ # This makes a passed in path proper. This function must be used in order
223
+ # to make paths passed in on the command line proper. This makes all paths
224
+ # relative to the directory where the command was executed. If the
225
+ # definition was not provided it is set to default, no path resolution is
226
+ # done on the default value.
227
+ #
228
+ # @param k [Symbol,String] The key of the option.
229
+ # @param default [Object] The value to use if +k+ is not set.
230
+ def self.resolve_path(k, default=nil)
231
+ k = k.to_sym
232
+
233
+ @@map[k] = if @@map[k] != nil
234
+ Pathname.new(@@map[k]).expand_path(R::Env.cmd_dir)
235
+ else
236
+ default
237
+ end
238
+ end
239
+ end
240
+
241
+ # Alias for D.[]
242
+ #
243
+ # @see D.[]
244
+ #
245
+ # @example
246
+ # D.define 'k1=v1'
247
+ # D.define 'k2=v2'
248
+ # D :k1 #=> "v1"
249
+ # D:k2 #=> "v2"
250
+ def D(k)
251
+ D[k]
252
+ end