rub 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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