squared 0.1.7 → 0.2.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.
@@ -14,7 +14,7 @@ module Squared
14
14
  include Utils
15
15
  include Rake::DSL
16
16
 
17
- VAR_SET = %i[parent global envname dependfile theme run script env].freeze
17
+ VAR_SET = %i[parent global envname dependfile theme run script env pass].freeze
18
18
  SEM_VER = /\b(\d+)(?:(\.)(\d+))?(?:(\.)(\d+)(\S+)?)?\b/.freeze
19
19
  private_constant :VAR_SET, :SEM_VER
20
20
 
@@ -50,18 +50,17 @@ module Squared
50
50
  end
51
51
  end
52
52
 
53
- @@tasks = {
54
- "#{ref}": {
55
- graph: %i[run print].freeze
56
- }.freeze
57
- }
53
+ (@@tasks = {})[ref] = {
54
+ 'graph' => %i[run print].freeze
55
+ }.freeze
56
+ @@task_desc = Rake::TaskManager.record_task_metadata
58
57
  @@print_order = 0
59
58
 
60
- attr_reader :name, :project, :workspace, :path, :exception, :pipe, :theme, :group, :parent,
61
- :dependfile, :verbose
59
+ attr_reader :name, :project, :workspace, :path, :theme, :exception, :pipe, :verbose,
60
+ :group, :parent, :dependfile
62
61
 
63
62
  def initialize(workspace, path, name, *, group: nil, graph: nil, pass: nil, exclude: nil,
64
- common: ARG[:COMMON], **kwargs)
63
+ first: {}, last: {}, error: {}, common: ARG[:COMMON], **kwargs)
65
64
  @path = path
66
65
  @workspace = workspace
67
66
  @name = name.to_s.freeze
@@ -72,6 +71,7 @@ module Squared
72
71
  @test = kwargs[:test]
73
72
  @copy = kwargs[:copy]
74
73
  @clean = kwargs[:clean]
74
+ @version = kwargs[:version]
75
75
  @exception = kwargs.key?(:exception) ? env_bool(kwargs[:exception]) : workspace.exception
76
76
  @pipe = kwargs.key?(:pipe) ? env_pipe(kwargs[:pipe]) : workspace.pipe
77
77
  @verbose = kwargs.key?(:verbose) ? kwargs[:verbose] : workspace.verbose
@@ -88,8 +88,13 @@ module Squared
88
88
  @graph = if graph
89
89
  as_a(graph, workspace.prefix ? ->(val) { workspace.task_name(val).to_sym } : :to_sym).freeze
90
90
  end
91
- @pass = (pass ? as_a(pass, :to_sym) : []).freeze
91
+ @pass = (pass ? as_a(pass) : []).freeze
92
92
  @exclude = (exclude ? as_a(exclude, :to_sym) : []).freeze
93
+ @events = {
94
+ first: first,
95
+ last: last,
96
+ error: error
97
+ }
93
98
  @envname = @name.gsub(/[^\w]+/, '_').upcase.freeze
94
99
  @desc = (@name.include?(':') ? @name.split(':').join(ARG[:SPACE]) : @name).freeze
95
100
  @parent = nil
@@ -115,6 +120,7 @@ module Squared
115
120
  @clean = @script[:clean] if @clean.nil?
116
121
  @exclude = @script[:exclude] if @exclude.empty? && @script.key?(:exclude)
117
122
  end
123
+ initialize_events(ref, **kwargs)
118
124
  initialize_logger(**kwargs)
119
125
  return if @output[0] == false
120
126
 
@@ -147,6 +153,12 @@ module Squared
147
153
  end
148
154
  end
149
155
 
156
+ def initialize_events(ref, **)
157
+ return unless (events = @workspace.events_get(group: @group, ref: ref))
158
+
159
+ events.each { |task, data| data.each { |ev, blk| (@events[ev] ||= {})[task] ||= blk } }
160
+ end
161
+
150
162
  def initialize_logger(log: nil, **)
151
163
  return if @log
152
164
 
@@ -187,17 +199,19 @@ module Squared
187
199
  @prod = env_match("#{pre}_PROD", prod)
188
200
  cmd = @output[0]
189
201
  unless cmd == false || cmd.is_a?(Array) || (val = env('BUILD', suffix: 'OPTS')).nil?
190
- @output[cmd ? 1 : 3] = shell_split(val, escape: false, join: true)
202
+ @output[cmd ? 1 : 3] = shell_split(val, join: true)
191
203
  end
192
204
  unless @output[2] == false || (val = env('BUILD', suffix: 'ENV')).nil?
193
205
  begin
194
206
  data = JSON.parse(val)
195
207
  raise_error('invalid JSON object', val, hint: "#{prefix}_ENV") unless data.is_a?(Hash)
196
- @output[2] = data
197
208
  rescue StandardError => e
198
- log&.warn e
209
+ log.warn e
210
+ else
211
+ @output[2] = data
199
212
  end
