rscons 1.4.3 → 1.5.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.
Files changed (48) hide show
  1. data/lib/rscons.rb +75 -5
  2. data/lib/rscons/build_target.rb +36 -0
  3. data/lib/rscons/builder.rb +41 -3
  4. data/lib/rscons/builders/cfile.rb +15 -0
  5. data/lib/rscons/builders/disassemble.rb +15 -0
  6. data/lib/rscons/builders/library.rb +17 -2
  7. data/lib/rscons/builders/object.rb +37 -5
  8. data/lib/rscons/builders/preprocess.rb +15 -0
  9. data/lib/rscons/builders/program.rb +42 -2
  10. data/lib/rscons/cache.rb +26 -6
  11. data/lib/rscons/environment.rb +259 -19
  12. data/lib/rscons/varset.rb +33 -10
  13. data/lib/rscons/version.rb +1 -1
  14. data/rscons.gemspec +8 -10
  15. metadata +38 -103
  16. checksums.yaml +0 -15
  17. data/.gitignore +0 -18
  18. data/.rspec +0 -2
  19. data/Gemfile +0 -4
  20. data/README.md +0 -384
  21. data/Rakefile.rb +0 -26
  22. data/build_tests/build_dir/src/one/one.c +0 -6
  23. data/build_tests/build_dir/src/two/two.c +0 -7
  24. data/build_tests/build_dir/src/two/two.h +0 -6
  25. data/build_tests/clone_env/src/program.c +0 -6
  26. data/build_tests/custom_builder/program.c +0 -7
  27. data/build_tests/d/main.d +0 -6
  28. data/build_tests/header/header.c +0 -7
  29. data/build_tests/header/header.h +0 -6
  30. data/build_tests/library/one.c +0 -8
  31. data/build_tests/library/three.c +0 -0
  32. data/build_tests/library/two.c +0 -0
  33. data/build_tests/simple/simple.c +0 -6
  34. data/build_tests/simple_cc/simple.cc +0 -8
  35. data/build_tests/two_sources/one.c +0 -8
  36. data/build_tests/two_sources/two.c +0 -3
  37. data/spec/build_tests_spec.rb +0 -527
  38. data/spec/rscons/builders/cfile_spec.rb +0 -28
  39. data/spec/rscons/builders/disassemble_spec.rb +0 -17
  40. data/spec/rscons/builders/library_spec.rb +0 -18
  41. data/spec/rscons/builders/object_spec.rb +0 -23
  42. data/spec/rscons/builders/preprocess_spec.rb +0 -18
  43. data/spec/rscons/builders/program_spec.rb +0 -18
  44. data/spec/rscons/cache_spec.rb +0 -271
  45. data/spec/rscons/environment_spec.rb +0 -361
  46. data/spec/rscons/varset_spec.rb +0 -163
  47. data/spec/rscons_spec.rb +0 -26
  48. data/spec/spec_helper.rb +0 -7
@@ -84,7 +84,8 @@ module Rscons
84
84
  @dirty = false
85
85
  end
86
86
 
87
- # Check if target(s) are up to date
87
+ # Check if target(s) are up to date.
88
+ #
88
89
  # @param targets [String, Array] The name(s) of the target file(s).
89
90
  # @param command [String, Array] The command used to build the target.
90
91
  # @param deps [Array] List of the target's dependency files.
@@ -93,6 +94,7 @@ module Rscons
93
94
  # @option options [Boolean] :strict_deps
94
95
  # Only consider a target up to date if its list of dependencies is
95
96
  # exactly equal (including order) to the cached list of dependencies
97
+ #
96
98
  # @return [Boolean]
97
99
  # True value if the targets are all up to date, meaning that,
98
100
  # for each target:
@@ -144,11 +146,14 @@ module Rscons
144
146
  true
145
147
  end
146
148
 
147
- # Store cache information about target(s) built by a builder
149
+ # Store cache information about target(s) built by a builder.
150
+ #
148
151
  # @param targets [String, Array] The name of the target(s) built.
149
152
  # @param command [String, Array] The command used to build the target.
