rscons 1.3.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- OWUzMDNlZjIxODVkZjhkNzg4MjQyZWZkMmQ2YWFiNjNmZmQyOWQ2OA==
4
+ NWMyYzkzNjU2MTQ1MmRiOTFkMjhlMzVlMjYzM2Q0MTJkOWRjMWM5Yw==
5
5
  data.tar.gz: !binary |-
6
- MWRhYzY1YTNlMjIyMDkyYzE4ZjZjMTU4YjVlZmFiMDk2MjBjYWRiNg==
6
+ M2QyYmEwN2U2MGUyNGJkYjI4ODVjYWU5MzUyNWRiZTEzMWYxNDE3Mw==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- ODQwNTE4YjI5MmRiMjljNTMzMjRkYzU2OGZkMGUwNTgzMThlZmQzYWZhN2U0
10
- NjUyNDgyZDUzZGQwZDMwYmJmZTYzNjA2MGQxMjcxYzIyNmQxNzcyM2RhODU5
11
- ODMyMjMyODk5MTI5MWEwMTFlYWRlMjJiZTkwMDJhN2EyMGY1N2I=
9
+ MzkyZDE0ZjQ2M2NlNTdmMTZiNzNmNDIwMjUwMmEwZjI1N2Y5Mjg3YTU0YjYy
10
+ MWQwYjgwOGM2MGY3NjA0ZjlmZGM5ZThhN2UyZWM1OGIwMzk5MjQ2YzFhZjA1
11
+ N2FhMWI2ZjY2Njk1YjkwOWUwNGI5MjQ4MGU2N2U2NjM5NDlkYWM=
12
12
  data.tar.gz: !binary |-
13
- ZjBkZDAzNjk3ZDkyMWFmMGEzZTg1ZTZiZTIzNmNjMmM4OGFmYzczOGQ0NTIw
14
- NTMxNjUyNThjNzQ4MmMxYWY5YjNhYTY3NzZkZjQ0MjNmOTY5NzkxN2U5ZjY4
15
- YzQ2NTIyNDMwZGU1OGNiMmIzZTM0ZWM3ODQwY2IzN2E1NGJlNTM=
13
+ YWRmNzc3OGU0NmE3ZTUzOWZkYmM5MjQ5OGU0ZTA2MWM2MmRlNzAyMzRiZTQx
14
+ YTc2NjRjYmZmYjU0MjA2ZmUyNmY5ZGI4OTZjN2Q5MzcyYzQ1ZWQ3Njg5MTc3
15
+ ZmVlY2NiZmViYmQyOWFmM2Q3MWEzMWY5ZTRkMjQ4ODYwNGM1ZmY=
data/README.md CHANGED
@@ -12,7 +12,7 @@ Add this line to your application's Gemfile:
12
12
 
13
13
  And then execute:
14
14
 
15
- $ bundle
15
+ $ bundle install
16
16
 
17
17
  Or install it yourself as:
18
18
 
@@ -176,11 +176,14 @@ subset of build targets or source files.
176
176
  The `build_op` parameter to the build hook block is a Hash describing the
177
177
  build operation with the following keys:
178
178
  * `:builder` - `Builder` instance in use
179
+ * `:env` - `Environment` calling the build hook; note that this may be
180
+ different from the Environment that the build hook was added to in the case
181
+ that the original Environment was cloned with build hooks!
179
182
  * `:target` - `String` name of the target file
180
183
  * `:sources` - `Array` of the source files
181
- * `:vars` - `Rscons::VarSet` containing the construction variables to use
182
- The build hook can overwrite entries in `build_op[:vars]` to alter the
183
- construction variables in use for this specific build operation.
184
+ * `:vars` - `Rscons::VarSet` containing the construction variables to use.
185
+ The build hook can overwrite entries in `build_op[:vars]` to alter the
186
+ construction variables in use for this specific build operation.
184
187
 
185
188
  ### Example: Creating a static library
186
189
 
