squared 0.0.11 → 0.0.12

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