squared 0.2.9 → 0.3.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.
@@ -25,7 +25,7 @@ module Squared
25
25
  def bannerargs(*); end
26
26
 
27
27
  def tasks
28
- %i[build depend graph doc test copy clean].freeze
28
+ %i[build depend graph doc lint test copy clean].freeze
29
29
  end
30
30
 
31
31
  def as_path(val)
@@ -46,7 +46,7 @@ module Squared
46
46
  end
47
47
 
48
48
  def to_s
49
- super.match(/[^:]+\z/)[0]
49
+ super[/[^:]+\z/, 0]
50
50
  end
51
51
  end
52
52
 
@@ -68,12 +68,19 @@ module Squared
68
68
  @group = group&.to_s.freeze
69
69
  @depend = kwargs[:depend]
70
70
  @doc = kwargs[:doc]
71
+ @lint = kwargs[:lint]
71
72
  @test = kwargs[:test]
72
73
  @copy = kwargs[:copy]
73
74
  @clean = kwargs[:clean]
74
75
  @version = kwargs[:version]
75
- @exception = kwargs.key?(:exception) ? env_bool(kwargs[:exception]) : workspace.exception
76
- @pipe = kwargs.key?(:pipe) ? env_pipe(kwargs[:pipe]) : workspace.pipe
76
+ @envname = @name.gsub(/[^\w]+/, '_').upcase.freeze
77
+ kwargs[:exception] = 'PIPE_FAIL'
78
+ @exception = if kwargs.key?(:exception)
79
+ env_bool(kwargs[:exception], workspace.exception, strict: true)
80
+ else
81
+ workspace.exception
82
+ end
83
+ @pipe = kwargs.key?(:pipe) ? env_pipe(kwargs[:pipe], workspace.pipe, strict: true) : workspace.pipe
77
84
  @verbose = kwargs.key?(:verbose) ? kwargs[:verbose] : workspace.verbose
78
85
  @theme = if !@verbose
79
86
  {}
@@ -95,7 +102,6 @@ module Squared
95
102
  last: last,
96
103
  error: error
97
104
  }
98
- @envname = @name.gsub(/[^\w]+/, '_').upcase.freeze
99
105
  @desc = (@name.include?(':') ? @name.split(':').join(ARG[:SPACE]) : @name).freeze
100
106
  @parent = nil
101
107
  @global = false
@@ -116,6 +122,7 @@ module Squared
116
122
  end
117
123
  @depend = @script[:depend] if @depend.nil?
118
124
  @doc = @script[:doc] if @doc.nil?
125
+ @lint = @script[:lint] if @lint.nil?
119
126
  @test = @script[:test] if @test.nil?
120
127
  @clean = @script[:clean] if @clean.nil?
121
128
  @exclude = @script[:exclude] if @exclude.empty? && @script.key?(:exclude)
@@ -128,7 +135,7 @@ module Squared
128
135
  if @output[0].nil?
129
136
  if (scr = data[:script])
130
137
  @global = true
131
- script_set(scr, prod: kwargs[:prod])
138
+ script_set(scr, args: data.fetch(:args, kwargs[:args]), prod: kwargs[:prod])
132
139
  elsif (run = data[:run])
133
140
  @global = true
134
141
  run_set run
@@ -136,11 +143,11 @@ module Squared
136
143
  unless data[:env]
137
144
  if (scr = kwargs[:script])
138
145
  @global = false
139
- script_set scr
146
+ script_set(scr, args: kwargs[:args])
140
147
  elsif @script && !data[:global]
141
148
  if (scr = @script[:script])
142
149
  @global = false
143
- script_set scr
150
+ script_set(scr, args: @script.fetch(:args, kwargs[:args]))
144
151
  elsif (run = @script[:run])
145
152
  @global = false
146
153
  run_set run
@@ -151,6 +158,7 @@ module Squared
151
158
  @global = true
152
159
  run_set data[:run]
153
160
  end
161
+ @global = true
154
162
  end
155
163
 
156
164
  def initialize_events(ref, **)
@@ -163,24 +171,19 @@ module Squared
163
171
  return if @log
164
172
 
165
173
  log = log.is_a?(Hash) ? log.dup : { file: log }