200
213
  end
214
+ @version = val if (val = env('BUILD', suffix: 'VERSION'))
201
215
  return unless (val = env('BUILD', strict: true))
202
216
 
203
217
  @global = false
@@ -214,49 +228,37 @@ module Squared
214
228
  Base.ref
215
229
  end
216
230
 
217
- def populate(*)
218
- namespace name do
219
- workspace.series.each_key do |key|
220
- next unless workspace.task_include?(self, key)
221
-
222
- s = workspace.series.name_get(key)
223
- unless workspace.task_defined?(name, s)
224
- desc message(@desc, s)
225
- task s do
226
- __send__(key)
227
- end
228
- end
229
- next if (items = @children.select { |item| workspace.task_include?(item, key) }).empty?
230
-
231
- desc message(@desc, s, 'workspace')
232
- task task_join(s, 'workspace') => items.map { |item| task_join(item.name, s) }
233
- end
234
- next unless ref?(Base.ref)
231
+ def populate(keys, **)
232
+ task_build keys
233
+ return unless ref?(Base.ref)
235
234
 
235
+ namespace name do
236
236
  @@tasks[Base.ref].each do |action, flags|
237
+ next if @pass.include?(action)
238
+
237
239
  namespace action do
238
240
  flags.each do |flag|
239
241
  case action
240
- when :graph
242
+ when 'graph'
241
243
  next unless graph?
242
244
 
243
245
  check = lambda do |args|
244
246
  next args if (args = args.to_a).empty?
245
247
 
246
- guard_params(action, flag, args: args.reject { |val| name == val.to_s })
248
+ param_guard(action, flag, args: args.reject { |val| name == val.to_s })
247
249
  end
248
250
 
249
- desc format_desc(action, flag, 'project*')
251
+ format_desc action, flag, 'project*'
250
252
  if flag == :run
251
- task flag, [:project] do |_, args|
253
+ task flag do |_, args|
252
254
  graph check.(args)
253
255
  end
254
256
  else
255
- task flag, [:project] do |_, args|
257
+ task flag do |_, args|
256
258
  out, done = graph(check.(args), out: [])
257
259
  out.map! do |val|
258
260
  done.each_with_index do |proj, i|
259
- next unless val.end_with?(" #{proj.name}")
261
+ next unless Regexp.new(" #{Regexp.escape(proj.name)}(?:@\\d|\\z)") =~ val
260
262
 
261
263
  val += " (#{i.succ})"
262
264
  break
@@ -277,6 +279,10 @@ module Squared
277
279
  end
278
280
  end
279
281
 
282
+ def generate(keys, **)
283
+ task_build keys
284
+ end
285
+
280
286
  def with(**kwargs, &blk)
281
287
  @withargs = kwargs.empty? ? nil : kwargs
282
288
  if block_given?
@@ -291,7 +297,7 @@ module Squared
291
297
  if val.directory? && !val.empty?
292
298
  true
293
299
  else
294
- log&.warn "workspace \"#{val}\" (#{val.empty? ? 'empty' : 'not found'})"
300
+ log.warn "workspace \"#{val}\" (#{val.empty? ? 'empty' : 'not found'})"
295
301
  false
296
302
  end
297
303
  end
@@ -329,7 +335,19 @@ module Squared
329
335
  self
330
336
  end
331
337
 
332
- def build(*args, sync: invoked_sync?('build'), **)
338
+ def inject(obj, *args, **kwargs, &blk)
339
+ return self unless enabled?
340
+
341
+ out = obj.link(self, *args, **kwargs, &blk) if obj.respond_to?(:link)
342
+ if !out
343
+ warn log_message(:warn, 'link not compatible', subject: obj.to_s, hint: name)
344
+ elsif out.respond_to?(:build)
345
+ out.build
346
+ end
347
+ self
348
+ end
349
+
350
+ def build(*args, from: :build, sync: invoked_sync?('build'), **)
333
351
  if args.empty?
334
352
  cmd, opts, var, flags = @output
335
353
  banner = verbose == 1
@@ -343,59 +361,71 @@ module Squared
343
361
  when String
344
362
  cmd = "#{cmd} #{opts}"
345
363
  when Array
346
- cmd = as_a(cmd).concat(opts).join(' && ')
364
+ cmd = cmd.join(' && ')
347
365
  else
348
366
  cmd = [cmd, compose(opts, script: false)].compact.join(' ') unless opts == false || !respond_to?(:compose)
349
367
  end
350
368
  else
351
369
  return unless respond_to?(:compose)
352
370
 
353
- cmd = compose(opts, flags, script: true)
371
+ cmd = compose(opts, flags, from: from, script: true)
354
372
  end
355
- run(cmd, var, banner: banner, sync: sync)
373
+ run(cmd, var, from: from, banner: banner, sync: sync)
356
374
  end
357
375
 