@@ -190,19 +193,96 @@ Rscons::Environment.new do |env|
190
193
  end
191
194
  ```
192
195
 
196
+ ### Example: Creating a C++ parser source from a Yacc/Bison input file
197
+
198
+ ```ruby
199
+ Rscons::Environment.new do |env|
200
+ env.CFile("#{env.build_root}/parser.tab.cc", "parser.yy")
201
+ end
202
+ ```
203
+
193
204
  ## Details
194
205
 
195
- ### Default Builders
206
+ ### Builders
196
207
 
197
- Rscons ships with a number of default builders:
208
+ Rscons ships with a number of builders:
198
209
 
210
+ * CFile, which builds a C or C++ source file from a lex or yacc input file
211
+ * Disassemble, which disassembles an object file to a disassembly listing
199
212
  * Library, which collects object files into a static library archive file
200
213
  * Object, which compiles source files to produce an object file
214
+ * Preprocess, which invokes the C/C++ preprocessor on a source file
201
215
  * Program, which links object files to produce an executable
202
216
 
203
217
  If you want to create an Environment that does not contain any builders,
204
218
  you can use the `exclude_builders` key to the Environment constructor.
205
219
 
220
+ #### CFile
221
+
222
+ ```ruby
223
+ env.CFile(target, source)
224
+ # Example
225
+ env.CFile("parser.c", "parser.y")
226
+ ```
227
+
228
+ The CFile builder will generate a C or C++ source file from a lex (.l, .ll)
229
+ or yacc (.y, .yy) input file.
230
+
231
+ #### Disassemble
232
+
233
+ ```ruby
234
+ env.Disassemble(target, source)
235
+ # Example
236
+ env.Disassemble("module.dis", "module.o")
237
+ ```
238
+
239
+ The Disassemble builder generates a disassembly listing using objdump from
240
+ and object file.
241
+
242
+ #### Library
243
+
244
+ ```ruby
245
+ env.Library(target, sources)
246
+ # Example
247
+ env.Library("lib.a", Dir["src/**/*.c"])
248
+ ```
249
+
250
+ The Library builder creates a static library archive from the given source
251
+ files.
252
+
253
+ #### Object
254
+
255
+ ```ruby
256
+ env.Object(target, sources)
257
+ # Example
258
+ env.Object("module.o", "module.c")
259
+ ```
260
+
261
+ The Object builder compiles the given sources to an object file.
262
+
263
+ #### Preprocess
264
+
265
+ ```ruby
266
+ env.Preprocess(target, source)
267
+ # Example
268
+ env.Preprocess("module-preprocessed.cc", "module.cc")
269
+ ```
270
+
271
+ The Preprocess builder invokes either ${CC} or ${CXX} (depending on if the
272
+ source contains an extension in ${CXXSUFFIX} or not) and writes the
273
+ preprocessed output to the target file.
274
+
275
+ #### Program
276
+
277
+ ```ruby
278
+ env.Program(target, sources)
279
+ # Example
280
+ env.Program("myprog", Dir["src/**/*.cc"])
281
+ ```
282
+
283
+ The Program builder compiles and links the given sources to an executable file.
284
+ Object files or source files can be given as `sources`.
285
+
206
286
  ### Managing Environments
207
287
 
208
288
  An Rscons::Environment consists of:
@@ -214,10 +294,17 @@ An Rscons::Environment consists of:
214
294
  * a collection of targets to build
215
295
  * a collection of build hooks
216
296
 
217
- When cloning an environment, the construction variables and builders are
218
- cloned, but the new environment does not inherit any of the targets, build
297
+ When cloning an environment, by default the construction variables and builders
298
+ are cloned, but the new environment does not inherit any of the targets, build
219
299
  hooks, build directories, or the build root from the source environment.
220
300
 
301
+ The set of environment attributes that are cloned is controllable via the
302
+ `:clone` option to the `#clone` method.
303
+ For example, `env.clone(clone: :all)` will include construction variables,
304
+ builders, build hooks, build directories, and the build root.
305
+
306
+ The set of pending targets is never cloned.
307
+
221
308
  Cloned environments contain "deep copies" of construction variables.