166
- if (file = env('LOG_FILE'))
167
- file = DateTime.now.strftime(file)
168
- elsif (val = env('LOG_AUTO'))
169
- file = "#{@name}-%s.log" % [case val
170
- when 'y', 'year'
171
- Date.today.year
172
- when 'm', 'month'
173
- Date.today.strftime('%Y-%m')
174
- when 'd', 'day', '1'
175
- Date.today
176
- else
177
- val.include?('%') ? DateTime.now.strftime(val) : DateTime.now.strftime
178
- end]
179
- elsif (val = log[:file])
180
- file = val.is_a?(String) ? DateTime.now.strftime(val) : "#{@name}-#{Date.today}.log"
181
- end
182
- if file
183
- file = (val = env('LOG_DIR')) ? @workspace.home.join(val, file) : @workspace.home.join(file)
174
+ unless (file = env('LOG_FILE'))
175
+ file = case env('LOG_AUTO')
176
+ when 'y', 'year'
177
+ "#{@name}-#{Date.today.year}.log"
178
+ when 'm', 'month'
179
+ "#{@name}-#{Date.today.strftime('%Y-%m')}.log"
180
+ when 'd', 'day', '1'
181
+ "#{@name}-#{Date.today}.log"
182
+ end
183
+ end
184
+ if file ||= log[:file]
185
+ file = Date.today.strftime(file)
186
+ file = (dir = env('LOG_DIR')) ? @workspace.home.join(dir, file) : @workspace.home.join(file)
184
187
  begin
185
188
  file = file.realdirpath
186
189
  rescue StandardError => e
@@ -199,23 +202,23 @@ module Squared
199
202
  end
200
203
 
201
204
  def initialize_env(dev: nil, prod: nil, **)
202
- pre = "BUILD_#{@envname}"
203
- @dev = env_match("#{pre}_DEV", dev)
204
- @prod = env_match("#{pre}_PROD", prod)
205
+ @dev = env_match('BUILD', dev, suffix: 'DEV', strict: true)
206
+ @prod = env_match('BUILD', prod, suffix: 'PROD', strict: true)
205
207
  cmd = @output[0]
206
- unless cmd == false || cmd.is_a?(Array) || (val = env('BUILD', suffix: 'OPTS')).nil?
207
- @output[cmd ? 1 : 3] = shell_split(val, escape: false, join: true)
208
- end
209
- unless @output[2] == false || (val = env('BUILD', suffix: 'ENV')).nil?
208
+ if @output[2] != false && (val = env('BUILD', suffix: 'ENV'))
210
209
  begin
211
210
  data = JSON.parse(val)
212
211
  raise_error('invalid JSON object', val, hint: "#{prefix}_ENV") unless data.is_a?(Hash)
213
212
  rescue StandardError => e
214
- log&.warn e
213
+ log.warn e
215
214
  else
216
215
  @output[2] = data
217
216
  end
218
217
  end
218
+ if cmd != false && !cmd.is_a?(Array)
219
+ @output[cmd ? 1 : 3] = shell_split(val, join: true) if (val = env('BUILD', suffix: 'OPTS'))
220
+ @output[4] = shell_split(val, join: true) if cmd.nil? && (val = env('SCRIPT', suffix: 'OPTS'))
221
+ end
219
222
  @version = val if (val = env('BUILD', suffix: 'VERSION'))
220
223
  return unless (val = env('BUILD', strict: true))
221
224
 
@@ -247,23 +250,16 @@ module Squared
247
250
  when 'graph'
248
251
  next unless graph?
249
252
 
250
- check = lambda do |args|
251
- next args if (args = args.to_a).empty?
252
-
253
- param_guard(action, flag, args: args.reject { |val| name == val.to_s })
254
- end
255
-
256
- format_desc action, flag, 'project*'
257
- if flag == :run
258
- task flag do |_, args|
259
- graph check.(args)
260
- end
261
- else
262
- task flag do |_, args|
263
- out, done = graph(check.(args), out: [])
253
+ format_desc action, flag, '(-)project*'
254
+ task flag do |_, args|
255
+ args = param_guard(action, flag, args: args.to_a.reject { |val| name == val.to_s })
256
+ if flag == :run
257
+ graph args
258
+ else
259
+ out, done = graph(args, out: [])
264
260
  out.map! do |val|
265
261
  done.each_with_index do |proj, i|
266
- next unless Regexp.new(" #{Regexp.escape(proj.name)}(?:@\\d|\\z)") =~ val
262
+ next unless val =~ / #{Regexp.escape(proj.name)}(?:@\d|\z)/
267
263
 
268
264
  val += " (#{i.succ})"
269
265
  break
@@ -302,12 +298,12 @@ module Squared
302
298
  if val.directory? && !val.empty?
303
299
  true
304
300
  else
305
- log&.warn "workspace \"#{val}\" (#{val.empty? ? 'empty' : 'not found'})"
301
+ log.warn "workspace \"#{val}\" (#{val.empty? ? 'empty' : 'not found'})"
306
302
  false
307
303
  end
308
304
  end
309
- if path.is_a?(String) && (data = %r{^(.+)[\\/]\*+$}.match(path))
310
- return self unless checkdir.(path = basepath(data[1]))
305
+ if path.is_a?(String) && (seg = path[%r{^(.+)[\\/]\*+$}, 1])
306
+ return self unless checkdir.(path = basepath(seg))
311
307
 