358
376
  def depend(*, sync: invoked_sync?('depend'), **)
359
- run(@depend, sync: sync) if @depend
377
+ return unless @depend
378
+
379
+ run(@depend, from: :depend, sync: sync)
360
380
  end
361
381
 
362
382
  def copy(*, sync: invoked_sync?('copy'), **)
363
- run_s(@copy, sync: sync) if @copy
383
+ return unless @copy
384
+
385
+ run_s(@copy, from: :copy, sync: sync)
364
386
  end
365
387
 
366
388
  def doc(*, sync: invoked_sync?('doc'), **)
367
- build(*as_a(@doc), sync: sync) if @doc
389
+ return unless @doc
390
+
391
+ build(@doc, from: :doc, sync: sync)
368
392
  end
369
393
 
370
394
  def test(*, sync: invoked_sync?('test'), **)
371
- build(*as_a(@test), sync: sync) if @test
395
+ return unless @test
396
+
397
+ build(@test, from: :test, sync: sync)
372
398
  end
373
399
 
374
- def clean(*)
400
+ def clean(*, sync: invoked_sync?('clean'), **)
401
+ return unless @clean
402
+
403
+ on :first, :clean
375
404
  case @clean
376
405
  when String
377
- run_s(@clean, sync: invoked_sync?('clean'))
406
+ run_s(@clean, from: :clean, sync: sync)
378
407
  when Enumerable
379
- (@clean.is_a?(Hash) ? @clean.values : as_a(@clean)).each do |val|
380
- val = val.to_s
381
- path = basepath(val)
382
- if path.directory? && val =~ %r{[\\/]$}
383
- log&.warn "rm -rf #{path}"
384
- path.rmtree
408
+ as_a(@clean).each do |val|
409
+ if val =~ %r{[\\/]$}
410
+ dir = Pathname.new(val.to_s)
411
+ dir = basepath(dir) unless dir.absolute?
412
+ next unless dir.directory?
413
+
414
+ log.warn "rm -rf #{dir}"
415
+ dir.rmtree
385
416
  else
386
- files = val.include?('*') ? Dir[path] : [path]
417
+ files = val.include?('*') ? Dir[basepath(val)] : [basepath(val)]
387
418
  files.each do |file|
388
- next unless File.file?(file)
389
-
390
419
  begin
391
- File.delete(file)
420
+ File.delete(file) if File.file?(file)
392
421
  rescue StandardError => e
393
- log&.error e
422
+ log.error e
394
423
  end
395
424
  end
396
425
  end
397
426
  end
398
427
  end
428
+ on :last, :clean
399
429
  end
400
430
 
401
431
  def graph(start = [], tasks = nil, sync: invoked_sync?('graph'), pass: [], out: nil)
@@ -411,9 +441,37 @@ module Squared
411
441
  end
412
442
  pass.concat(split_escape(val)) if (val = env('GRAPH_PASS', strict: true))
413
443
  data = graph_collect(self, start)
414
- data[name] << self unless out
415
- done = run_graph(data, name, tasks, out, sync: sync, pass: pass)
416
- [out, done] if out
444
+ unless out
445
+ data[name] << self
446
+ on :first, :graph
447
+ end
448
+ begin
449
+ done = graph_branch(self, data, tasks, out, sync: sync, pass: pass)
450
+ rescue StandardError => e
451
+ ret = on(:error, :graph, e)
452
+ raise unless ret == true
453
+ end
454
+ if out
455
+ [out, done]
456
+ else
457
+ on :last, :graph
458
+ end
459
+ end
460
+
461
+ def first(key, *args, **kwargs, &blk)
462
+ event(:first, key, *args, **kwargs, &blk)
463
+ end
464
+
465
+ def last(key, *args, **kwargs, &blk)
466
+ event(:last, key, *args, **kwargs, &blk)
467
+ end
468
+
469
+ def error(key, *args, **kwargs, &blk)
470
+ event(:error, key, *args, **kwargs, &blk)
471
+ end
472
+
473
+ def event(name, key, *args, **kwargs, &blk)
474
+ (@events[name.to_sym] ||= {})[key.to_sym] = [block_given? ? [blk] + args : args, kwargs]
417
475
  end
418
476
 
419
477
  def variable_set(key, *val, **kwargs)
@@ -440,11 +498,11 @@ module Squared
440
498
  instance_variable_set :"@#{key}", val.first
441
499
  end
442
500
  else
443
- log&.warn "variable_set: @#{key} (private)"
501
+ log.warn "variable_set: @#{key} (private)"
444
502
  end
445
503
  end
446
504
 
447
- def enabled?(ref = nil)
505
+ def enabled?(ref = nil, **)
448
506
  return false if ref && !ref?(ref)
449
507
 
450
508
  path.directory? && !path.empty?
@@ -500,7 +558,13 @@ module Squared
500
558
  @prod != false && workspace.prod?(pat: @prod, **scriptargs)
501
559
  end