222
309
  For example, in:
223
310
 
@@ -233,8 +320,7 @@ cloned_env["CPPPATH"] << "three"
233
320
  ### Construction Variable Naming
234
321
 
235
322
  * uppercase strings - the default construction variables that Rscons uses
236
- * lowercase symbols - Rscons options
237
- * lowercase strings - reserved as user-defined construction variables
323
+ * symbols, lowercase strings - reserved as user-defined construction variables
238
324
 
239
325
  ### API documentation
240
326
 
@@ -243,6 +329,22 @@ http://rubydoc.info/github/holtrop/rscons/frames.
243
329
 
244
330
  ## Release Notes
245
331
 
332
+ ### v1.4.0
333
+
334
+ - add CFile builder
335
+ - add Disassemble builder
336
+ - add Preprocess builder
337
+ - pass the Environment object to build hooks in the :env key of the build_op parameter
338
+ - expand target/source paths beginning with "^/" to be relative to the Environment's build root
339
+ - many performance improvements, including:
340
+ - use JSON instead of YAML for the cache to improve loading speed (Issue #7)
341
+ - store a hash of the build command instead of the full command contents in the cache
342
+ - implement copy-on-write semantics for construction variables when cloning Environments
343
+ - only load the cache once instead of on each Environment#process
344
+ - only write the cache when something has changed
345
+ - fix Cache#mkdir_p to handle relative paths (Issue #5)
346
+ - flush the cache to disk if a builder raises an exception (Issue #4)
347
+
246
348
  ### v1.3.0
247
349
 
248
350
  - change Environment#execute() options parameter to accept the following options keys:
@@ -5,15 +5,21 @@ require_relative "rscons/varset"
5
5
  require_relative "rscons/version"
6
6
 
7
7
  # default builders
8
+ require_relative "rscons/builders/cfile"
9
+ require_relative "rscons/builders/disassemble"
8
10
  require_relative "rscons/builders/library"
9
11
  require_relative "rscons/builders/object"
12
+ require_relative "rscons/builders/preprocess"
10
13
  require_relative "rscons/builders/program"
11
14
 
12
15
  # Namespace module for rscons classes
13
16
  module Rscons
14
17
  DEFAULT_BUILDERS = [
18
+ :CFile,
19
+ :Disassemble,
15
20
  :Library,
16
21
  :Object,
22
+ :Preprocess,
17
23
  :Program,
18
24
  ]
19
25
 
@@ -21,7 +27,7 @@ module Rscons
21
27
 
22
28
  # Remove all generated files
23
29
  def self.clean
24
- cache = Cache.new
30
+ cache = Cache.instance
25
31
  # remove all built files
26
32
  cache.targets.each do |target|
27
33
  FileUtils.rm_f(target)
@@ -33,7 +39,7 @@ module Rscons
33
39
  Dir.rmdir(directory) rescue nil
34
40
  end
35
41
  end
36
- Cache.clear
42
+ cache.clear
37
43
  end
38
44
 
39
45
  # Return whether the given path is an absolute filesystem path or not
@@ -0,0 +1,40 @@
1
+ module Rscons
2
+ module Builders
3
+ # Build a C or C++ source file given a lex (.l, .ll) or yacc (.y, .yy)
4
+ # input file.
5
+ #
6
+ # Examples::
7
+ # env.CFile("parser.tab.cc", "parser.yy")
8
+ # env.CFile("lex.yy.cc", "parser.ll")
9
+ class CFile < Builder
10
+ def default_variables(env)
11
+ {
12
+ "YACC" => "bison",
13
+ "YACC_FLAGS" => ["-d"],
14
+ "YACC_CMD" => ["${YACC}", "${YACC_FLAGS}", "-o", "${_TARGET}", "${_SOURCES}"],
15
+ "LEX" => "flex",
16
+ "LEX_FLAGS" => [],
17
+ "LEX_CMD" => ["${LEX}", "${LEX_FLAGS}", "-o", "${_TARGET}", "${_SOURCES}"],
18
+ }
19
+ end
20
+
21
+ def run(target, sources, cache, env, vars)
22
+ vars = vars.merge({
23
+ "_TARGET" => target,
24
+ "_SOURCES" => sources,
25
+ })
26
+ cmd =
27
+ case
28
+ when sources.first.end_with?(".l", ".ll")
29
+ "LEX"
30
+ when sources.first.end_with?(".y", ".yy")
31
+ "YACC"
32
+ else
33
+ raise "Unknown source file #{sources.first.inspect} for CFile builder"
34
+ end
35
+ command = env.build_command(env["#{cmd}_CMD"], vars)
36
+ standard_build("#{cmd} #{target}", target, command, sources, env, cache)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,25 @@
1
+ module Rscons
2
+ module Builders
3
+ # The Disassemble builder produces a disassembly listing of a source file.
4
+ class Disassemble < Builder
5
+ def default_variables(env)
6
+ {
7
+ "OBJDUMP" => "objdump",
8
+ "DISASM_CMD" => ["${OBJDUMP}", "${DISASM_FLAGS}", "${_SOURCES}"],
9
+ "DISASM_FLAGS" => ["--disassemble", "--source"],
10
+ }
11
+ end
12
+
13
+ def run(target, sources, cache, env, vars)
14
+ vars = vars.merge("_SOURCES" => sources)
15
+ command = env.build_command(env["DISASM_CMD"], vars)
16
+ unless cache.up_to_date?(target, command, sources, env)
17
+ cache.mkdir_p(File.dirname(target))
18
+ return false unless env.execute("Disassemble #{target}", command, options: {out: target})
19
+ cache.register_build(target, command, sources, env)
20
+ end
21
+ target
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,7 +1,7 @@
1
1
  module Rscons
2
2
  module Builders
3
3
  # A default Rscons builder that produces a static library archive.
4
- class Rscons::Builders::Library < Rscons::Builder
4
+ class Library < Builder
5
5
  def default_variables(env)
6
6
  {
7
7
  'AR' => 'ar',
@@ -0,0 +1,25 @@
1
+ module Rscons
2
+ module Builders
3
+ # The Preprocess builder invokes the C preprocessor
4
+ class Preprocess < Builder
5
+ def default_variables(env)
6
+ {
7
+ "CPP_CMD" => ["${_PREPROCESS_CC}", "-E", "-o", "${_TARGET}", "-I${CPPPATH}", "${CPPFLAGS}", "${CFLAGS}", "${_SOURCES}"],
8
+ }
9
+ end
10
+
11
+ def run(target, sources, cache, env, vars)
12
+ pp_cc = if sources.find {|s| s.end_with?(*env["CXXSUFFIX"])}
13
+ env["CXX"]
14
+ else
15
+ env["CC"]
16
+ end
17
+ vars = vars.merge("_PREPROCESS_CC" => pp_cc,
18
+ "_TARGET" => target,
19
+ "_SOURCES" => sources)
20
+ command = env.build_command(env["CPP_CMD"], vars)
21
+ standard_build("Preprocess #{target}", target, command, sources, env, cache)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -2,7 +2,7 @@ module Rscons
2
2
  module Builders
3
3
  # A default Rscons builder that knows how to link object files into an
4
4
  # executable program.
5
- class Rscons::Builders::Program < Rscons::Builder
5
+ class Program < Builder
6
6
  def default_variables(env)
7
7
  {
8
8
  'LD' => nil,
@@ -1,88 +1,87 @@
1
- require 'yaml'
2
- require 'fileutils'
3
- require 'digest/md5'
4
- require 'set'
5
- require 'rscons/version'
1
+ require "digest/md5"
2
+ require "fileutils"
3
+ require "json"
4
+ require "set"
5
+ require "singleton"
6
+ require "rscons/version"
6
7
 
7
8
  module Rscons
8
9
  # The Cache class keeps track of file checksums, build target commands and
9
- # dependencies in a YAML file which persists from one invocation to the next.
10
+ # dependencies in a JSON file which persists from one invocation to the next.
10
11
  # Example cache:
11
12
  # {
12
- # version: '1.2.3',
13
- # targets: {
14
- # 'program' => {
15
- # 'checksum' => 'A1B2C3D4',
16
- # 'command' => ['gcc', '-o', 'program', 'program.o'],
17
- # 'deps' => [
13
+ # "version" => "1.2.3",
14
+ # "targets" => {
15
+ # "program" => {
16
+ # "checksum" => "A1B2C3D4",
17
+ # "command" => "13543518FE",
18
+ # "deps" => [
18
19
  # {
19
- # 'fname' => 'program.o',
20
- # 'checksum' => '87654321',
20
+ # "fname" => "program.o",
21
+ # "checksum" => "87654321",
21
22
  # },
22
23
  # ],
23
- # 'user_deps' => [
24
+ # "user_deps" => [
24
25
  # {
25
- # 'fname' => 'lscript.ld',
26
- # 'checksum' => '77551133',
26
+ # "fname" => "lscript.ld",
27
+ # "checksum" => "77551133",
27
28
  # },
28
29
  # ],
29
30
  # },
30
- # 'program.o' => {
31
- # 'checksum' => '87654321',
32
- # 'command' => ['gcc', '-c', '-o', 'program.o', 'program.c'],
33
- # 'deps' => [
31
+ # "program.o" => {
32
+ # "checksum" => "87654321",
33
+ # "command" => "98765ABCD",
34
+ # "deps" => [
34
35
  # {
35
- # 'fname' => 'program.c',
36
- # 'checksum' => '456789ABC',
36
+ # "fname" => "program.c",
37
+ # "checksum" => "456789ABC",
37
38
  # },
38
39
  # {
39
- # 'fname' => 'program.h',
40
- # 'checksum' => '7979764643',
40
+ # "fname" => "program.h",
41
+ # "checksum" => "7979764643",
41
42
  # },
42
43
  # ],
43
- # 'user_deps' => [],
44
+ # "user_deps" => [],
44
45
  # }
45
46
  # },
46
- # directories: {
47
- # 'build' => true,
48
- # 'build/one' => true,
49
- # 'build/two' => true,
47
+ # "directories" => {
48
+ # "build" => true,
49
+ # "build/one" => true,
50
+ # "build/two" => true,
50
51
  # },
51
52
  # }
52
53
  class Cache
53
- #### Constants
54
+ include Singleton
54
55
 
55
56
  # Name of the file to store cache information in
56
- CACHE_FILE = '.rsconscache'
57
+ CACHE_FILE = ".rsconscache"
57
58
 
58
- #### Class Methods
59
+ # Create a Cache object and load in the previous contents from the cache
60
+ # file.
61
+ def initialize
62
+ initialize!
63
+ end
59
64
 
60
- # Remove the cache file
61
- def self.clear
65
+ # Remove the cache file.
66
+ def clear
62
67
  FileUtils.rm_f(CACHE_FILE)
68
+ initialize!
63
69
  end
64
70
 
65
- #### Instance Methods
66
-
67
- # Create a Cache object and load in the previous contents from the cache
68
- # file.
69
- def initialize
70
- @cache = YAML.load(File.read(CACHE_FILE)) rescue {}
71
- unless @cache.is_a?(Hash)
72
- $stderr.puts "Warning: #{CACHE_FILE} was corrupt. Contents:\n#{@cache.inspect}"
73
- @cache = {}
74
- end
75
- @cache[:targets] ||= {}
76
- @cache[:directories] ||= {}
71
+ # Clear the cached file checksums.
72
+ def clear_checksum_cache!
77
73
  @lookup_checksums = {}
78
74
  end
79
75
 
80
76
  # Write the cache to disk to be loaded next time.
81
77
  def write
82
- @cache[:version] = VERSION
83
- File.open(CACHE_FILE, 'w') do |fh|
84
- fh.puts(YAML.dump(@cache))
78
+ if @dirty || (@cache["version"] != VERSION)
79
+ @cache["version"] = VERSION
80
+ File.open(CACHE_FILE, "w") do |fh|
81
+ fh.puts(JSON.dump(@cache))
82
+ end
85
83
  end
84
+ @dirty = false
86
85
  end
87
86
 
88
87
  # Check if target(s) are up to date
@@ -90,11 +89,12 @@ module Rscons
90
89
  # @param command [String, Array] The command used to build the target.
91
90
  # @param deps [Array] List of the target's dependency files.
92
91
  # @param env [Environment] The Rscons::Environment.
93
- # @param options [Hash] Optional options. Can contain the following keys:
94
- # :strict_deps::
95
- # Only consider a target up to date if its list of dependencies is
96
- # exactly equal (including order) to the cached list of dependencies
97
- # @return true if the targets are all up to date, meaning that,
92
+ # @param options [Hash] Optional options.
93
+ # @option options [Boolean] :strict_deps
94
+ # Only consider a target up to date if its list of dependencies is
95
+ # exactly equal (including order) to the cached list of dependencies
96
+ # @return [Boolean]
97
+ # True value if the targets are all up to date, meaning that,
98
98
  # for each target:
99
99
  # - the target exists on disk
100
100
  # - the cache has information for the target
@@ -111,16 +111,16 @@ module Rscons
111
111
  return false unless File.exists?(target)
112
112
 
113
113
  # target must be registered in the cache
114
- return false unless @cache[:targets].has_key?(target)
114
+ return false unless @cache["targets"].has_key?(target)
115
115
 
116
116
  # target must have the same checksum as when it was built last
117
- return false unless @cache[:targets][target][:checksum] == lookup_checksum(target)
117
+ return false unless @cache["targets"][target]["checksum"] == lookup_checksum(target)
118
118
 
119
119
  # command used to build target must be identical
120
- return false unless @cache[:targets][target][:command] == command
120
+ return false unless @cache["targets"][target]["command"] == Digest::MD5.hexdigest(command.inspect)
121
121
 
122
- cached_deps = @cache[:targets][target][:deps] || []
123
- cached_deps_fnames = cached_deps.map { |dc| dc[:fname] }
122
+ cached_deps = @cache["targets"][target]["deps"] || []
123
+ cached_deps_fnames = cached_deps.map { |dc| dc["fname"] }
124
124
  if options[:strict_deps]
125
125
  # depedencies passed in must exactly equal those in the cache
126
126
  return false unless deps == cached_deps_fnames
@@ -131,13 +131,13 @@ module Rscons
131
131
 
132
132
  # set of user dependencies must match
133
133
  user_deps = env.get_user_deps(target) || []
134
- cached_user_deps = @cache[:targets][target][:user_deps] || []
135
- cached_user_deps_fnames = cached_user_deps.map { |dc| dc[:fname] }
134
+ cached_user_deps = @cache["targets"][target]["user_deps"] || []
135
+ cached_user_deps_fnames = cached_user_deps.map { |dc| dc["fname"] }
136
136
  return false unless user_deps == cached_user_deps_fnames
137
137
 
138
138
  # all cached dependencies must have their checksums match
139
139
  (cached_deps + cached_user_deps).each do |dep_cache|
140
- return false unless dep_cache[:checksum] == lookup_checksum(dep_cache[:fname])
140
+ return false unless dep_cache["checksum"] == lookup_checksum(dep_cache["fname"])
141
141
  end
142
142
  end
143
143
 
@@ -148,54 +148,70 @@ module Rscons
148
148
  # @param targets [String, Array] The name of the target(s) built.
149
149
  # @param command [String, Array] The command used to build the target.
150
150
  # @param deps [Array] List of dependencies for the target.
151
- # @param env [Environment] The Rscons::Environment.
151
+ # @param env [Environment] The {Rscons::Environment}.
152
152
  def register_build(targets, command, deps, env)
153
153
  Array(targets).each do |target|
154
- @cache[:targets][target.encode(__ENCODING__)] = {
155
- command: command,
156
- checksum: calculate_checksum(target),
157
- deps: deps.map do |dep|
154
+ @cache["targets"][target] = {
155
+ "command" => Digest::MD5.hexdigest(command.inspect),
156
+ "checksum" => calculate_checksum(target),
157
+ "deps" => deps.map do |dep|
158
158
  {
159
- fname: dep.encode(__ENCODING__),
160
- checksum: lookup_checksum(dep),
159
+ "fname" => dep,
160
+ "checksum" => lookup_checksum(dep),
161
161
  }
162
162
  end,
163
- user_deps: (env.get_user_deps(target) || []).map do |dep|
163
+ "user_deps" => (env.get_user_deps(target) || []).map do |dep|
164
164
  {
165
- fname: dep.encode(__ENCODING__),
166
- checksum: lookup_checksum(dep),
165
+ "fname" => dep,
166
+ "checksum" => lookup_checksum(dep),
167
167
  }
168
168
  end,
169
169
  }
170
+ @dirty = true
170
171
  end
171
172
  end
172
173
 
173
174
  # Return a list of targets that have been built
174
175
  def targets
175
- @cache[:targets].keys
176
+ @cache["targets"].keys
176
177
  end
177
178
 
178
179
  # Make any needed directories and record the ones that are created for
179
180
  # removal upon a "clean" operation.
180
181
  def mkdir_p(path)
181
182
  parts = path.split(/[\\\/]/)
182
- (0..parts.size-1).each do |i|
183
- subpath = File.join(*parts[0, i + 1]).encode(__ENCODING__)
183
+ parts.each_index do |i|
184
+ next if parts[i] == ""
185
+ subpath = File.join(*parts[0, i + 1])
184
186
  unless File.exists?(subpath)
185
187
  FileUtils.mkdir(subpath)
186
- @cache[:directories][subpath] = true
188
+ @cache["directories"][subpath] = true
189
+ @dirty = true
187
190
  end
188
191
  end
189
192
  end
190
193
 
191
194
  # Return a list of directories which were created as a part of the build
192
195
  def directories
193
- @cache[:directories].keys
196
+ @cache["directories"].keys
194
197
  end
195
198
 
196
- # Private Instance Methods
197
199
  private
198
200
 
201
+ # Create a Cache object and load in the previous contents from the cache
202
+ # file.
203
+ def initialize!
204
+ @cache = JSON.load(File.read(CACHE_FILE)) rescue {}
205
+ unless @cache.is_a?(Hash)
206
+ $stderr.puts "Warning: #{CACHE_FILE} was corrupt. Contents:\n#{@cache.inspect}"
207
+ @cache = {}
208
+ end
209
+ @cache["targets"] ||= {}
210
+ @cache["directories"] ||= {}
211
+ @lookup_checksums = {}
212
+ @dirty = false
213
+ end
214
+
199
215
  # Return a file's checksum, or the previously calculated checksum for
200
216
  # the same file
201
217
  # @param file [String] The file name.
@@ -206,7 +222,7 @@ module Rscons
206
222
  # Calculate and return a file's checksum
207
223
  # @param file [String] The file name.
208
224
  def calculate_checksum(file)
209
- @lookup_checksums[file] = Digest::MD5.hexdigest(File.read(file, mode: 'rb')).encode(__ENCODING__) rescue ''
225
+ @lookup_checksums[file] = Digest::MD5.hexdigest(File.read(file, mode: "rb")) rescue ""
210
226
  end
211
227
  end
212
228
  end