312
308
  path = path.children.select { |val| checkdir.(val) }
313
309
  end
@@ -345,63 +341,87 @@ module Squared
345
341
 
346
342
  out = obj.link(self, *args, **kwargs, &blk) if obj.respond_to?(:link)
347
343
  if !out
348
- warn log_message(:warn, 'link not compatible', subject: obj.to_s, hint: name)
344
+ warn log_message(Logger::WARN, 'link not compatible', subject: obj.to_s, hint: name)
349
345
  elsif out.respond_to?(:build)
350
346
  out.build
351
347
  end
352
348
  self
353
349
  end
354
350
 
355
- def build(*args, from: :build, sync: invoked_sync?('build'), **)
351
+ def build(*args, sync: invoked_sync?('build'), from: :run, **)
356
352
  banner = verbose
357
353
  if args.empty?
358
- return unless from == :build
354
+ return unless from == :run
359
355
 
360
- cmd, opts, var, flags = @output
356
+ args = @output
361
357
  banner = verbose == 1 if task_invoked?('build', 'build:sync')
358
+ end
359
+ if args.all? { |val| val.is_a?(Array) }
360
+ cmd = []
361
+ var = {}
362
+ args.each do |val|
363
+ a, b, c, d, e = val
364
+ case b
365
+ when Hash
366
+ b = append_hash(b).join(' ')
367
+ when Enumerable
368
+ b = b.to_a.join(' ')
369
+ end
370
+ d = append_hash(d).join(' ') if d.is_a?(Hash)
371
+ if a
372
+ cmd << [a, d, b].compact.join(' ')
373
+ else
374
+ next unless respond_to?(:compose)
375
+
376
+ cmd << a if (a = compose(as_get(b), d, script: true, args: e, from: from))
377
+ end
378
+ var.merge!(c) if c.is_a?(Hash)
379
+ end
380
+ cmd = cmd.join(' && ')
362
381
  else
363
- cmd = args.shift
364
- opts = args.map { |val| shell_quote(val, force: false) }.join(' ')
382
+ cmd, opts, var, flags, scr = args
365
383
  end
366
384
  if cmd
385
+ cmd = as_get(cmd)
386
+ opts = compose(opts, script: false) if opts && respond_to?(:compose)
387
+ flags = append_hash(flags).join(' ') if flags.is_a?(Hash)
367
388
  case opts
368
- when String
369
- cmd = "#{cmd} #{opts}"
370
- when Array
371
- cmd = as_a(cmd).concat(opts).join(' && ')
389
+ when Hash
390
+ opts = append_hash(opts)
391
+ cmd = as_a(cmd).push(flags).concat(opts).compact.join(' ')
392
+ when Enumerable
393
+ cmd = as_a(cmd).concat(opts.to_a)
394
+ cmd.map! { |val| "#{val} #{flags}" } if flags
395
+ cmd = cmd.join(' && ')
372
396
  else
373
- cmd = [cmd, compose(opts, script: false)].compact.join(' ') unless opts == false || !respond_to?(:compose)
397
+ cmd = [cmd, flags, opts].compact.join(' ') if opts || flags
374
398
  end
375
399
  else
376
400
  return unless respond_to?(:compose)
377
401
 
378
- cmd = compose(opts, flags, from: from, script: true)
402
+ cmd = compose(as_get(opts), flags, script: true, args: scr, from: from)
379
403
  end
380
404
  run(cmd, var, from: from, banner: banner, sync: sync)
381
405
  end
382
406
 
383
407
  def depend(*, sync: invoked_sync?('depend'), **)
384
- return unless @depend
385
-
386
- run(@depend, from: :depend, sync: sync)
387
- end
388
-
389
- def copy(*, sync: invoked_sync?('copy'), **)
390
- return unless @copy
391
-
392
- run_s(@copy, from: :copy, sync: sync)
408
+ run_b(@depend, sync: sync, from: :depend)
393
409
  end
394
410
 
395
411
  def doc(*, sync: invoked_sync?('doc'), **)
396
- return unless @doc
412
+ run_b(@doc, sync: sync, from: :doc)
413
+ end
397
414
 
398
- build(*as_a(@doc), from: :doc, sync: sync)
415
+ def lint(*, sync: invoked_sync?('lint'), **)
416
+ run_b(@lint, sync: sync, from: :lint)
399
417
  end
400
418
 
401
419
  def test(*, sync: invoked_sync?('test'), **)
402
- return unless @test
420
+ run_b(@test, sync: sync, from: :test)
421
+ end
403
422
 
404
- build(*as_a(@test), from: :test, sync: sync)
423
+ def copy(*, sync: invoked_sync?('copy'), **)
424
+ run_b(@copy, sync: sync, from: :copy)
405
425
  end
406
426
 