502
560
 
503
- def version(*); end
561
+ def task_include?(key, ref = nil)
562
+ workspace.task_include?(self, key, ref) && !@pass.include?(key.to_s)
563
+ end
564
+
565
+ def version(*)
566
+ @version
567
+ end
504
568
 
505
569
  def dependtype(*)
506
570
  @dependindex ? @dependindex.succ : 0
@@ -528,6 +592,10 @@ module Squared
528
592
  ret
529
593
  end
530
594
 
595
+ def localname
596
+ workspace.task_localname(name)
597
+ end
598
+
531
599
  def inspect
532
600
  "#<#{self.class}: #{name} => #{self}>"
533
601
  end
@@ -546,12 +614,13 @@ module Squared
546
614
  puts_oe(*args, pipe: pipe)
547
615
  end
548
616
 
549
- def run(cmd = @session, var = nil, exception: @exception, sync: true, banner: true, chdir: path, **)
617
+ def run(cmd = @session, var = nil, exception: @exception, sync: true, banner: true, chdir: path, from: nil, **)
550
618
  cmd = session_done(cmd)
551
- log&.info cmd
619
+ log.info cmd
620
+ on :first, from if from
552
621
  begin
553
- if cmd =~ /\A[^:]+:[^:]/ && workspace.task_defined?(cmd)
554
- log&.warn "ENV was discarded: #{var}" if var
622
+ if cmd.match?(/\A[^:]+:[^:]/) && workspace.task_defined?(cmd)
623
+ log.warn "ENV was discarded: #{var}" if var
555
624
  task_invoke(cmd, exception: exception, warning: warning?)
556
625
  else
557
626
  print_item format_banner(cmd, banner: banner) if sync
@@ -559,17 +628,28 @@ module Squared
559
628
  shell(*args, chdir: chdir, exception: exception)
560
629
  end
561
630
  rescue StandardError => e
562
- log&.error e
563
- raise
631
+ log.error e
632
+ ret = on(:error, from, e) if from
633
+ raise unless ret == true
634
+ else
635
+ on :last, from if from
564
636
  end
565
637
  end
566
638
 
567
- def run_s(*cmd, env: nil, banner: verbose != false, **kwargs)
568
- cmd.each { |val| run(val, env, banner: banner, **kwargs) }
639
+ def run_s(*cmd, env: nil, sync: true, banner: verbose != false, from: nil, **kwargs)
640
+ on :first, from if from
641
+ begin
642
+ cmd.each { |val| run(val, env, sync: sync, banner: banner, **kwargs) }
643
+ rescue StandardError => e
644
+ ret = on(:error, from, e) if from
645
+ raise unless ret == true
646
+ end
647
+ on :last, from if from
569
648
  end
570
649
 
