squared 0.0.11 → 0.0.12

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.
@@ -8,23 +8,22 @@ module Squared
8
8
  module Workspace
9
9
  module Project
10
10
  class Base
11
- include Common
12
- include Format
11
+ include Common::Format
13
12
  include System
14
13
  include Shell
15
- include Task
16
14
  include Utils
17
15
  include ::Rake::DSL
18
16
 
19
17
  SEM_VER = /(\d+)(?:(\.)(\d+))?(?:(\.)(\d+)(\S+)?)?/.freeze
20
18
 
21
- VAR_SET = %i[parent global envname theme run script env depend doc test copy clean].freeze
19
+ VAR_SET = %i[parent global envname dependfile theme run script env depend graph doc test copy clean].freeze
22
20
  private_constant :VAR_SET
23
21
 
24
22
  class << self
25
23
  def populate(*); end
26
24
  def batchargs(*); end
27
25
  def aliasargs(*); end
26
+ def bannerargs(*); end
28
27
 
29
28
  def tasks
30
29
  [].freeze
@@ -44,7 +43,7 @@ module Squared
44
43
  end
45
44
 
46
45
  def to_s
47
- super.to_s.match(/[^:]+$/)[0]
46
+ super.match(/[^:]+$/)[0]
48
47
  end
49
48
 
50
49
  def config?(*)
@@ -52,12 +51,18 @@ module Squared
52
51
  end
53
52
  end
54
53
 
54
+ @@tasks = {
55
+ "#{ref}": {
56
+ graph: %i[run print].freeze
57
+ }.freeze
58
+ }
55
59
  @@print_order = 0
56
- @@tasks = {}
57
60
 
58
- attr_reader :name, :project, :workspace, :path, :exception, :pipe, :verbose, :theme, :group, :parent
61
+ attr_reader :name, :project, :workspace, :path, :exception, :pipe, :theme, :group, :parent,
62
+ :dependfile, :verbose
59
63
 
60
- def initialize(workspace, path, name, *, group: nil, pass: nil, exclude: nil, common: ARG[:COMMON], **kwargs)
64
+ def initialize(workspace, path, name, *, group: nil, graph: nil, pass: nil, exclude: nil,
65
+ common: ARG[:COMMON], **kwargs)
61
66
  @path = path
62
67
  @workspace = workspace
63
68
  @name = name.to_s.freeze
@@ -81,6 +86,9 @@ module Squared
81
86
  @output = []
82
87
  @ref = []
83
88
  @children = []
89
+ @graph = if graph
90
+ as_a(graph, workspace.prefix ? ->(val) { workspace.task_name(val).to_sym } : :to_sym).freeze
91
+ end
84
92
  @pass = (pass ? as_a(pass, :to_sym) : []).freeze
85
93
  @exclude = (exclude ? as_a(exclude, :to_sym) : []).freeze
86
94
  @envname = @name.gsub(/[^\w]+/, '_').upcase.freeze
@@ -212,17 +220,48 @@ module Squared
212
220
  workspace.series.each_key do |key|
213
221
  next unless workspace.task_include?(self, key)
214
222
 
215
- alt = workspace.series.name_get(key)
216
- unless workspace.task_defined?(name, alt)
217
- desc message(@desc, alt)
218
- task alt do
223
+ s = workspace.series.name_get(key)
224
+ unless workspace.task_defined?(name, s)
225
+ desc message(@desc, s)
226
+ task s do
219
227
  __send__(key)
220
228
  end
221
229
  end
222
230
  next if (items = @children.select { |item| workspace.task_include?(item, key) }).empty?
223
231
 