407
427
  def clean(*, sync: invoked_sync?('clean'), **)
@@ -410,23 +430,31 @@ module Squared
410
430
  on :first, :clean
411
431
  case @clean
412
432
  when String
413
- run(@clean, from: :clean, sync: sync)
433
+ run_s(@clean, from: :clean, sync: sync)
434
+ when Hash
435
+ begin
436
+ @clean.each { |cmd, opts| build(cmd.to_s, opts, sync: sync) }
437
+ rescue StandardError => e
438
+ log.error e
439
+ ret = on(:error, from, e)
440
+ raise if @exception && ret != true
441
+ end
414
442
  when Enumerable
415
- (@clean.is_a?(Hash) ? @clean.values : as_a(@clean)).each do |val|
443
+ as_a(@clean).each do |val|
416
444
  val = val.to_s
417
445
  path = basepath(val)
418
446
  if path.directory? && val =~ %r{[\\/]\z}
419
- log&.warn "rm -rf #{path}"
420
- FileUtils.rm_rf(path, verbose: verbose)
447
+ log.warn "rm -rf #{path}"
448
+ path.rmtree(verbose: verbose)
421
449
  else
422
- log&.warn "rm #{path}"
450
+ log.warn "rm #{path}"
423
451
  (val.include?('*') ? Dir[path] : [path]).each do |file|
424
452
  next unless File.file?(file)
425
453
 
426
454
  begin
427
455
  File.delete(file)
428
456
  rescue StandardError => e
429
- log&.error e
457
+ log.error e
430
458
  end
431
459
  end
432
460
  end
@@ -446,8 +474,9 @@ module Squared
446
474
  end
447
475
  end
448
476
  end
449
- pass.concat(split_escape(val)) if (val = env('GRAPH_PASS', strict: true))
450
- data = graph_collect(self, start)
477
+ pass += split_escape(val) if (val = env('GRAPH', suffix: 'PASS'))
478
+ start, neg = start.partition { |name| !name.start_with?('-') }
479
+ data = graph_collect(self, start, pass: neg.map { |name| name[1..-1] })
451
480
  unless out
452
481
  data[name] << self
453
482
  on :first, :graph
@@ -481,6 +510,12 @@ module Squared
481
510
  (@events[name.to_sym] ||= {})[key.to_sym] = [block_given? ? [blk] + args : args, kwargs]
482
511
  end
483
512
 
513
+ def as(cmd, script, to = nil)
514
+ script = { "#{script}": to } if to
515
+ data = (@as ||= {})[cmd.to_sym] ||= {}
516
+ script.each { |key, val| data[key.to_s] = val }
517
+ end
518
+
484
519
  def variable_set(key, *val, **kwargs)
485
520
  if variables.include?(key)
486
521
  case key
@@ -505,7 +540,7 @@ module Squared
505
540
  instance_variable_set :"@#{key}", val.first
506
541
  end
507
542
  else
508
- log&.warn "variable_set: @#{key} (private)"
543
+ log.warn "variable_set: @#{key} (private)"
509
544
  end
510
545
  end
511
546
 
@@ -549,6 +584,10 @@ module Squared
549
584
  !!@doc
550
585
  end
551
586
 
587
+ def lint?
588
+ !!@lint
589
+ end
590
+
552
591
  def test?
553
592
  !!@test
554
593
  end
@@ -628,12 +667,19 @@ module Squared
628
667
  end
629
668
 
630
669
  def run(cmd = @session, var = nil, exception: @exception, sync: true, banner: true, chdir: path, from: nil, **)
670
+ unless cmd
671
+ if warning?
672
+ from &&= from.to_s
673
+ warn log_message(Logger::WARN, from || 'unknown', subject: project, hint: 'no command given')
674
+ end
675
+ return
676
+ end
631
677
  cmd = session_done(cmd)
632
- log&.info cmd
633
- on :first, from if from
678
+ log.info cmd
679
+ on :first, from
634
680
  begin
635
681
  if cmd.match?(/\A[^:]+:[^:]/) && workspace.task_defined?(cmd)
636
- log&.warn "ENV was discarded: #{var}" if var
682
+ log.warn "ENV was discarded: #{var}" if var
637
683
  task_invoke(cmd, exception: exception, warning: warning?)
638
684
  else
639
685
  print_item format_banner(cmd, banner: banner) if sync
@@ -641,23 +687,33 @@ module Squared
641
687
  shell(*args, chdir: chdir, exception: exception)
642
688
  end
643
689
  rescue StandardError => e
644
- log&.error e
645
- ret = on(:error, from, e) if from
690
+ log.error e
691
+ ret = on(:error, from, e)
646
692
  raise unless ret == true
647
693
  else
648
- on :last, from if from
694
+ on :last, from
649
695
  end
650
696
  end
651
697
 