571
- def run_graph(data, start, tasks = nil, out = nil, sync: true, pass: [], done: [], depth: 0, last: false,
572
- single: false)
650
+ def graph_branch(target, data, tasks = nil, out = nil, sync: true, pass: [], done: [], depth: 0, last: false,
651
+ single: false)
652
+ tag = ->(proj) { "#{proj.name}#{SEM_VER =~ proj.version ? "@#{proj.version}" : ''}" }
573
653
  check = ->(deps) { deps.reject { |val| done.include?(val) } }
574
654
  dedupe = lambda do |name|
575
655
  next [] unless (ret = data[name])
@@ -581,6 +661,7 @@ module Squared
581
661
  end
582
662
  ret
583
663
  end
664
+ start = target.name
584
665
  if depth == 0
585
666
  items = check.(dedupe.(start))
586
667
  single = items.size == 1
@@ -589,24 +670,24 @@ module Squared
589
670
  end
590
671
  if out
591
672
  a, b, c, d, e = ARG[:GRAPH]
673
+ f = tag.(target)
592
674
  out << case depth
593
675
  when 0
594
- start
676
+ f
595
677
  when 1
596
678
  if items.empty?
597
- "#{d}#{b * 4} #{start}"
679
+ "#{d}#{b * 4} #{f}"
598
680
  else
599
- "#{last ? d : c}#{b * 3}#{e} #{start}"
681
+ "#{last ? d : c}#{b * 3}#{e} #{f}"
600
682
  end
601
683
  else
602
- "#{single ? ' ' : a}#{' ' * (depth - 1)}#{last ? d : c}#{b * 3}#{items.empty? ? b : e} #{start}"
684
+ "#{single ? ' ' : a}#{' ' * (depth - 1)}#{last ? d : c}#{b * 3}#{items.empty? ? b : e} #{f}"
603
685
  end
604
686
  end
605
687
  items.each_with_index do |proj, i|
606
688
  next if done.include?(proj)
607
689
 
608
- s = proj.name
609
- t = dedupe.(s)
690
+ t = dedupe.(proj.name)
610
691
  j = if out
611
692
  if i == items.size - 1 || check.(post = items[i + 1..-1]).empty?
612
693
  true
@@ -614,35 +695,35 @@ module Squared
614
695
  post.reject { |pr| t.include?(pr) }.empty?
615
696
  end
616
697
  end
617
- unless start == s || (none = check.(t).empty?)
618
- run_graph(data, s, tasks, out, sync: sync, pass: pass, done: done, depth: depth.succ, single: single,
619
- last: j == true)
698
+ unless start == proj.name || (none = check.(t).empty?)
699
+ graph_branch(proj, data, tasks, out, sync: sync, pass: pass, done: done, depth: depth.succ,
700
+ single: single, last: j == true)
620
701
  end
621
702
  if !out
622
- unless tasks || !(script = workspace.script_get(:graph, group: proj.group, ref: proj.allref))
703
+ if !tasks && (script = workspace.script_get(:graph, group: proj.group, ref: proj.allref))
623
704
  group = script[:graph]
624
705
  end
625
706
  (tasks || group || (dev? ? ['build', 'copy'] : ['depend', 'build'])).each do |meth|
626
707
  next if pass.include?(meth)
627
708
 
628
- if workspace.task_defined?(cmd = task_join(s, meth))
709
+ if workspace.task_defined?(cmd = task_join(proj.name, meth))
629
710
  run(cmd, sync: false, banner: false)
630
- elsif proj.has?(meth)
711
+ elsif proj.has?(meth, tasks || group ? nil : workspace.baseref)
631
712
  proj.__send__(meth.to_sym, sync: sync)
632
713
  end
633
714
  end
634
715
  elsif none
635
716
  a, b, c, d = ARG[:GRAPH]
636
717
  out << if depth == 0
637
- "#{i == items.size - 1 ? d : c}#{b * 4} #{s}"
718
+ "#{i == items.size - 1 ? d : c}#{b * 4} #{tag.(proj)}"
638
719
  else
639
- f = ''.dup
720
+ s = ''.dup
640
721
  k = 0
641
722
  while k < depth
642
- f += "#{(last && !j) || (j && k > 0 && k == depth - 1) || single ? ' ' : a} "
723
+ s += "#{(last && !j) || (j && k > 0 && k == depth - 1) || single ? ' ' : a} "
643
724
  k += 1
644
725
  end
645
- f + "#{j ? d : c}#{b * 3} #{s}"
726
+ s + "#{j ? d : c}#{b * 3} #{tag.(proj)}"
646
727
  end
647
728
  end
648
729
  done << proj
@@ -653,11 +734,20 @@ module Squared
653
734
  def graph_collect(target, start = [], data: {})
654
735
  deps = []
655
736
  (start.empty? ? target.instance_variable_get(:@graph) : start)&.each do |val|
656
- next unless (proj = workspace.find(name: val)) && proj.enabled?
737
+ if (obj = workspace.find(name: val))
738
+ next unless obj.enabled?
657
739
 
658
- deps << proj
659
- graph_collect(proj, data: data) if proj.graph? && !data.key?(proj.name)
660
- deps += data.fetch(val, [])
740
+ items = [obj]
741
+ else
742
+ items = workspace.find(group: val, ref: val.to_sym)
743
+ end
744
+ items.each do |proj|
745
+ graph_collect(proj, data: data) if proj.graph? && !data.key?(proj.name)
746
+ next if (objs = data.fetch(proj.name, [])).include?(target)
747
+
748
+ deps << proj
749
+ deps += objs
750
+ end
661
751
  end
662
752
  data[target.name] = deps.uniq.reject { |proj| proj == target }
663
753
  data
@@ -678,16 +768,22 @@ module Squared
678
768
  ret.empty? || (ignore && as_a(ignore).any? { |val| ret == val.to_s }) ? default : ret
679
769
  end
680
770
 
681
- def session(*cmd, prefix: cmd.first)
682
- prefix = stripext(prefix.to_s).upcase
683
- if (val = env("#{prefix}_OPTIONS"))
684
- split_escape(val).each { |opt| cmd << fill_option(opt) }
771
+ def session(*cmd, prefix: cmd.first, main: true, options: true)
772
+ prefix = prefix.to_s.upcase
773
+ if (val = PATH[prefix] || PATH[prefix.to_sym])
774
+ cmd[0] = shell_quote(val, force: false)
685
775
  end
686
- @session = JoinSet.new(cmd)
776
+ split_escape(val).each { |opt| cmd << fill_option(opt) } if options && (val = env("#{prefix}_OPTIONS"))
777
+ ret = JoinSet.new(cmd.flatten(1))
778
+ main ? @session = ret : ret
779
+ end
780
+
781
+ def session_output(*cmd, **kwargs)
782
+ session(*cmd, main: false, options: false, **kwargs)
687
783
  end
688
784
 
689
785
  def session_done(cmd)
690
- return cmd.to_s unless cmd.respond_to?(:done)
786
+ return cmd unless cmd.respond_to?(:done)
691
787
 
692
788
  raise_error('no args were added', hint: cmd.first || name) unless cmd.size > 1
693
789
  @session = nil if cmd == @session
@@ -697,13 +793,47 @@ module Squared
697
793
  def option(*args, prefix: @session&.first, **kwargs)
698
794
  if prefix
699
795
  args.each do |val|
700
- ret = env("#{stripext(prefix)}_#{val.gsub(/\W/, '_')}".upcase, **kwargs)
796
+ ret = env("#{prefix}_#{val.gsub(/\W/, '_')}".upcase, **kwargs)
701
797
  return ret if ret
702
798
  end
703
799
  end
704
800
  nil
705
801
  end
706
802
 
803
+ def option_partition(opts, list, target: @session, no: [], first: false, pass: ['='])
804
+ out = []
805
+ bare = []
806
+ reg, list = list.partition do |val|
807
+ next unless (n = val.index('='))
808
+
809
+ bare << val[0..n - 1] if val.end_with?('?')
810
+ true
811
+ end
812
+ list += bare
813
+ no = no.map { |val| (n = val.index('=')) ? val[0..n - 1] : val }
814
+ found = false
815
+ opts.each do |opt|
816
+ next out << opt if found
817
+
818
+ if list.include?(opt)
819
+ target << (opt.size == 1 ? "-#{opt}" : "--#{opt}")
820
+ elsif opt.start_with?('no-') && no.include?(name = opt[3..-1])
821
+ target << "--no-#{name}"
822
+ else
823
+ out << opt
824
+ found = true if first && pass.none? { |val| opt.include?(val) }
825
+ end
826
+ end
827
+ pat = Regexp.new("^(#{reg.map { |val| val[0..val.index('=') - 1] }.join('|')})=(.+)$")
828
+ [out, pat]
829
+ end
830
+
831
+ def option_clear(params, target: @session, **kwargs)
832
+ kwargs[:subject] ||= target&.first
833
+ kwargs[:hint] ||= 'not used'
834
+ warn log_message(:warn, params.join(', '), **kwargs) unless params.empty?
835
+ end
836
+
707
837
  def print_item(*val)
708
838
  puts if @@print_order > 0 && verbose && !stdin?
709
839
  @@print_order += 1
@@ -716,7 +846,7 @@ module Squared
716
846
  if styles.any? { |s| s.to_s.end_with?('!') }
717
847
  pad = 1
718
848
  elsif !client && styles.size <= 1
719
- styles = %i[bold] + styles
849
+ styles = [:bold] + styles
720
850
  end
721
851
  end
722
852
  n = Project.max_width(lines)
@@ -749,32 +879,33 @@ module Squared
749
879
  ret.join("\n")
750
880
  end
751
881
 
752
- def format_desc(action, flag, opts = nil, req: nil, arg: 'opts*')
753
- return unless Rake::TaskManager.record_task_metadata
882
+ def format_desc(action, flag, opts = nil, **kwargs)
883
+ return unless @@task_desc
754
884
 
755
- opts = "#{arg}=#{opts.join(',')}" if opts.is_a?(Array)
756
- out = [@desc]
757
- if flag
758
- out << action
759
- else
760
- flag = action
761
- end
762
- req &&= opts ? "#{req}," : "[#{req}]"
763
- out << (opts ? "#{flag}[#{req}#{opts}]" : "#{flag}#{req}")
764
- message(*out)
885
+ ret = [@desc, action]
886
+ ret << flag if flag
887
+ workspace.format_desc(ret, opts, **kwargs)
765
888
  end
766
889
 
767
890
  def format_banner(cmd, banner: true)
768
891
  return unless banner && ARG[:BANNER]
769
892
 
770
893
  if (data = workspace.banner_get(*@ref, group: group))
894
+ return if data.empty?
895
+
771
896
  client = true
772
897
  else
773
- data = { command: true, order: %i[path], styles: theme[:banner], border: theme[:border] }
898
+ data = { command: true, order: [:path], styles: theme[:banner], border: theme[:border] }
774
899
  end
775
900
  if verbose
776
901
  out = []
777
- out << cmd.sub(/\A\S+/, &:upcase) if data[:command]
902
+ if data[:command]
903
+ if /\A(?:"((?:[^"]|(?<=\\)")+)"|'((?:[^']|(?<=\\)')+)'|(\S+)) /.match(cmd)
904
+ path = $3 || $2 || $1
905
+ cmd = cmd.sub(path, File.basename(path).upcase)
906
+ end
907
+ out << cmd
908
+ end
778
909
  data[:order].each do |val|