150
153
  # @param deps [Array] List of dependencies for the target.
151
154
  # @param env [Environment] The {Rscons::Environment}.
155
+ #
156
+ # @return [void]
152
157
  def register_build(targets, command, deps, env)
153
158
  Array(targets).each do |target|
154
159
  @cache["targets"][target] = {
@@ -171,13 +176,19 @@ module Rscons
171
176
  end
172
177
  end
173
178
 
174
- # Return a list of targets that have been built
179
+ # Return a list of targets that have been built.
180
+ #
181
+ # @return [Array<String>] List of targets that have been built.
175
182
  def targets
176
183
  @cache["targets"].keys
177
184
  end
178
185
 
179
186
  # Make any needed directories and record the ones that are created for
180
187
  # removal upon a "clean" operation.
188
+ #
189
+ # @param path [String] Directory to create.
190
+ #
191
+ # @return [void]
181
192
  def mkdir_p(path)
182
193
  parts = path.split(/[\\\/]/)
183
194
  parts.each_index do |i|
@@ -191,7 +202,10 @@ module Rscons
191
202
  end
192
203
  end
193
204
 
194
- # Return a list of directories which were created as a part of the build
205
+ # Return a list of directories which were created as a part of the build.
206
+ #
207
+ # @return [Array<String>]
208
+ # List of directories which were created as a part of the build.
195
209
  def directories
196
210
  @cache["directories"].keys
197
211
  end
@@ -213,14 +227,20 @@ module Rscons
213
227
  end
214
228
 
215
229
  # Return a file's checksum, or the previously calculated checksum for
216
- # the same file
230
+ # the same file.
231
+ #
217
232
  # @param file [String] The file name.
233
+ #
234
+ # @return [String] The file's checksum.
218
235
  def lookup_checksum(file)
219
236
  @lookup_checksums[file] || calculate_checksum(file)
220
237
  end
221
238
 
222
- # Calculate and return a file's checksum
239
+ # Calculate and return a file's checksum.
240
+ #
223
241
  # @param file [String] The file name.
242
+ #
243
+ # @return [String] The file's checksum.
224
244
  def calculate_checksum(file)
225
245
  @lookup_checksums[file] = Digest::MD5.hexdigest(File.read(file, mode: "rb")) rescue ""
226
246
  end
@@ -1,30 +1,39 @@
1
- require 'set'
2
- require 'fileutils'
1
+ require "fileutils"
2
+ require "set"
3
+ require "shellwords"
3
4
 
4
5
  module Rscons
5
6
  # The Environment class is the main programmatic interface to Rscons. It
6
7
  # contains a collection of construction variables, options, builders, and
7
8
  # rules for building targets.
8
9
  class Environment
9
- # Hash of +{"builder_name" => builder_object}+ pairs.
10
+ # @return [Hash] Set of \{"builder_name" => builder_object} pairs.
10
11
  attr_reader :builders
11
12
 
12
13
  # :command, :short, or :off
13
14
  attr_accessor :echo
14
15
 
15
- # String or +nil+
16
+ # @return [String, nil] The build root.
16
17
  attr_reader :build_root
18
+
19
+ # Set the build root.
20
+ #
21
+ # @param build_root [String] The build root.
17
22
  def build_root=(build_root)
18
23
  @build_root = build_root
19
24
  @build_root.gsub!('\\', '/') if @build_root
20
25
  end
21
26
 
22
27
  # Create an Environment object.
28
+ #
23
29
  # @param options [Hash]
24
- # Possible options keys:
25
- # :echo => :command, :short, or :off (default :short)
26
- # :build_root => String specifying build root directory (default nil)
27
- # :exclude_builders => true to omit adding default builders (default false)
30
+ # @option options [Symbol] :echo
31
+ # :command, :short, or :off (default :short)
32
+ # @option options [String] :build_root
33
+ # Build root directory (default nil)
34
+ # @option options [Boolean] :exclude_builders
35
+ # Whether to omit adding default builders (default false)
36
+ #
28
37
  # If a block is given, the Environment object is yielded to the block and
29
38
  # when the block returns, the {#process} method is automatically called.
30
39
  def initialize(options = {})
@@ -107,6 +116,10 @@ module Rscons
107
116
  end
108
117
 
109
118
  # Add a {Builder} object to the Environment.
119
+ #
120
+ # @param builder [Builder] The {Builder} object to add.
121
+ #
122
+ # @return [void]
110
123
  def add_builder(builder)
111
124
  @builders[builder.name] = builder
112
125
  var_defs = builder.default_variables(self)
@@ -118,19 +131,45 @@ module Rscons
118
131
  end
119
132
 
120
133
  # Add a build hook to the Environment.
134
+ #
135
+ # @yield [build_op]
136
+ # Invoke the given block with the current build operation.
137
+ # @yieldparam build_op [Hash]
138
+ # Hash with keys:
139
+ # - :builder - The builder object in use.
140
+ # - :target - Target file name.
141
+ # - :sources - List of source file(s).
142
+ # - :vars - Set of construction variable values in use.
143
+ # - :env - The Environment invoking the builder.
144
+ #
145
+ # @return [void]
121
146
  def add_build_hook(&block)
122
147
  @build_hooks << block
123
148
  end
124
149
 
125
150
  # Specify a build directory for this Environment.
151
+ #
126
152
  # Source files from src_dir will produce object files under obj_dir.
153
+ #
154
+ # @param src_dir [String] Path to the source directory.
155
+ # @param obj_dir [String] Path to the object directory.
156
+ #
157
+ # @return [void]
127
158
  def build_dir(src_dir, obj_dir)
128
159
  src_dir = src_dir.gsub('\\', '/') if src_dir.is_a?(String)
129
160
  @build_dirs << [src_dir, obj_dir]
130
161
  end
131
162
 
132
- # Return the file name to be built from source_fname with suffix suffix.
163
+ # Return the file name to be built from +source_fname+ with suffix
164
+ # +suffix+.
165
+ #
133
166
  # This method takes into account the Environment's build directories.
167
+ #
168
+ # @param source_fname [String] Source file name.
169
+ # @param suffix [String] Suffix, including "." if desired.
170
+ #
171
+ # @return [String]
172
+ # The file name to be built from +source_fname+ with suffix +suffix+.
134
173
  def get_build_fname(source_fname, suffix)
135
174
  build_fname = Rscons.set_suffix(source_fname, suffix).gsub('\\', '/')
136
175
  found_match = @build_dirs.find do |src_dir, obj_dir|
@@ -150,26 +189,32 @@ module Rscons
150
189
  end
151
190
 
152
191
  # Access a construction variable or environment option.
192
+ #
153
193
  # @see VarSet#[]
154
194
  def [](*args)
155
195
  @varset.send(:[], *args)
156
196
  end
157
197
 
158
198
  # Set a construction variable or environment option.
199
+ #
159
200
  # @see VarSet#[]=
160
201
  def []=(*args)
161
202
  @varset.send(:[]=, *args)
162
203
  end
163
204
 
164
205
  # Add a set of construction variables or environment options.
206
+ #
165
207
  # @see VarSet#append
166
208
  def append(*args)
167
209
  @varset.append(*args)
168
210
  end
169
211
 
170
- # Build all target specified in the Environment.
212
+ # Build all build targets specified in the Environment.
213
+ #
171
214
  # When a block is passed to Environment.new, this method is automatically
172
215
  # called after the block returns.
216
+ #
217
+ # @return [void]
173
218
  def process
174
219
  unless @targets.empty?
175
220
  expand_paths!
@@ -226,13 +271,16 @@ module Rscons
226
271
  end
227
272
  alias_method :build_command, :expand_varref
228
273
 
229
- # Execute a builder command
274
+ # Execute a builder command.
275
+ #
230
276
  # @param short_desc [String] Message to print if the Environment's echo
231
277
  # mode is set to :short
232
278
  # @param command [Array] The command to execute.
233
279
  # @param options [Hash] Optional options, possible keys:
234
280
  # - :env - environment Hash to pass to Kernel#system.
235
281
  # - :options - options Hash to pass to Kernel#system.
282
+ #
283
+ # @return [true,false,nil] Return value from Kernel.system().
236
284
  def execute(short_desc, command, options = {})
237
285
  print_command = proc do
238
286
  puts command.map { |c| c =~ /\s/ ? "'#{c}'" : c }.join(' ')
@@ -244,7 +292,7 @@ module Rscons
244
292
  end
245
293
  env_args = options[:env] ? [options[:env]] : []
246
294
  options_args = options[:options] ? [options[:options]] : []
247
- system(*env_args, *command, *options_args).tap do |result|
295
+ system(*env_args, *Rscons.command_executer, *command, *options_args).tap do |result|
248
296
  unless result or @echo == :command
249
297
  $stdout.write "Failed command was: "
250
298
  print_command.call
@@ -252,6 +300,14 @@ module Rscons
252
300
  end
253
301
  end
254
302
 
303
+ # Define a build target.
304
+ #
305
+ # @param method [Symbol] Method name.
306
+ # @param args [Array] Method arguments.
307
+ #
308
+ # @return [BuildTarget]
309
+ # The {BuildTarget} object registered, if the method called is a
310
+ # {Builder}.
255
311
  def method_missing(method, *args)
256
312
  if @builders.has_key?(method.to_s)
257
313
  target, sources, vars, *rest = args
@@ -259,12 +315,24 @@ module Rscons
259
315
  raise "Unexpected construction variable set: #{vars.inspect}"
260
316
  end
261
317
  sources = Array(sources)
262
- add_target(target, @builders[method.to_s], sources, vars, rest)
318
+ builder = @builders[method.to_s]
319
+ build_target = builder.create_build_target(env: self, target: target, sources: sources)
320
+ add_target(build_target.to_s, builder, sources, vars, rest)
321
+ build_target
263
322
  else
264
323
  super
265
324
  end
266
325
  end
267
326
 
327
+ # Add a build target.
328
+ #
329
+ # @param target [String] Build target file name.
330
+ # @param builder [Builder] The {Builder} to use to build the target.
331
+ # @param sources [Array<String>] Source file name(s).
332
+ # @param vars [Hash] Construction variable overrides.
333
+ # @param args [Object] Any extra arguments passed to the {Builder}.
334
+ #
335
+ # @return [void]
268
336
  def add_target(target, builder, sources, vars, args)
269
337
  @targets[target] = {
270
338
  builder: builder,
@@ -274,27 +342,40 @@ module Rscons
274
342
  }
275
343
  end
276
344
 
277
- # Manually record a given target as depending on the specified
278
- # dependency files.
345
+ # Manually record a given target as depending on the specified files.
346
+ #
347
+ # @param target [String,BuildTarget] Target file.
348
+ # @param user_deps [Array<String>] Dependency files.
349
+ #
350
+ # @return [void]
279
351
  def depends(target, *user_deps)
352
+ target = target.to_s
280
353
  @user_deps[target] ||= []
281
354
  @user_deps[target] = (@user_deps[target] + user_deps).uniq
282
355
  end
283
356
 
284
- # Return the list of user dependencies for a given target, or +nil+ for
285
- # none.
357
+ # Return the list of user dependencies for a given target.
358
+ #
359
+ # @param target [String] Target file name.
360
+ #
361
+ # @return [Array<String>,nil]
362
+ # List of user-specified dependencies for the target, or nil if none were
363
+ # specified.
286
364
  def get_user_deps(target)
287
365
  @user_deps[target]
288
366
  end
289
367
 
290
368
  # Build a list of source files into files containing one of the suffixes
291
369
  # given by suffixes.
370
+ #
292
371
  # This method is used internally by Rscons builders.
372
+ #
293
373
  # @param sources [Array] List of source files to build.
294
374
  # @param suffixes [Array] List of suffixes to try to convert source files into.
295
375
  # @param cache [Cache] The Cache.
296
376
  # @param vars [Hash] Extra variables to pass to the builder.
297
- # Return a list of the converted file names.
377
+ #
378
+ # @return [Array<String>] List of the converted file name(s).
298
379
  def build_sources(sources, suffixes, cache, vars)
299
380
  sources.map do |source|
300
381
  if source.end_with?(*suffixes)
@@ -316,12 +397,14 @@ module Rscons
316
397
  end
317
398
 
318
399
  # Invoke a builder to build the given target based on the given sources.
400
+ #
319
401
  # @param builder [Builder] The Builder to use.
320
402
  # @param target [String] The target output file.
321
403
  # @param sources [Array] List of source files.
322
404
  # @param cache [Cache] The Cache.
323
405
  # @param vars [Hash] Extra variables to pass to the builder.
324
- # Return the result of the builder's run() method.
406
+ #
407
+ # @return [String,false] Return value from the {Builder}'s +run+ method.
325
408
  def run_builder(builder, target, sources, cache, vars)
326
409
  vars = @varset.merge(vars)
327
410
  @build_hooks.each do |build_hook_block|
@@ -349,6 +432,157 @@ module Rscons
349
432
  path.sub(%r{^\^(?=[\\/])}, @build_root)
350
433
  end
351
434
 
435
+ # Execute a command using the system shell.
436
+ #
437
+ # The shell is automatically determined but can be overridden by the SHELL
438
+ # construction variable. If the SHELL construction variable is specified,
439
+ # the flag to pass to the shell is automatically dtermined but can be
440
+ # overridden by the SHELLFLAG construction variable.
441
+ #
442
+ # @param command [String] Command to execute.
443
+ #
444
+ # @return [String] The command's standard output.
445
+ def shell(command)
446
+ shell_cmd =
447
+ if self["SHELL"]
448
+ flag = self["SHELLFLAG"] || (self["SHELL"] == "cmd" ? "/c" : "-c")
449
+ [self["SHELL"], flag]
450
+ else
451
+ Rscons.get_system_shell
452
+ end
453
+ IO.popen([*shell_cmd, command]) do |io|
454
+ io.read
455
+ end
456
+ end
457
+
458
+ # @!method parse_flags(flags)
459
+ # @!method parse_flags!(flags)
460
+ #
461
+ # Parse command-line flags for compilation/linking options into separate
462
+ # construction variables.
463
+ #
464
+ # The parsed construction variables are returned in a Hash instead of
465
+ # merging them directly to the Environment. They can be merged with
466
+ # {#merge_flags}. The {#parse_flags!} version immediately merges the parsed
467
+ # flags as well.
468
+ #
469
+ # Example:
470
+ # # Import FreeType build options
471
+ # env.parse_flags!("!freetype-config --cflags --libs")
472
+ #
473
+ # @param flags [String]
474
+ # String containing the flags to parse, or if the flags string begins
475
+ # with "!", a shell command to execute using {#shell} to obtain the
476
+ # flags to parse.
477
+ #
478
+ # @return [Hash] Set of construction variables to append.
479
+ def parse_flags(flags)
480
+ if flags =~ /^!(.*)$/
481
+ flags = shell($1)
482
+ end
483
+ rv = {}
484
+ words = Shellwords.split(flags)
485
+ skip = false
486
+ words.each_with_index do |word, i|
487
+ if skip
488
+ skip = false
489
+ next
490
+ end
491
+ append = lambda do |var, val|
492
+ rv[var] ||= []
493
+ rv[var] += val
494
+ end
495
+ handle = lambda do |var, val|
496
+ if val.nil? or val.empty?
497
+ val = words[i + 1]
498
+ skip = true
499
+ end
500
+ if val and not val.empty?
501
+ append[var, [val]]
502
+ end
503
+ end
504
+ if word == "-arch"
505
+ if val = words[i + 1]
506
+ append["CCFLAGS", ["-arch", val]]
507
+ append["LDFLAGS", ["-arch", val]]
508
+ end
509
+ skip = true
510
+ elsif word =~ /^#{self["CPPDEFPREFIX"]}(.*)$/
511
+ handle["CPPDEFINES", $1]
512
+ elsif word == "-include"
513
+ if val = words[i + 1]
514
+ append["CCFLAGS", ["-include", val]]
515
+ end
516
+ skip = true
517
+ elsif word == "-isysroot"
518
+ if val = words[i + 1]
519
+ append["CCFLAGS", ["-isysroot", val]]
520
+ append["LDFLAGS", ["-isysroot", val]]
521
+ end
522
+ skip = true
523
+ elsif word =~ /^#{self["INCPREFIX"]}(.*)$/
524
+ handle["CPPPATH", $1]
525
+ elsif word =~ /^#{self["LIBLINKPREFIX"]}(.*)$/
526
+ handle["LIBS", $1]
527
+ elsif word =~ /^#{self["LIBDIRPREFIX"]}(.*)$/
528
+ handle["LIBPATH", $1]
529
+ elsif word == "-mno-cygwin"
530
+ append["CCFLAGS", [word]]
531
+ append["LDFLAGS", [word]]
532
+ elsif word == "-mwindows"
533
+ append["LDFLAGS", [word]]
534
+ elsif word == "-pthread"
535
+ append["CCFLAGS", [word]]
536
+ append["LDFLAGS", [word]]
537
+ elsif word =~ /^-std=/
538
+ append["CFLAGS", [word]]
539
+ elsif word =~ /^-Wa,(.*)$/
540
+ append["ASFLAGS", $1.split(",")]
541
+ elsif word =~ /^-Wl,(.*)$/
542
+ append["LDFLAGS", $1.split(",")]
543
+ elsif word =~ /^-Wp,(.*)$/
544
+ append["CPPFLAGS", $1.split(",")]
545
+ elsif word.start_with?("-")
546
+ append["CCFLAGS", [word]]
547
+ elsif word.start_with?("+")
548
+ append["CCFLAGS", [word]]
549
+ append["LDFLAGS", [word]]
550
+ else
551
+ append["LIBS", [word]]
552
+ end
553
+ end
554
+ rv
555
+ end
556
+
557
+ def parse_flags!(flags)
558
+ flags = parse_flags(flags)
559
+ merge_flags(flags)
560
+ flags
561
+ end
562
+
563
+ # Merge construction variable flags into this Environment's construction
564
+ # variables.
565
+ #
566
+ # This method does the same thing as {#append}, except that Array values in
567
+ # +flags+ are appended to the end of Array construction variables instead
568
+ # of replacing their contents.
569
+ #
570
+ # @param flags [Hash]
571
+ # Set of construction variables to merge into the current Environment.
572
+ # This can be the value (or a modified version) returned by
573
+ # {#parse_flags}.
574
+ #
575
+ # @return [void]
576
+ def merge_flags(flags)
577
+ flags.each_pair do |key, val|
578
+ if self[key].is_a?(Array) and val.is_a?(Array)
579
+ self[key] += val
580
+ else
581
+ self[key] = val
582
+ end
583
+ end
584
+ end
585
+
352
586
  private
353
587
 
354
588
  # Expand target and source paths before invoking builders.
@@ -356,6 +590,8 @@ module Rscons
356
590
  # This method expand construction variable references in the target and
357
591
  # source file names before passing them to the builder. It also expands
358
592
  # "^/" prefixes to the Environment's build root if a build root is defined.
593
+ #
594
+ # @return [void]
359
595
  def expand_paths!
360
596
  @targets = @targets.reduce({}) do |result, (target, target_params)|
361
597
  sources = target_params[:sources].map do |source|
@@ -370,9 +606,13 @@ module Rscons
370
606
  end
371
607
 
372
608
  # Parse dependencies for a given target from a Makefile.
609
+ #
373
610
  # This method is used internally by Rscons builders.
611
+ #
374
612
  # @param mf_fname [String] File name of the Makefile to read.
375
613
  # @param target [String] Name of the target to gather dependencies for.
614
+ #
615
+ # @return [Array<String>] Paths of dependency files.
376
616
  def self.parse_makefile_deps(mf_fname, target)
377
617
  deps = []
378
618
  buildup = ''