652
698
  def run_s(*cmd, env: nil, sync: true, banner: verbose != false, from: nil, **kwargs)
653
- on :first, from if from
699
+ on :first, from
654
700
  begin
655
701
  cmd.flatten.each { |val| run(val, env, sync: sync, banner: banner, **kwargs) }
656
702
  rescue StandardError => e
657
- ret = on(:error, from, e) if from
703
+ ret = on(:error, from, e)
658
704
  raise unless ret == true
659
705
  end
660
- on :last, from if from
706
+ on :last, from
707
+ end
708
+
709
+ def run_b(obj, from: nil, sync: true)
710
+ return unless obj
711
+
712
+ if obj.is_a?(Array) && obj.any? { |val| !val.is_a?(String) }
713
+ build(*obj, from: from, sync: sync)
714
+ else
715
+ run_s(obj.is_a?(Enumerable) ? obj.to_a : obj, from: from, sync: sync)
716
+ end
661
717
  end
662
718
 
663
719
  def graph_branch(target, data, tasks = nil, out = nil, sync: true, pass: [], done: [], depth: 0, last: false,
@@ -744,9 +800,11 @@ module Squared
744
800
  done
745
801
  end
746
802
 
747
- def graph_collect(target, start = [], data: {})
803
+ def graph_collect(target, start = [], data: {}, pass: [])
748
804
  deps = []
749
805
  (start.empty? ? target.instance_variable_get(:@graph) : start)&.each do |val|
806
+ next if pass.include?(val)
807
+
750
808
  if (obj = workspace.find(name: val))
751
809
  next unless obj.enabled?
752
810
 
@@ -754,8 +812,11 @@ module Squared
754
812
  else
755
813
  items = workspace.find(group: val, ref: val.to_sym)
756
814
  end
815
+
757
816
  items.each do |proj|
758
- graph_collect(proj, data: data) if proj.graph? && !data.key?(proj.name)
817
+ next if pass.include?(proj.name)
818
+
819
+ graph_collect(proj, data: data, pass: pass) if proj.graph? && !data.key?(proj.name)
759
820
  next if (objs = data.fetch(proj.name, [])).include?(target)
760
821
 
761
822
  deps << proj
@@ -782,15 +843,29 @@ module Squared
782
843
  end
783
844
 
784
845
  def session(*cmd, prefix: cmd.first, main: true, options: true)
785
- prefix = stripext(prefix.to_s).upcase
846
+ prefix = prefix.to_s.upcase
786
847
  if (val = PATH[prefix] || PATH[prefix.to_sym])
787
848
  cmd[0] = shell_quote(val, force: false)
788
849
  end
789
- split_escape(val).each { |opt| cmd << fill_option(opt) } if options && (val = env("#{prefix}_OPTIONS"))
790
850
  ret = JoinSet.new(cmd.flatten(1))
851
+ if options && (val = env("#{prefix}_OPTIONS"))
852
+ split_escape(val).each { |opt| ret.last(fill_option(opt), /^(--?[^ =]+)[ =].+$/) }
853
+ end
791
854
  main ? @session = ret : ret
792
855
  end
793
856
 
857
+ def session_delete(*list, target: @session)
858
+ ret = []
859
+ list.each do |val|
860
+ pat = /^#{Regexp.escape(shell_option(val))}(?: |=|$)/
861
+ if (key = target.find { |opt| opt =~ pat })
862
+ target.delete(key)
863
+ ret << key
864
+ end
865
+ end
866
+ ret
867
+ end
868
+
794
869
  def session_output(*cmd, **kwargs)
795
870
  session(*cmd, main: false, options: false, **kwargs)
796
871
  end
@@ -803,50 +878,96 @@ module Squared
803
878
  cmd.done
804
879
  end
805
880
 
806
- def option(*args, target: @session, prefix: target&.first, **kwargs)
881
+ def option(*args, prefix: @session&.first, **kwargs)
807
882
  if prefix
808
883
  args.each do |val|
809
- ret = env("#{stripext(prefix)}_#{val.gsub(/\W/, '_')}".upcase, **kwargs)
884
+ ret = env("#{prefix}_#{val.gsub(/\W/, '_')}".upcase, **kwargs)
810
885
  return ret if ret
811
886
  end
812
887
  end
813
888
  nil
814
889
  end
815
890
 
816
- def option_partition(opts, list, target: @session, no: [], first: false, pass: ['='])
817
- out = []
891
+ def option_sanitize(opts, list, target: @session, no: nil, first: false, pass: ['='])
892
+ ret = []
893
+ reg = []
818
894
  bare = []
819
- reg, list = list.partition do |val|
820
- next unless (n = val.index('='))
821
-
822
- bare << val[0..n - 1] if val.end_with?('?')
823
- true
895
+ e = []
896
+ b = []
897
+ p = []
898
+ q = []
899
+ i = []
900
+ list = list.map do |val|
901
+ x, y = val.split('|')
902
+ if y
903
+ if (n = val.index('='))
904
+ x += val[n..-1]
905
+ end
906
+ [x, y]
907
+ else
908
+ x
909
+ end
824
910
  end
825
- list += bare
826
- no = no.map { |val| (n = val.index('=')) ? val[0..n - 1] : val }
911
+ list.flatten.each do |val|
912
+ if (n = val.index('='))
913
+ flag = val[0, n]
914
+ case val[n + 1]
915
+ when 'e'
916
+ e << flag
917
+ when 'b'
918
+ b << flag
919
+ when 'q'
920
+ q << flag
921
+ when 'p'
922
+ p << flag
923
+ when 'i'
924
+ i << flag
925
+ else
926
+ reg << Regexp.escape(flag)
927
+ end
928
+ bare << flag if val.end_with?('?')
929
+ else
930
+ bare << val
931
+ end
932
+ end
933
+ no = (no || []).map { |val| (n = val.index('=')) ? val[0, n] : val }
934
+ bare += no
827
935
  found = false
828
936
  opts.each do |opt|
829
- next out << opt if found
937
+ next ret << opt if found
830
938
 
831
- if list.include?(opt)
939
+ if bare.include?(opt)
832
940
  target << (opt.size == 1 ? "-#{opt}" : "--#{opt}")
833
941
  elsif opt.start_with?('no-') && no.include?(name = opt[3..-1])
834
942
  target << "--no-#{name}"
835
943
  else
836
- out << opt
944
+ if opt =~ /^([^=]+)=(.+)$/
945
+ a = $1
946
+ if e.include?(a)
947
+ target << shell_option(a, $2)
948
+ elsif q.include?(a)
949
+ target << quote_option(a, $2)
950
+ elsif p.include?(a)
951
+ target << quote_option(a, basepath($2))
952
+ elsif b.include?(a) || (i.include?(a) && /^\d+$/.match?($2))
953
+ target << basic_option(a, $2)
954
+ else
955
+ ret << opt
956
+ end
957
+ opt = a
958
+ else
959
+ ret << opt
960
+ end
837
961
  found = true if first && pass.none? { |val| opt.include?(val) }
838
962
  end
839
963
  end
840
- pat = Regexp.new("^(#{reg.map { |val| val[0..val.index('=') - 1] }.join('|')})=(.+)$")
841
- [out, pat]
964
+ [ret, reg.empty? ? /\A\s+\z/ : /^(#{reg.join('|')})=(.+)$/]
842
965
  end
843
966
 
844
- def option_clear(params, target: @session, **kwargs)
845
- return if params.empty?
846
-
967
+ def option_clear(opts, target: @session, **kwargs)
847
968
  kwargs[:subject] ||= target&.first
848
- kwargs[:hint] ||= 'unrecognized'
849
- warn log_message(:warn, params.join(', '), **kwargs)
969
+ kwargs[:hint] ||= 'not used'
970
+ warn log_message(Logger::WARN, opts.join(', '), **kwargs) unless opts.empty?
850
971
  end
851
972
 
852
973
  def print_item(*val)
@@ -989,18 +1110,23 @@ module Squared
989
1110
  opts.each { |val| target << shell_option(flag, val) }
990
1111
  end
991
1112
 
992
- def append_value(*opts, target: @session, delim: false, escape: true, quote: false)
993
- return if (opts = opts.flatten).empty?
994
-
995
- target << '--' if delim
996
- opts.each { |val| target << (escape ? shell_escape(val, quote: quote) : shell_quote(val)) }
997
- end
998
-
999
- def append_hash(opts, target: @session)
1000
- out = target.to_s
1001
- opts.each do |key, val|
1002
- next if out =~ / #{Regexp.escape(shell_option(key))}(?: |=|$)/
1113
+ def append_hash(data, target: @session)
1114
+ target ||= []
1115
+ if (type = env('BUILD', suffix: 'TYPE') || ENV['BUILD_TYPE'])
1116
+ type = "__#{type}__"
1117
+ if (extra = data[type] || data[type.to_sym]).is_a?(Hash)
1118
+ data = data.merge(extra)
1119
+ else
1120
+ extra = nil
1121
+ end
1122
+ end
1123
+ data.each do |key, val|
1124
+ next if (key = key.to_s).start_with?('__')
1003
1125
 
1126
+ if val.nil? || extra || session_arg?(key, target: target)
1127
+ session_delete(key, target: target)
1128
+ next if val.nil?
1129
+ end
1004
1130
  case val
1005
1131
  when Array
1006
1132
  append_repeat(key, val, target: target)
@@ -1012,14 +1138,50 @@ module Squared
1012
1138
  target << shell_option(key, val.is_a?(String) ? val : nil)
1013
1139
  end
1014
1140
  end
1141
+ target
1015
1142
  end
1016
1143
 
1017
- def append_first(list, target: @session, flag: true, equals: false, quote: false, escape: true, **kwargs)
1018
- list.each do |opt|
1019
- next unless (val = option(opt, target: target, **kwargs))
1144
+ def append_any(val, target: @session, delim: false)
1145
+ if delim && !target.include?('--')
1146
+ target << '--'
1147
+ else
1148
+ delim = false
1149
+ end
1150
+ case val
1151
+ when String
1152
+ target << val
1153
+ when Hash
1154
+ append_hash(val, target: target)
1155
+ when Enumerable
1156
+ if target.is_a?(Array)
1157
+ target.concat(val.to_a)
1158
+ else
1159
+ target.merge(val.to_a)
1160
+ end
1161
+ else
1162
+ target.delete('--') if delim
1163
+ end
1164
+ end
1165
+
1166
+ def append_value(*list, target: @session, delim: false, escape: false, quote: true)
1167
+ return if (list = list.flatten).empty?
1168
+
1169
+ target << '--' if delim && !target.include?('--')
1170
+ list.map do |val|
1171
+ target << (val = escape ? shell_escape(val, quote: quote) : shell_quote(val))
1172
+ val
1173
+ end
1174
+ end
1175
+
1176
+ def append_first(*list, target: @session, flag: true, equals: false, escape: true, quote: true, force: true,
1177
+ **kwargs)
1178
+ return if (list = list.flatten).empty?
1179
+
1180
+ list.flatten.each do |opt|
1181
+ next unless (val = option(opt, **kwargs))
1020
1182
 
1021
1183
  return target << (if flag
1022
- shell_option(opt, equals ? val : nil, quote: quote, escape: escape)
1184
+ shell_option(opt, equals ? val : nil, quote: quote, escape: escape, force: force)
1023
1185
  else
1024
1186
  shell_quote(val)
1025
1187
  end)
@@ -1027,21 +1189,36 @@ module Squared
1027
1189
  nil
1028
1190
  end
1029
1191
 
1030
- def append_option(list, target: @session, equals: false, quote: false, escape: true, **kwargs)
1031
- list.each do |flag|
1032
- next unless (val = option(flag, target: target, **kwargs))
1192
+ def append_option(*list, target: @session, no: false, equals: false, escape: true, quote: true, force: true,
1193
+ **kwargs)
1194
+ return if (list = list.flatten).empty?
1033
1195
 
1034
- target << shell_option(flag, equals ? val : nil, quote: quote, escape: escape)
1196
+ ret = []
1197
+ list.flatten.each do |flag|
1198
+ next unless (val = option(flag, **kwargs))
1199
+
1200
+ if val == '0' && no
1201
+ flag = "no-#{flag}"
1202
+ val = nil
1203
+ end
1204
+ ret << shell_option(flag, equals ? val : nil, escape: escape, quote: quote, force: force)
1035
1205
  end
1206
+ ret.each { |val| target << val } unless ret.empty?
1036
1207
  end
1037
1208
 
1038
1209
  def append_nocolor(target: @session)
1039
- target << '--no-color' if !ARG[:COLOR] || stdin? || option('no-color', target: target, ignore: false)
1210
+ target << '--no-color' if !ARG[:COLOR] || stdin? || option('no-color', ignore: false)
1211
+ end
1212
+
1213
+ def collect_hash(data, pass: [])
1214
+ ret = []
1215
+ data.each { |key, val| ret += val unless pass.include?(key) }
1216
+ ret
1040
1217
  end
1041
1218
 
1042
1219
  def param_guard(action, flag, args: nil, key: nil, pat: nil)
1043
1220
  if args && key
1044
- val = args.fetch(key, nil)
1221
+ val = args[key]
1045
1222
  return val unless val.nil? || (pat && !val.match?(pat))
1046
1223
 
1047
1224
  @session = nil
@@ -1076,9 +1253,9 @@ module Squared
1076
1253
  end
1077
1254
 
1078
1255
  def indexitem(val)
1079
- return unless (data = /\A\^(\d+)(:.+)?\z/.match(val))
1256
+ return unless val =~ /\A\^(\d+)(:.+)?\z/
1080
1257
 
1081
- [data[1].to_i, data[2] ? data[2][1..-1] : nil]
1258
+ [$1.to_i, $2 && $2[1..-1]]
1082
1259
  end
1083
1260
 
1084
1261
  def indexerror(val, list = nil)
@@ -1094,8 +1271,8 @@ module Squared
1094
1271
  val.compact.map { |s| color(s) }.flatten
1095
1272
  end
1096
1273
 
1097
- def on(event, action, *args, **kwargs)
1098
- return unless (obj = @events[event][action])
1274
+ def on(event, from, *args, **kwargs)
1275
+ return unless from && (obj = @events[event][from])
1099
1276
 
1100
1277
  if obj.is_a?(Array) && obj[1].is_a?(Hash)
1101
1278
  opts = kwargs.empty? ? obj[1] : obj[1].merge(kwargs)
@@ -1114,7 +1291,7 @@ module Squared
1114
1291
  end
1115
1292
  end
1116
1293
 
1117
- def pwd_set(done = nil, pass: false, from: nil, &blk)
1294
+ def pwd_set(done = nil, pass: false, from: nil, dryrun: false, &blk)
1118
1295
  pwd = Pathname.pwd
1119
1296
  if block_given?
1120
1297
  begin
@@ -1126,9 +1303,11 @@ module Squared
1126
1303
  Dir.chdir(pwd)
1127
1304
  end
1128
1305
  rescue StandardError => e
1129
- log&.error e
1130
- ret = on(:error, from, e) if from
1131
- raise if exception && ret != true
1306
+ log.error e
1307
+ unless dryrun
1308
+ ret = on(:error, from, e)
1309
+ raise if exception && ret != true
1310
+ end
1132
1311
  else
1133
1312
  ret
1134
1313
  end
@@ -1148,6 +1327,8 @@ module Squared
1148
1327
  end
1149
1328
 
1150
1329
  def run_set(cmd, val = nil, opts: nil, **)
1330
+ return @output = cmd.dup if cmd.is_a?(Array)
1331
+
1151
1332
  unless @output[1] == false && !@output[0].nil?
1152
1333
  if opts == false
1153
1334
  @output[1] = false
@@ -1165,7 +1346,7 @@ module Squared
1165
1346
  @output[0] = cmd
1166
1347
  end
1167
1348
 
1168
- def script_set(cmd, prod: nil, **)
1349
+ def script_set(cmd, prod: nil, args: nil, **)
1169
1350
  return if @output[1] == false && @output[0].nil?
1170
1351
 
1171
1352
  @output[0] = nil
@@ -1174,31 +1355,36 @@ module Squared
1174
1355
  else
1175
1356
  cmd
1176
1357
  end
1358
+ @output[4] = args unless @output[4] == false || args.nil?
1359
+ end
1360
+
1361
+ def as_get(val)
1362
+ @global && (ret = @as && @as[from] && @as[from][val]) ? ret : val
1177
1363
  end
1178
1364
 
1179
1365
  def task_build(keys)
1180
1366
  namespace name do
1367
+ ws = workspace
1181
1368
  keys.each do |key|
1182
- next unless workspace.task_include?(self, key)
1369
+ next unless ws.task_include?(self, key)
1183
1370
 
1184
- action = workspace.series.name_get(key)
1185
- unless @pass.include?(key.to_s) || workspace.task_defined?(name, action)
1186
- workspace.task_desc(@desc, action)
1371
+ action = ws.series.name_get(key)
1372
+ unless @pass.include?(key.to_s) || ws.task_defined?(name, action) || ws.task_exclude?(action, self)
1373
+ ws.task_desc(@desc, action)
1187
1374
  task action do
1188
1375
  __send__(key)
1189
1376
  end
1190
1377
  end
1191
1378
  next if (items = @children.select { |item| item.task_include?(key) }).empty?
1192
1379
 
1193
- workspace.task_desc(@desc, action, 'workspace')
1380
+ ws.task_desc(@desc, action, 'workspace')
1194
1381
  task task_join(action, 'workspace') => items.map { |item| task_join(item.name, action) }
1195
1382
  end
1196
1383
  end
1197
1384
  end
1198
1385
 
1199
1386
  def projectpath?(val)
1200
- val = Pathname.new(val).cleanpath
1201
- val.absolute? ? val.to_s.start_with?(File.join(path, '')) : !val.to_s.start_with?(File.join('..', ''))
1387
+ Pathname.new(val).absolute? ? val.to_s.start_with?(File.join(path, '')) : !val.to_s.start_with?('..')
1202
1388
  end
1203
1389
 
1204
1390
  def semmajor?(cur, want)
@@ -1216,6 +1402,13 @@ module Squared
1216
1402
  end
1217
1403
  end
1218
1404
 
1405
+ def session_arg?(*list, target: @session, value: false)
1406
+ list.any? do |val|
1407
+ pat = /^#{Regexp.escape(shell_option(val))}#{value ? '[ =].' : '(?:[ =]|$)'}/
1408
+ target.any? { |opt| opt =~ pat }
1409
+ end
1410
+ end
1411
+
1219
1412
  def from_sync?(*val)
1220
1413
  if task_invoked?(key = task_join(*val))
1221
1414
  !workspace.task_defined?(key, 'sync')