779
910
  if val.is_a?(Array)
780
911
  s = ' '
@@ -804,14 +935,15 @@ module Squared
804
935
  def format_list(items, cmd, type, grep: [], from: nil, each: nil)
805
936
  reg = grep.map { |val| Regexp.new(val) }
806
937
  out = []
807
- if (pad = items.size) > 0
808
- pad = pad.to_s.size
938
+ unless items.empty?
939
+ pad = items.size.to_s.size
809
940
  items.each_with_index do |val, i|
810
- next unless reg.empty? || reg.any? { |pat| pat.match?(val[0]) }
941
+ next unless reg.empty? || reg.any? { |pat| val[0] =~ pat }
811
942
 
812
943
  out << "#{(i + 1).to_s.rjust(pad)}. #{each ? each.(val) : val[0]}"
813
944
  end
814
945
  end
946
+ sub = [headerstyle]
815
947
  if out.empty?
816
948
  out = ["No #{type} were found:", '']
817
949
  unless grep.empty?
@@ -821,56 +953,83 @@ module Squared
821
953
  end
822
954
  if from
823
955
  out << from
824
- pat = /\A(#{Regexp.escape(out.last)})(.*)\z/m
956
+ pat = /\A(#{Regexp.escape(from)})(.*)\z/
825
957
  end
826
958
  else
827
- pat = /\A(\s*\d+\.)(.+)\z/m
959
+ pat = /\A(\s*\d+\.)(.+)\z/
960
+ unless grep.empty?
961
+ footer = "#{out.size} found"
962
+ sub << { pat: /\A(\d+)( .+)\z/, styles: theme[:inline] }
963
+ end
828
964
  end
829
- sub = [headerstyle]
830
965
  sub << { pat: pat, styles: theme[:active] } if pat
831
- emphasize(out, title: task_join(name, cmd), border: borderstyle, sub: sub)
966
+ emphasize(out, title: task_join(name, cmd), border: borderstyle, sub: sub, footer: footer, right: true)
832
967
  end
833
968
 
834
969
  def empty_status(msg, title, obj, always: false)
835
970
  "#{msg}#{!always && (!obj || obj == 0 || obj.to_s.empty?) ? '' : message(hint: message(title, obj.to_s))}"
836
971
  end
837
972
 
838
- def append_repeat(flag, opts)
839
- opts.each { |val| @session << shell_option(flag, val, quote: true) }
973
+ def append_repeat(flag, opts, target: @session)
974
+ opts.each { |val| target << shell_option(flag, val, quote: true) }
840
975
  end
841
976
 
842
- def append_value(opts, delim: false, escape: true)
843
- @session << '--' if delim && !opts.empty?
844
- opts.each { |val| @session << (escape ? shell_escape(val) : shell_quote(val)) }
977
+ def append_value(opts, target: @session, delim: false, escape: true)
978
+ return if opts.empty?
979
+
980
+ target << '--' if delim
981
+ opts.each { |val| target << (escape ? shell_escape(val) : shell_quote(val)) }
845
982
  end
846
983
 
847
- def append_first(list, flag: true, equals: false, quote: false, **kwargs)
984
+ def append_hash(opts, target: @session)
985
+ out = target.to_s
986
+ opts.each do |key, val|
987
+ key = key.to_s
988
+ key = "--#{key}" unless key[0] == '-'
989
+ next if out =~ Regexp.new(" #{key}(?: |=|$)")
990
+
991
+ case val
992
+ when String
993
+ append_repeat(key, [val], target: target)
994
+ when Array
995
+ append_repeat(key, val, target: target)
996
+ when Numeric
997
+ target << (key + (key.start_with?('--') ? '=' : '') + val)
998
+ when false
999
+ target << key.sub(/^--(?!no-)/, '--no-')
1000
+ else
1001
+ target << key
1002
+ end
1003
+ end
1004
+ end
1005
+
1006
+ def append_first(list, target: @session, flag: true, equals: false, quote: false, escape: true, **kwargs)
848
1007
  list.each do |opt|
849
1008
  next unless (val = option(opt, **kwargs))
850
1009
 
851
- return @session << (if flag
852
- shell_option(opt, equals ? val : nil, quote: quote)
853
- else
854
- shell_quote(val)
855
- end)
1010
+ return target << (if flag
1011
+ shell_option(opt, equals ? val : nil, quote: quote, escape: escape)
1012
+ else
1013
+ shell_quote(val)
1014
+ end)
856
1015
  end
857
1016
  end
858
1017
 
859
- def append_option(list, equals: false, quote: false, **kwargs)
1018
+ def append_option(list, target: @session, equals: false, quote: false, escape: true, **kwargs)
860
1019
  list.each do |flag|
861
1020
  next unless (val = option(flag, **kwargs))
862
1021
 
863
- @session << shell_option(flag, equals ? val : nil, quote: quote)
1022
+ target << shell_option(flag, equals ? val : nil, quote: quote, escape: escape)
864
1023
  end
865
1024
  end
866
1025
 
867
- def append_nocolor(flag = nil)
868
- @session << '--no-color' if flag || !ARG[:COLOR] || stdin?
1026
+ def append_nocolor(target: @session)
1027
+ target << '--no-color' if !ARG[:COLOR] || stdin? || option('no-color', ignore: false)
869
1028
  end
870
1029
 
871
- def guard_params(action, flag, args: nil, key: nil, pat: nil)
1030
+ def param_guard(action, flag, args: nil, key: nil, pat: nil)
872
1031
  if args && key
873
- val = args.fetch(key, nil)
1032
+ val = args[key]
874
1033
  return val unless val.nil? || (pat && !val.match?(pat))
875
1034
 
876
1035
  @session = nil
@@ -919,17 +1078,48 @@ module Squared
919
1078
  ret && !ret.empty? ? ret : [val]
920
1079
  end
921
1080
 
922
- def pwd_set(done = nil, pass: false, &blk)
1081
+ def colormap(val)
1082
+ val.compact.map { |s| color(s) }.flatten
1083
+ end
1084
+
1085
+ def on(event, action, *args, **kwargs)
1086
+ return unless (obj = @events[event][action])
1087
+
1088
+ if obj.is_a?(Array) && obj[1].is_a?(Hash)
1089
+ opts = kwargs.empty? ? obj[1] : obj[1].merge(kwargs)
1090
+ target = obj[0]
1091
+ else
1092
+ opts = kwargs
1093
+ target = obj
1094
+ end
1095
+ as_a(target, flat: true).each do |cmd|
1096
+ case cmd
1097
+ when Proc, Method
1098
+ cmd.call(*args, **opts)
1099
+ when String
1100
+ run_s(cmd, **opts)
1101
+ end
1102
+ end
1103
+ end
1104
+
1105
+ def pwd_set(done = nil, pass: false, from: nil, &blk)
923
1106
  pwd = Pathname.pwd
924
1107
  if block_given?
925
- if path == pwd || pass == true || (pass.is_a?(String) && semscan(pass).join <= RUBY_VERSION)
926
- ret = instance_eval(&blk)
1108
+ begin
1109
+ if path == pwd || pass == true || (pass.is_a?(String) && semscan(pass).join >= RUBY_VERSION)
1110
+ ret = instance_eval(&blk)
1111
+ else
1112
+ Dir.chdir(path)
1113
+ ret = instance_eval(&blk)
1114
+ Dir.chdir(pwd)
1115
+ end
1116
+ rescue StandardError => e
1117
+ log.error e
1118
+ ret = on(:error, from, e) if from
1119
+ raise if exception && ret != true
927
1120
  else
928
- Dir.chdir(path)
929
- ret = instance_eval(&blk)
930
- Dir.chdir(pwd)
1121
+ ret
931
1122
  end
932
- ret
933
1123
  elsif @pwd == pwd
934
1124
  @pwd = nil
935
1125
  pwd unless done
@@ -974,9 +1164,28 @@ module Squared
974
1164
  end
975
1165
  end
976
1166
 
1167
+ def task_build(keys)
1168
+ namespace name do
1169
+ keys.each do |key|
1170
+ next unless workspace.task_include?(self, key)
1171
+
1172
+ action = workspace.series.name_get(key)
1173
+ unless @pass.include?(key.to_s) || workspace.task_defined?(name, action)
1174
+ workspace.task_desc(@desc, action)
1175
+ task action do
1176
+ __send__(key)
1177
+ end
1178
+ end
1179
+ next if (items = @children.select { |item| item.task_include?(key) }).empty?
1180
+
1181
+ workspace.task_desc(@desc, action, 'workspace')
1182
+ task task_join(action, 'workspace') => items.map { |item| task_join(item.name, action) }
1183
+ end
1184
+ end
1185
+ end
1186
+
977
1187
  def projectpath?(val)
978
- val = Pathname.new(val).cleanpath
979
- val.absolute? ? val.to_s.start_with?(File.join(path, '')) : !val.to_s.start_with?(File.join('..', ''))
1188
+ Pathname.new(val).absolute? ? val.to_s.start_with?(File.join(path, '')) : !val.to_s.start_with?('..')
980
1189
  end
981
1190
 
982
1191
  def semmajor?(cur, want)
@@ -1003,7 +1212,7 @@ module Squared
1003
1212
  end
1004
1213
 
1005
1214
  def invoked_sync?(action, val = nil)
1006
- return true if !val.nil? || from_sync?(ac = workspace.task_name(action))
1215
+ return true if val || from_sync?(ac = workspace.task_name(action))
1007
1216
  return val if group && !(val = from_sync?(ac, group)).nil?
1008
1217
  return val if (base = workspace.find_base(self)) && !(val = from_sync?(ac, base.ref)).nil?
1009
1218
 
@@ -1011,7 +1220,7 @@ module Squared
1011
1220
  true
1012
1221
  else
1013
1222
  val = workspace.series.name_get(action)
1014
- val == action ? false : invoked_sync?(val)
1223
+ val != action && invoked_sync?(val)
1015
1224
  end
1016
1225
  end
1017
1226