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,71 @@
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 'pathname'
26
+
27
+ # Environment Information
28
+ module R::Env
29
+ # @!attribute [r] self.cmd_dir
30
+ # @return [Pathname] The directory from which rub was executed.
31
+ cattr_accessor :cmd_dir
32
+
33
+ # @!attribute [r] self.global_cache
34
+ # @return [Pathname] The global cache directory.
35
+ cattr_reader :global_cache
36
+
37
+ @cmd_dir = Pathname.pwd
38
+
39
+ # @private
40
+ def self.find_src_dir
41
+ d = @cmd_dir
42
+ while not (d+'root.rub').exist?
43
+ d = d.parent
44
+
45
+ if d.root?
46
+ $stderr.puts('root.rub not found. Make sure you are in the source directory.')
47
+ Sysexits.exit :usage
48
+ end
49
+ end
50
+
51
+ d.parent + (d.basename.to_s+'/') # Ensure this ends in a '/'
52
+ end
53
+ private_class_method :find_src_dir
54
+
55
+ # @return [Pathname] The directory from which rub was executed.
56
+ def self.src_dir
57
+ @src_dir ||= find_src_dir
58
+ end
59
+
60
+ # @return [Pathname] The build output directory.
61
+ def self.out_dir
62
+ @out_dir ||= src_dir + 'build/'
63
+ end
64
+
65
+ @global_cache = XDG[:cache_home].to_path + 'rub/'
66
+
67
+ # @return [Pathname] The project cache directory.
68
+ def self.project_cache
69
+ out_dir + "cache/"
70
+ end
71
+ end
@@ -0,0 +1,192 @@
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 'getoptlong'
26
+
27
+ # Command line parsing and handling.
28
+ module R::I::CommandLine
29
+ help = lambda do
30
+ puts <<EOS
31
+ #{R::Version.info_string}
32
+
33
+ Usage: #{$0} [options] [target...]
34
+
35
+ Targets:
36
+ Specify the targets to build. If none are specified ':all' is assumed.
37
+
38
+ Options:
39
+ -o --out <dir>
40
+ Sets the Rub build directory. This defaults to 'build/'. This is
41
+ merely a scratch location and none of the files in this directory should
42
+ be used outside of Rub. You may want to put this on fast storage (maybe
43
+ in RAM) to speed up complex builds.
44
+ -D, --define <key>[=<value>]
45
+ Define a configuration option. The key is everything up to the first
46
+ '=' and the value is everything after. If the character before the '=' is '+'
47
+ or '^' it is treated as a '-A' or '-P' respectivly. If there is no '=' it
48
+ is treated as a flag and it is set. Latter '-D' options overwrite earlier
49
+ ones.
50
+ -D, --define <key>+=<value>
51
+ -A, --append <key>[+=<value>]
52
+ -A, --append <key>[=<value>]
53
+ Append a configuration option to a list. If the current value is not
54
+ a list it is overwritten. The key and value specification is the same
55
+ as for '-D' except that a '+' is not nessary and is assumed.
56
+ -D, --define <key>^=<value>
57
+ -P, --prepend <key>[^=<value>]
58
+ -P, --prepend <key>[=<value>]
59
+ Prepend a configuration option to a list. If the current value is not
60
+ a list it is overwritten. The key and value specification is the same
61
+ as for '-D' except that a '^' is not nessary and is assumed.
62
+ -S, --script <script>
63
+ Run a script. This script should only set define options as all of Rub
64
+ may not be initilized yet. The script is executed in order with other
65
+ '--script', '-D' and '-P' options.
66
+ --explicit-scripts
67
+ Only run scripts specified on the command line. This prevents system
68
+ and user defaults from being used. Use with caution because it could
69
+ cause a build to fail if the provided definitions don't contain enough
70
+ information.
71
+ --no-cache
72
+ Disable caching. All state from previous runs will be discarded. Caching
73
+ will still be performed inside a single run.
74
+ --doc
75
+ Generate documentation of installed libraries. All additional parameters will
76
+ be passed to yardoc.
77
+ -V, --version
78
+ Print the version and exit.
79
+ --version-number
80
+ Print just the version number.
81
+ --version-describe
82
+ Print a short description of the version you are running.
83
+ --version-verbose
84
+ Print lots of information about the code you are running.
85
+ --version-version-commit
86
+ Print the version, and if not a release commit, the current commit.
87
+ -h, --help
88
+ Print this help text and exit.
89
+ EOS
90
+ exit
91
+ end
92
+
93
+ class << self
94
+ attr_reader :cache
95
+ end
96
+ @cache = true
97
+
98
+ opts = GetoptLong.new(
99
+ ['--out', '-o', GetoptLong::REQUIRED_ARGUMENT ],
100
+ ['-D', '--define', GetoptLong::REQUIRED_ARGUMENT ],
101
+ ['-A', '--append', GetoptLong::REQUIRED_ARGUMENT ],
102
+ ['-P', '--prepend', GetoptLong::REQUIRED_ARGUMENT ],
103
+ ['-S', '--script', GetoptLong::REQUIRED_ARGUMENT ],
104
+ ['--explicit-scripts', GetoptLong::NO_ARGUMENT ],
105
+ ['--no-cache', GetoptLong::NO_ARGUMENT ],
106
+ ['--doc', GetoptLong::NO_ARGUMENT ],
107
+ ['--help', '-h', GetoptLong::NO_ARGUMENT ],
108
+ ['--version', '-V', '-v', GetoptLong::NO_ARGUMENT ],
109
+ ['--version-number', GetoptLong::NO_ARGUMENT ],
110
+ ['--version-describe', GetoptLong::NO_ARGUMENT ],
111
+ ['--version-verbose', GetoptLong::NO_ARGUMENT ],
112
+ ['--version-version-commit', GetoptLong::NO_ARGUMENT ],
113
+ )
114
+
115
+ scripts = [];
116
+ sysscripts = XDG[:config].paths.map do |d|
117
+ [:file, d+'rub/config.rub']
118
+ end.keep_if { |t, n| n.exist? }
119
+
120
+ opts.each do |opt, arg|
121
+ case opt
122
+ when '--out'
123
+ Rub::Env.out_dir = Rub::Env.cmd_dir + arg
124
+ when '-D'
125
+ scripts.push [:define, arg]
126
+ when '-A'
127
+ scripts.push [:append, arg]
128
+ when '-P'
129
+ scripts.push [:prepend, arg]
130
+ when '-S'
131
+ scripts.push [:file, Pathname(arg)]
132
+ when '--explicit-scripts'
133
+ sysscripts = []
134
+ when '--no-cache'
135
+ @cache = false
136
+ when '--doc'
137
+ lib = Pathname.new(__FILE__).realpath.parent.parent.parent.parent
138
+ base = lib.parent
139
+
140
+ source = lib+'**/*.rb'
141
+ readme = [
142
+ base+'share/doc/'+R::Version.slug+'README.md', # Installed path.
143
+ base+'README.md' # Dev path.
144
+ ].find{|r| r.exist? }
145
+ pp readme
146
+
147
+ exec(
148
+ 'yardoc',
149
+ '--default-return', 'void',
150
+ *ARGV,
151
+ '-r', readme.to_s,
152
+ source.to_s
153
+ )
154
+ when '--help'
155
+ help.call
156
+ when '--version'
157
+ puts R::Version.info_string
158
+ exit 0
159
+ when '--version-number'
160
+ puts R::Version.number_string
161
+ exit 0
162
+ when '--version-describe'
163
+ puts R::Version.string
164
+ exit 0
165
+ when '--version-verbose'
166
+ puts R::Version.verbose
167
+ exit 0
168
+ when '--version-version-commit'
169
+ puts R::Version.version_commit
170
+ exit 0
171
+ end
172
+ end
173
+
174
+ sysscripts.concat(scripts).each do | t, a |
175
+ case t
176
+ when :file
177
+ if not a.exist?
178
+ $stderr.puts "Can't load defines from \"#{a}\" because it doesn't exist!"
179
+ Sysexits.exit :noinput
180
+ end
181
+
182
+ load a
183
+ when :define
184
+ D.define(a)
185
+ when :append
186
+ D.append(a)
187
+ when :prepend
188
+ D.prepend(a)
189
+ end
190
+ end
191
+ end
192
+
@@ -0,0 +1,78 @@
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
+ # Functions for running build scripts.
26
+ module R::I::Runner
27
+ @@loaded = {}
28
+
29
+ # Execute a file.
30
+ #
31
+ # Runs a script if it hasn't been run already.
32
+ #
33
+ # @param f [Pathname] The file to run.
34
+ # @return [void]
35
+ def self.do_file(f)
36
+ if @@loaded[f]
37
+ return
38
+ end
39
+
40
+ if not f.exist?
41
+ $stderr.puts "\"#{f}\" is not readable!"
42
+ Sysexits.exit :noinput
43
+ end
44
+
45
+ @@loaded[f] = true
46
+
47
+ Dir.chdir f.dirname do
48
+ load f.to_s
49
+ end
50
+ end
51
+ end
52
+
53
+ module C
54
+ # Add a directory to the build.
55
+ #
56
+ # This will run the "dir.rub" file in that directory synchronously. Any
57
+ # values that that directory defines will be available when this call
58
+ # returns.
59
+ #
60
+ # This function only runs scripts once, if the script has already run this
61
+ # function will return success without running the script, and as the script
62
+ # has already been run the exported values should be available.
63
+ def self.add_dir(dir)
64
+ dir = C.path(dir)
65
+
66
+ if not dir.directory?
67
+ raise "\"#{dir}\" is not a directory!"
68
+ end
69
+
70
+ dir += 'dir.rub'
71
+ if not dir.exist?
72
+ raise "\"#{dir}\" does not exist!"
73
+ end
74
+ dir = dir.realpath
75
+
76
+ R::I::Runner.do_file(dir)
77
+ end
78
+ end
@@ -0,0 +1,71 @@
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
+ module R
26
+ # @!attribute [r] self.ppersistant
27
+ # @return [Hash] The project cache.
28
+ cattr_reader :ppersistant
29
+
30
+ # @!attribute [r] self.spersistant
31
+ # @return [Hash] The system cache.
32
+ cattr_reader :spersistant
33
+
34
+ ppersistfile = R::Env.project_cache + "persistant.marshal"
35
+ if ppersistfile.exist? && R::I::CommandLine.cache
36
+ @ppersistant = Marshal.load(File.new(ppersistfile, 'r').read)
37
+ else
38
+ @ppersistant = {}
39
+ end
40
+
41
+ END {
42
+ File.new(ppersistfile, 'w').write(Marshal.dump(@ppersistant))
43
+ }
44
+
45
+ spersistfile = R::Env.global_cache + "persistant.marshal"
46
+ if spersistfile.exist? && R::I::CommandLine.cache
47
+ @spersistant = Marshal.load(spersistfile.read)
48
+ else
49
+ @spersistant = {}
50
+ end
51
+
52
+ END {
53
+ spersistfile.open('w').write(Marshal.dump(@spersistant))
54
+ }
55
+
56
+ # Clear the system cache.
57
+ def self.clear_system_cache
58
+ @spersistant.clear
59
+ end
60
+ # Clear the project cache.
61
+ def self.clear_project_cache
62
+ @ppersistant.clear
63
+ end
64
+ # Clear all caches.
65
+ def self.clear_cache
66
+ clear_system_cache
67
+ clear_project_cache
68
+ end
69
+ end
70
+
71
+
@@ -0,0 +1,250 @@
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
+ module R
26
+ # All targets.
27
+ #
28
+ # This should only be used for debugging. Use {find_target}, {get_target}
29
+ # and {set_target} instead.
30
+ #
31
+ # @return [Hash{Pathname,Symbol=>Target}]
32
+ cattr_reader :targets
33
+
34
+ @targets = {}
35
+ @sources = {}
36
+
37
+ # Find a target.
38
+ #
39
+ # Returns a target for +path+ or nil.
40
+ #
41
+ # @param path [Pathname,String] The path of the target.
42
+ # @return [Target,nil] The target.
43
+ def self.find_target(path)
44
+ path = C.path(path)
45
+ @targets[path] || @sources[path]
46
+ end
47
+
48
+ # Get a target.
49
+ #
50
+ # This function get's an existing target if it exists or returns a new
51
+ # source target if there is no existing target to build it.
52
+ #
53
+ # @param path [Pathname,String] The path of the target.
54
+ # @return [Target,TargetSource]
55
+ def self.get_target(path)
56
+ path = C.path(path)
57
+
58
+ find_target(path) or @sources[path] ||= TargetSource.new(path)
59
+ end
60
+
61
+ # Set a target to a path.
62
+ #
63
+ # This function registers +target+ as a way to build +path+.
64
+ #
65
+ # @param path [Pathname,String] The path that is build by the target.
66
+ # @param target [Target] The target that builds +path+.
67
+ # @return [void]
68
+ #
69
+ # @see Target#register.
70
+ def self.set_target(path, target)
71
+ if find_target(path)
72
+ $stderr.puts "Warning: #{path} can be built two ways."
73
+ end
74
+ @targets[C.path(path)] = target
75
+ end
76
+
77
+ # The base target class.
78
+ #
79
+ # It has simple building logic and a way to register targets. All
80
+ # targets should inherit from this class.
81
+ class Target
82
+ # Inputs
83
+ #
84
+ # @return [Set<Pathname>] The inputs this target depends on.
85
+ def input
86
+ Set.new
87
+ end
88
+
89
+ # Outputs
90
+ #
91
+ # @return [Set<Pathname>] The outputs this target creates.
92
+ def output
93
+ Set.new
94
+ end
95
+
96
+ # Description.
97
+ #
98
+ # Shown for :help.
99
+ # @return [String,nil]
100
+ def description
101
+ nil
102
+ end
103
+
104
+ # Register this target.
105
+ #
106
+ # Registers this target as building it's {#output}s.
107
+ #
108
+ # @return [void]
109
+ def register
110
+ output.each do |d|
111
+ R.set_target(d, self)
112
+ end
113
+ end
114
+
115
+ # Is this target up to date?
116
+ def clean?
117
+ false
118
+ end
119
+
120
+ # Return a hash of this target.
121
+ #
122
+ # This hash should represent a unique build environment and change if
123
+ # anything in that environment does. This includes, but is not limited
124
+ # to:
125
+ # - Input files.
126
+ # - Output files.
127
+ # - Build commands.
128
+ #
129
+ # @return [String] the hash.
130
+ def hash_input
131
+ Digest::SHA1.digest(
132
+ (
133
+ input.map{|i| R::get_target(i).hash_output(i) }
134
+ ).join
135
+ )
136
+ end
137
+
138
+ @@symbolcounter = rand(2**160) # Shouldn't repeat very often.
139
+ def hash_output(t)
140
+ if t.is_a? Symbol
141
+ @@symbolcounter++
142
+ "symbol-#{@@symbolcounter.to_s(16)}" # Never clean.
143
+ else
144
+ Digest::SHA1.file(t).to_s
145
+ end
146
+ end
147
+
148
+ def hash_outputs(t = output)
149
+ Digest::SHA1.digest(t.map{|o| hash_output(o)}.join)
150
+ end
151
+
152
+ def hash_self
153
+ Digest::SHA1.digest(hash_input+hash_outputs)
154
+ end
155
+
156
+ # Build the inputs.
157
+ def build_dependancies
158
+ input.each{|i| R::get_target(i).build }
159
+ end
160
+ private :build_dependancies
161
+
162
+ # Build this target.
163
+ #
164
+ # This should be overridden if {#build} itself is not overridden.
165
+
166
+ # @return [void]
167
+ def build_self
168
+ raise "#build_self not implemented in #{self.class}."
169
+ end
170
+ private :build_self
171
+
172
+ # Build.
173
+ #
174
+ # This is a simple build method. It calls {#build_dependancies} then
175
+ # {#build_self}. Either or both of these methods can be overwritten to
176
+ # customize the build or this function can be overwritten to have more
177
+ # control.
178
+ #
179
+ # @return [void]
180
+ def build
181
+ build_dependancies
182
+ build_self
183
+
184
+ nil
185
+ end
186
+ end
187
+
188
+ # Target with additional functionality.
189
+ class TargetSmart < Target
190
+ attr_reader :input, :output
191
+
192
+ def initialize
193
+ @input = Set.new
194
+ @output = Set.new
195
+ end
196
+
197
+ # Mark target as clean.
198
+ #
199
+ # @return [void]
200
+ def clean
201
+ output.all?{|f| !f.is_a?(Symbol) and f.exist?} or return
202
+
203
+ R::ppersistant["Rub.TargetSmart.#{@output.sort.join('\0')}"] = hash_self
204
+ end
205
+
206
+ # Is this target clean?
207
+ #
208
+ # @return [true,false] True if this target is up-to-date.
209
+ def clean?
210
+ output.each do |f|
211
+ f.is_a?(Symbol) and return false # Tags are never clean.
212
+ f.exist? or return false # Output missing, rebuild.
213
+ end
214
+
215
+ R::ppersistant["Rub.TargetSmart.#{@output.sort.join('\0')}"] == hash_self
216
+ end
217
+
218
+ def build
219
+ build_dependancies
220
+
221
+ clean? and return
222
+
223
+ build_self
224
+
225
+ clean
226
+ end
227
+ end
228
+
229
+ # A target for existing sources.
230
+ class TargetSource < Target
231
+ attr_reader :output
232
+
233
+ def initialize(p)
234
+ @src = p
235
+ @output = Set[p]
236
+ end
237
+
238
+ def hash_output(f)
239
+ @hashcache ||= Digest::SHA1.file(f).to_s
240
+ end
241
+
242
+ def build
243
+ if not @src.exist?
244
+ #p self
245
+ $stderr.puts "Error: source file #{@src} does not exist!"
246
+ Sysexits.exit :noinput
247
+ end
248
+ end
249
+ end
250
+ end