224
- desc message(@desc, alt, 'workspace')
225
- task task_join(alt, 'workspace') => items.map { |item| task_join(item.name, alt) }
232
+ desc message(@desc, s, 'workspace')
233
+ task task_join(s, 'workspace') => items.map { |item| task_join(item.name, s) }
234
+ end
235
+ next unless ref?(Base.ref)
236
+
237
+ @@tasks[Base.ref].each do |action, flags|
238
+ namespace action do
239
+ flags.each do |flag|
240
+ case action
241
+ when :graph
242
+ next unless graph?
243
+
244
+ if flag == :run
245
+ desc format_desc(action, flag, 'pass*')
246
+ task flag, [:pass] do |_, args|
247
+ graph(pass: args.to_a)
248
+ end
249
+ else
250
+ desc format_desc(action, flag)
251
+ task flag do
252
+ out = graph(out: [])
253
+ emphasize(out, title: path, right: true, border: borderstyle,
254
+ footer: "dependencies: #{out.size - 1}",
255
+ sub: [
256
+ { pat: /^(#{Regexp.escape(path.to_s)})(.*)$/, styles: theme[:header] },
257
+ { pat: /^(#{Regexp.escape(name)})(.*)$/, styles: theme[:active] },
258
+ { pat: /^(\s*)([a-z]+: )(\d+)$/, styles: theme[:inline], index: 3 }
259
+ ])
260
+ end
261
+ end
262
+ end
263
+ end
264
+ end
226
265
  end
227
266
  end
228
267
  end
@@ -238,15 +277,17 @@ module Squared
238
277
 
239
278
  def add(path, name = nil, **kwargs, &blk)
240
279
  if path.is_a?(::String) && (data = %r{^(.+)[\\/]\*+$}.match(path))
241
- path = base_path(data[1]).children.select(&:directory?)
280
+ path = basepath(data[1]).children.select(&:directory?)
242
281
  end
243
282
  if path.is_a?(::Array)
244
283
  name = @name if name == true
245
284
  path.each { |val| add(val, name && task_join(name, File.basename(val)), **kwargs, &blk) }
246
285
  return self
247
- elsif !source_path?(path = base_path(path))
286
+ elsif !projectpath?(path = basepath(path))
248
287
  return self
249
- elsif !name.is_a?(::String) && !name.is_a?(::Symbol)
288
+ elsif name.is_a?(::Symbol)
289
+ name = name.to_s
290
+ elsif !name.is_a?(::String)
250
291
  name = nil
251
292
  end
252
293
  if @withargs
@@ -317,13 +358,13 @@ module Squared
317
358
  as_a(@clean).each do |val|
318
359
  if (val = val.to_s) =~ %r{[\\/]$}
319
360
  dir = Pathname.new(val)
320
- dir = base_path(dir) unless dir.absolute?
361
+ dir = basepath(dir) unless dir.absolute?
321
362
  next unless dir.directory?
322
363
 
323
364
  log.warn "rm -rf #{dir}"
324
365
  dir.rmtree(verbose: true)
325
366
  else
326
- files = val.include?('*') ? Dir[base_path(val)] : [base_path(val)]
367
+ files = val.include?('*') ? Dir[basepath(val)] : [basepath(val)]
327
368
  files.each do |file|
328
369
  begin
329
370
  File.delete(file) if File.file?(file)
@@ -336,13 +377,20 @@ module Squared
336
377
  end
337
378
  end
338
379
 
380
+ def graph(*, sync: invoked_sync?('graph'), pass: [], out: nil)
381
+ data = graph_collect(self)
382
+ data[name] << self unless out
383
+ run_graph(data, name, out, sync: sync, pass: pass)
384
+ out
385
+ end
386
+
339
387
  def log
340
388
  return @log unless @log.is_a?(::Array)
341
389
 
342
390
  @log = Logger.new(enabled? ? @log[0] : nil, **@log[1])
343
391
  end
344
392
 
345
- def base_path(*args, ascend: nil)
393
+ def basepath(*args, ascend: nil)
346
394
  ret = path.join(*args)
347
395
  return ret unless ascend && !ret.exist?
348
396
 
@@ -370,6 +418,15 @@ module Squared
370
418
  run_set(output[0], *val, **kwargs)
371
419
  when :parent
372
420
  @parent = val if (val = val.first).is_a?(Project::Base)
421
+ when :graph
422
+ @graph = case val.first
423
+ when nil, false
424
+ nil
425
+ else
426
+ val.flatten.map!(&:to_s).freeze
427
+ end
428
+ when :dependfile
429
+ @dependfile = basepath(*val)
373
430
  else
374
431
  instance_variable_set :"@#{key}", val.first
375
432
  end
@@ -378,14 +435,14 @@ module Squared
378
435
  end
379
436
  end
380
437
 
381
- def variables
382
- VAR_SET
383
- end
384
-
385
438
  def allref
386
439
  @ref.reverse_each
387
440
  end
388
441
 
442
+ def dependtype(*)
443
+ @dependindex ? @dependindex.succ : 0
444
+ end
445
+
389
446
  def version(*); end
390
447
 
391
448
  def inspect
@@ -428,6 +485,10 @@ module Squared
428
485
  !!@depend
429
486
  end
430
487
 
488
+ def graph?
489
+ @graph.is_a?(::Array) && !@graph.empty?
490
+ end
491
+
431
492
  def copy?
432
493
  runnable?(@copy) || workspace.task_defined?(name, 'copy')
433
494
  end
@@ -454,22 +515,22 @@ module Squared
454
515
 
455
516
  private
456
517
 
518
+ def variables
519
+ VAR_SET
520
+ end
521
+
457
522
  def puts(*args)
458
523
  puts_oe(*args, pipe: pipe)
459
524
  end
460
525
 
461
- def run(cmd = @session, var = nil, exception: @exception, sync: true, req: nil, banner: ARG[:BANNER], **)
462
- if req && !base_path(req).exist?
463
- log.warn "#{req} (not found)"
464
- return
465
- end
526
+ def run(cmd = @session, var = nil, exception: @exception, sync: true, banner: true, **)
466
527
  cmd = session_done(cmd)
467
528
  log.info cmd
468
529
  begin
469
530
  if cmd =~ /^\S+:(\S+:?)+$/ && workspace.task_defined?(cmd)
470
531
  print_item if sync
471
532
  log.warn "ENV was discarded: #{var}" if var
472
- invoke(cmd, exception: exception, warning: warning?)
533
+ task_invoke(cmd, exception: exception, warning: warning?)
473
534
  else
474
535
  print_item format_banner(cmd, banner: banner) if sync
475
536
  args = var.is_a?(::Hash) ? [var, cmd] : [cmd]
@@ -481,15 +542,72 @@ module Squared
481
542
  end
482
543
  end
483
544
 
484
- def run_s(*cmd, env: nil, **kwargs)
485
- cmd.each { |val| run(val, env, banner: !!verbose, **kwargs) }
545
+ def run_s(*cmd, env: nil, banner: verbose != false, **kwargs)
546
+ cmd.each { |val| run(val, env, banner: banner, **kwargs) }
547
+ end
548
+
549
+ def run_graph(data, start, out = nil, sync: true, pass: [], done: [], depth: 0, last: false)
550
+ check = ->(deps) { deps.reject { |val| done.include?(val) } }
551
+ items = check.(data[start])
552
+ if out
553
+ a, b, c, d, e = ARG[:GRAPH]
554
+ out << case depth
555
+ when 0
556
+ start
557
+ when 1
558
+ if items.empty?
559
+ "#{d}#{b * 4} #{start}"
560
+ else
561
+ "#{last ? d : c}#{b * 3}#{e} #{start}"
562
+ end
563
+ else
564
+ "#{a}#{' ' * (depth - 1)}#{d}#{b * 2}#{items.empty? ? b : e} #{start}"
565
+ end
566
+ end
567
+ items.each_with_index do |proj, i|
568
+ next if done.include?(proj)
569
+
570
+ j = out ? i == items.size - 1 || check.(items[i + 1..-1]).empty? : false
571
+ s = proj.name
572
+ unless start == s || (none = check.(data.fetch(s, [])).empty?)
573
+ run_graph(data, s, out, sync: sync, pass: pass, done: done, depth: depth.succ, last: j)
574
+ end
575
+ if !out
576
+ if (script = workspace.script_get(group: proj.group, ref: proj.ref))
577
+ tasks = script[:graph]
578
+ end
579
+ (tasks || (dev? ? %w[build copy] : %w[depend build])).each do |meth|
580
+ next if pass.include?(meth)
581
+
582
+ if workspace.task_defined?(cmd = task_join(s, meth))
583
+ run(cmd, sync: false, banner: false)
584
+ elsif proj.has?(meth)
585
+ proj.__send__(meth.to_sym, sync: sync)
586
+ end
587
+ end
588
+ elsif none
589
+ a, b, c, d = ARG[:GRAPH]
590
+ out << if depth == 0
591
+ "#{i == items.size - 1 ? d : c}#{b * 4} #{s}"
592
+ else
593
+ "#{last && depth == 1 ? ' ' : a}#{' ' * depth}#{j ? d : c}#{b * 3} #{s}"
594
+ end
595
+ end
596
+ done << proj
597
+ end
486
598
  end
487
599
 
488
- def run_task(key)
489
- return false unless workspace.task_defined?(key)
600
+ def graph_collect(target, data: {})
601
+ deps = []
602
+ target.instance_variable_get(:@graph)&.each do |val|
603
+ next unless (proj = workspace.find(name: val))
490
604
 
491
- invoke(key, **workspace.invokeargs)
492
- true
605
+ deps << proj
606
+ graph_collect(proj, data: data) if proj.graph? && !data.key?(proj.name)
607
+ deps += data.fetch(val, [])
608
+ end
609
+ data[target.name] = deps.uniq.reject { |proj| proj == target }
610
+ data
493
611
  end
494
612
 
495
613
  def env(key, default = nil, suffix: nil, equals: nil, ignore: nil, strict: false)
@@ -531,7 +649,7 @@ module Squared
531
649
  end
532
650
 
533
651
  def print_item(*val)
534
- puts unless @@print_order == 0 || stdin?
652
+ puts if @@print_order > 0 && verbose && !stdin?
535
653
  @@print_order += 1
536
654
  puts val unless val.empty? || (val.size == 1 && val.first.nil?)
537
655
  end
@@ -542,7 +660,7 @@ module Squared
542
660
  if styles.any? { |s| s.to_s.end_with?('!') }
543
661
  pad = 1
544
662
  elsif !client && styles.size <= 1
545
- styles = [:bold] + styles
663
+ styles = %i[bold] + styles
546
664
  end
547
665
  end
548
666
  n = Project.max_width(lines)
@@ -577,19 +695,19 @@ module Squared
577
695
 
578
696
  def format_desc(action, flag, opts = nil, req: nil, arg: 'opts*')
579
697
  opts = "#{arg}=#{opts.join(',')}" if opts.is_a?(::Array)
580
- val = [@desc]
698
+ out = [@desc]
581
699
  if flag
582
- val << action
700
+ out << action
583
701
  else
584
702
  flag = action
585
703
  end
586
- req = opts ? "#{req}," : "[#{req}]" if req
587
- val << (opts ? "#{flag}[#{req}#{opts}]" : "#{flag}#{req}")
588
- message(*val)
704
+ req &&= opts ? "#{req}," : "[#{req}]"
705
+ out << (opts ? "#{flag}[#{req}#{opts}]" : "#{flag}#{req}")
706
+ message(*out)
589
707
  end
590
708
 
591
- def format_banner(cmd, banner: ARG[:BANNER], multiple: false)
592
- return unless banner
709
+ def format_banner(cmd, banner: true)
710
+ return unless banner && ARG[:BANNER]
593
711
 
594
712
  if (data = workspace.banner_get(*@ref, group: group))
595
713
  client = true
@@ -625,6 +743,36 @@ module Squared
625
743
  end
626
744
  end
627
745
 
746
+ def format_list(items, cmd, type, grep: [], from: nil, each: nil)
747
+ reg = grep.map { |val| Regexp.new(val) }
748
+ out = []
749
+ if (pad = items.size) > 0
750
+ pad = pad.to_s.size
751
+ items.each_with_index do |val, i|
752
+ next unless reg.empty? || reg.any? { |pat| pat.match?(val[0]) }
753
+
754
+ out << "#{(i + 1).to_s.rjust(pad)}. #{each ? each.(val) : val[0]}"
755
+ end
756
+ end
757
+ if out.empty?
758
+ out = ["No #{type} were found:", '']
759
+ unless grep.empty?
760
+ i = 0
761
+ out += grep.map { |s| "#{i += 1}. #{s}" }
762
+ out << ''
763
+ end
764
+ if from
765
+ out << from
766
+ pat = /^(#{Regexp.escape(out.last)})(.*)$/
767
+ end
768
+ else
769
+ pat = /^(\s*\d+\.)(.+)$/
770
+ end
771
+ sub = [headerstyle]
772
+ sub << { pat: pat, styles: theme[:active] } if pat
773
+ emphasize(out, title: task_join(name, cmd), border: borderstyle, sub: sub)
774
+ end
775
+
628
776
  def empty_status(msg, title, obj, always: false)
629
777
  "#{msg}#{!always && (!obj || obj == 0 || obj.to_s.empty?) ? '' : message(hint: message(title, obj.to_s))}"
630
778
  end
@@ -641,42 +789,18 @@ module Squared
641
789
  @session << '--no-color' if flag || !ARG[:COLOR] || stdin?
642
790
  end
643
791
 
644
- def task_join(*val)
645
- val.join(':')
646
- end
647
-
648
792
  def guard_params(action, flag, args: nil, key: nil, pat: nil)
649
793
  if args && key
650
- return unless (val = args[key]).nil? || (pat && !val.match?(pat))
794
+ val = args[key]
795
+ return val unless val.nil? || (pat && !val.match?(pat))
651
796
 
652
797
  @session = nil
653
798
  raise_error(action, "#{flag}[#{key}]", hint: val.nil? ? 'missing' : 'invalid')
654
- elsif args.is_a?(::Array)
655
- return unless args.empty?
656
-
799
+ elsif args.is_a?(::Array) && args.empty?
657
800
  @session = nil
658
- raise_error(action, "#{flag}[]", hint: 'empty')
659
- end
660
- end
661
-
662
- def semver(val)
663
- return val if val[3]
664
-
665
- val[3] = '.'
666
- val[4] = '0'
667
- unless val[1]
668
- val[1] = '.'
669
- val[2] = '0'
801
+ raise_error(action, "#{flag}+", hint: 'empty')
670
802
  end
671
- val
672
- end
673
-
674
- def semmajor(cur, want)
675
- (cur[0] == '0' && want[0] == '0' ? cur[2] != want[2] : cur[0] != want[0]) && !want[5]
676
- end
677
-
678
- def semscan(val)
679
- val.scan(SEM_VER).first
803
+ args
680
804
  end
681
805
 
682
806
  def pwd_set(done = nil, &blk)
@@ -733,8 +857,12 @@ module Squared
733
857
  end
734
858
  end
735
859
 
736
- def source_path?(val)
737
- Pathname.new(val).absolute? ? val.to_s.start_with?(File.join(path, '')) : !val.to_s.match?(%r{^\.\.[/\\]})
860
+ def projectpath?(val)
861
+ Pathname.new(val).absolute? ? val.to_s.start_with?(File.join(path, '')) : !val.to_s.start_with?('..')
862
+ end
863
+
864
+ def semmajor?(cur, want)
865
+ (cur[0] == '0' && want[0] == '0' ? cur[2] != want[2] : cur[0] != want[0]) && !want[5]
738
866
  end
739
867
 
740
868
  def runnable?(val)
@@ -749,7 +877,7 @@ module Squared
749
877
  end
750
878
 
751
879
  def from_sync?(*val)
752
- if invoked?(key = task_join(*val))
880
+ if task_invoked?(key = task_join(*val))
753
881
  !workspace.task_defined?(key, 'sync')
754
882
  elsif workspace.series.sync?(task_join(key, 'sync'))
755
883
  true
@@ -758,14 +886,15 @@ module Squared
758
886
 
759
887
  def invoked_sync?(action, val = nil)
760
888
  return true if !val.nil? || from_sync?(ac = workspace.task_name(action))
761
-
762
889
  return val if group && !(val = from_sync?(ac, group)).nil?
763
890
  return val if (base = workspace.find_base(self)) && !(val = from_sync?(ac, base.ref)).nil?
764
891
 
765
- return true if invoked?(name, ac) && (!invoked?(ac) || !workspace.task_defined?(ac, 'sync'))
766
-
767
- val = workspace.series.name_get(action)
768
- val == action ? false : invoked_sync?(val)
892
+ if task_invoked?(task_join(name, ac)) && (!task_invoked?(ac) || !workspace.task_defined?(ac, 'sync'))
893
+ true
894
+ else
895
+ val = workspace.series.name_get(action)
896
+ val == action ? false : invoked_sync?(val)
897
+ end
769
898
  end
770
899
 
771
900
  def stdin?
@@ -776,18 +905,54 @@ module Squared
776
905
  workspace.warning
777
906
  end
778
907
 
908
+ def projectmap(files, parent: false)
909
+ files = files.select { |val| projectpath?(val) } unless parent
910
+ files.map { |val| val == '.' ? '.' : shell_quote(basepath(val.strip)) }
911
+ end
912
+
913
+ def semver(val)
914
+ return val if val[3]
915
+
916
+ val[3] = '.'
917
+ val[4] = '0'
918
+ unless val[1]
919
+ val[1] = '.'
920
+ val[2] = '0'
921
+ end
922
+ val
923
+ end
924
+
925
+ def semscan(val)
926
+ val.scan(SEM_VER).first
927
+ end
928
+
779
929
  def borderstyle
780
930
  if (data = workspace.banner_get(*@ref, group: group))
781
931
  data[:border]
782
932
  end
783
933
  end
784
934
 
935
+ def headerstyle
936
+ { pat: /^(\S+)(\s+)$/, styles: theme[:header] }
937
+ end
938
+
785
939
  def scriptargs
786
940
  { target: script? ? @output[1] : @output[0], ref: ref, group: group, global: @global }
787
941
  end
942
+
943
+ def indexdata(val)
944
+ if (data = /\A\^(\d+)(:.+)?\z/.match(val))
945
+ [data[1].to_i, data[2] ? data[2][1..-1] : nil]
946
+ end
947
+ end
948
+
949
+ def indexerror(val, list = nil)
950
+ raise_error("requested index #{val}", hint: list && "of #{list.size}")
951
+ end
788
952
  end
789
953
 
790
- Application.base_project = Base
954
+ Application.impl_project = Base
955
+ Application.attr_banner = Common::SymSet.new(%i[name project path ref group parent])
791
956
  end
792
957
  end
793
958
  end