rscons 1.4.3 → 1.5.0

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