squared 0.0.12 → 0.1.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.
@@ -12,12 +12,11 @@ module Squared
12
12
  include System
13
13
  include Shell
14
14
  include Utils
15
- include ::Rake::DSL
16
-
17
- SEM_VER = /(\d+)(?:(\.)(\d+))?(?:(\.)(\d+)(\S+)?)?/.freeze
15
+ include Rake::DSL
18
16
 
19
17
  VAR_SET = %i[parent global envname dependfile theme run script env depend graph doc test copy clean].freeze
20
- private_constant :VAR_SET
18
+ SEM_VER = /(\d+)(?:(\.)(\d+))?(?:(\.)(\d+)(\S+)?)?/.freeze
19
+ private_constant :VAR_SET, :SEM_VER
21
20
 
22
21
  class << self
23
22
  def populate(*); end
@@ -31,9 +30,9 @@ module Squared
31
30
 
32
31
  def as_path(val)
33
32
  case val
34
- when ::Pathname
33
+ when Pathname
35
34
  val
36
- when ::String
35
+ when String
37
36
  Pathname.new(val)
38
37
  end
39
38
  end
@@ -42,13 +41,13 @@ module Squared
42
41
  @ref ||= to_s.downcase.to_sym
43
42
  end
44
43
 
45
- def to_s
46
- super.match(/[^:]+$/)[0]
47
- end
48
-
49
44
  def config?(*)
50
45
  false
51
46
  end
47
+
48
+ def to_s
49
+ super.match(/[^:]+$/)[0]
50
+ end
52
51
  end
53
52
 
54
53
  @@tasks = {
@@ -151,7 +150,7 @@ module Squared
151
150
  def initialize_logger(log: nil, **)
152
151
  return if @log
153
152
 
154
- log = log.is_a?(::Hash) ? log.dup : { file: log }
153
+ log = log.is_a?(Hash) ? log.dup : { file: log }
155
154
  unless (file = env('LOG_FILE'))
156
155
  file = case env('LOG_AUTO')
157
156
  when 'y', 'year'
@@ -171,7 +170,7 @@ module Squared
171
170
  raise if @exception
172
171
 
173
172
  file = nil
174
- warn log_message(::Logger::WARN, e) if warning?
173
+ warn log_message(Logger::WARN, e) if warning?
175
174
  end
176
175
  end
177
176
  log[:progname] ||= @name
@@ -187,13 +186,13 @@ module Squared
187
186
  @dev = env_match("#{pre}_DEV", dev)
188
187
  @prod = env_match("#{pre}_PROD", prod)
189
188
  cmd = @output[0]
190
- unless cmd == false || cmd.is_a?(::Array) || (val = env('BUILD', suffix: 'OPTS')).nil?
189
+ unless cmd == false || cmd.is_a?(Array) || (val = env('BUILD', suffix: 'OPTS')).nil?
191
190
  @output[cmd ? 1 : 3] = shell_split(val, join: true)
192
191
  end
193
192
  unless @output[2] == false || (val = env('BUILD', suffix: 'ENV')).nil?
194
193
  begin
195
194
  data = JSON.parse(val)
196
- raise_error('invalid JSON object', val, hint: "#{prefix}_ENV") unless data.is_a?(::Hash)
195
+ raise_error('invalid JSON object', val, hint: "#{prefix}_ENV") unless data.is_a?(Hash)
197
196
  @output[2] = data
198
197
  rescue StandardError => e
199
198
  log.warn e
@@ -241,22 +240,34 @@ module Squared
241
240
  when :graph
242
241
  next unless graph?
243
242
 
243
+ check = lambda do |args|
244
+ next args if (args = args.to_a).empty?
245
+
246
+ guard_params(action, flag, args: args.reject { |val| name == val.to_s })
247
+ end
248
+
249
+ desc format_desc(action, flag, 'project*')
244
250
  if flag == :run
245
- desc format_desc(action, flag, 'pass*')
246
- task flag, [:pass] do |_, args|
247
- graph(pass: args.to_a)
251
+ task flag, [:project] do |_, args|
252
+ graph check.(args)
248
253
  end
249
254
  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
- ])
255
+ task flag, [:project] do |_, args|
256
+ out, done = graph(check.(args), out: [])
257
+ out.map! do |val|
258
+ done.each_with_index do |proj, i|
259
+ next unless val.end_with?(" #{proj.name}")
260
+
261
+ val += " (#{i.succ})"
262
+ break
263
+ end
264
+ val
265
+ end
266
+ emphasize(out, title: path, right: true, border: borderstyle, sub: [
267
+ { pat: /^(#{Regexp.escape(path.to_s)})(.*)$/, styles: theme[:header] },
268
+ { pat: /^(#{Regexp.escape(name)})(.*)$/, styles: theme[:active] },
269
+ { pat: /^(.+ )(\()(\d+)(\))(.*)$/, styles: theme[:inline], index: 3 }
270
+ ])
260
271
  end
261
272
  end
262
273
  end
@@ -276,18 +287,28 @@ module Squared
276
287
  end
277
288
 
278
289
  def add(path, name = nil, **kwargs, &blk)
279
- if path.is_a?(::String) && (data = %r{^(.+)[\\/]\*+$}.match(path))
280
- path = basepath(data[1]).children.select(&:directory?)
290
+ checkdir = lambda do |val|
291
+ if val.directory? && val.exist?
292
+ true
293
+ else
294
+ log.warn "workspace \"#{val}\" (not found)"
295
+ false
296
+ end
297
+ end
298
+ if path.is_a?(String) && (data = %r{^(.+)[\\/]\*+$}.match(path))
299
+ return self unless checkdir.(path = basepath(data[1]))
300
+
301
+ path = path.children.select { |val| checkdir.(val) }
281
302
  end
282
- if path.is_a?(::Array)
303
+ if path.is_a?(Array)
283
304
  name = @name if name == true
284
305
  path.each { |val| add(val, name && task_join(name, File.basename(val)), **kwargs, &blk) }
285
306
  return self
286
- elsif !projectpath?(path = basepath(path))
307
+ elsif !projectpath?(path = basepath(path)) || !checkdir.(path)
287
308
  return self
288
- elsif name.is_a?(::Symbol)
309
+ elsif name.is_a?(Symbol)
289
310
  name = name.to_s
290
- elsif !name.is_a?(::String)
311
+ elsif !name.is_a?(String)
291
312
  name = nil
292
313
  end
293
314
  if @withargs
@@ -319,9 +340,9 @@ module Squared
319
340
  end
320
341
  if cmd
321
342
  case opts
322
- when ::String
343
+ when String
323
344
  cmd = "#{cmd} #{opts}"
324
- when ::Array
345
+ when Array
325
346
  cmd = cmd.join(' && ')
326
347
  else
327
348
  cmd = [cmd, compose(opts, script: false)].compact.join(' ') unless opts == false || !respond_to?(:compose)
@@ -352,9 +373,9 @@ module Squared
352
373
 
353
374
  def clean(*)
354
375
  case @clean
355
- when ::String
376
+ when String
356
377
  run_s(@clean, sync: invoked_sync?('clean'))
357
- when ::Enumerable
378
+ when Enumerable
358
379
  as_a(@clean).each do |val|
359
380
  if (val = val.to_s) =~ %r{[\\/]$}
360
381
  dir = Pathname.new(val)
@@ -377,34 +398,22 @@ module Squared
377
398
  end
378
399
  end
379
400
 
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
-
387
- def log
388
- return @log unless @log.is_a?(::Array)
389
-
390
- @log = Logger.new(enabled? ? @log[0] : nil, **@log[1])
391
- end
392
-
393
- def basepath(*args, ascend: nil)
394
- ret = path.join(*args)
395
- return ret unless ascend && !ret.exist?
396
-
397
- path.parent.ascend.each do |dir|
398
- target = dir.join(*args)
399
- return target if target.exist?
400
- break if (ascend.is_a?(String) && dir.join(ascend).exist?) || workspace.root == dir || parent&.path == dir
401
+ def graph(start = [], tasks = nil, sync: invoked_sync?('graph'), pass: [], out: nil)
402
+ if (val = env('GRAPH', strict: true))
403
+ tasks ||= []
404
+ split_escape(val).each do |task|
405
+ if ref?(task.to_sym) && (script = workspace.script_get(:graph, ref: task.to_sym))
406
+ tasks += script[:graph]
407
+ else
408
+ tasks << task
409
+ end
410
+ end
401
411
  end
402
- ret
403
- end
404
-
405
- def color(val)
406
- ret = theme[val]
407
- ret && !ret.empty? ? ret : [val]
412
+ pass.concat(split_escape(val)) if (val = env('GRAPH_PASS', strict: true))
413
+ 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
408
417
  end
409
418
 
410
419
  def variable_set(key, *val, **kwargs)
@@ -435,28 +444,6 @@ module Squared
435
444
  end
436
445
  end
437
446
 
438
- def allref
439
- @ref.reverse_each
440
- end
441
-
442
- def dependtype(*)
443
- @dependindex ? @dependindex.succ : 0
444
- end
445
-
446
- def version(*); end
447
-
448
- def inspect
449
- "#<#{self.class}: #{name} => #{self}>"
450
- end
451
-
452
- def to_s
453
- path.to_s
454
- end
455
-
456
- def to_sym
457
- name.to_sym
458
- end
459
-
460
447
  def enabled?(ref = nil)
461
448
  return false if ref && !ref?(ref)
462
449
 
@@ -486,7 +473,7 @@ module Squared
486
473
  end
487
474
 
488
475
  def graph?
489
- @graph.is_a?(::Array) && !@graph.empty?
476
+ @graph.is_a?(Array) && !@graph.empty?
490
477
  end
491
478
 
492
479
  def copy?
@@ -513,12 +500,48 @@ module Squared
513
500
  @prod != false && workspace.prod?(pat: @prod, **scriptargs)
514
501
  end
515
502
 
516
- private
503
+ def version(*); end
517
504
 
518
- def variables
519
- VAR_SET
505
+ def dependtype(*)
506
+ @dependindex ? @dependindex.succ : 0
507
+ end
508
+
509
+ def log
510
+ return @log unless @log.is_a?(Array)
511
+
512
+ @log = Logger.new(enabled? ? @log[0] : nil, **@log[1])
513
+ end
514
+
515
+ def allref
516
+ @ref.reverse_each
517
+ end
518
+
519
+ def basepath(*args, ascend: nil)
520
+ ret = path.join(*args)
521
+ return ret unless ascend && !ret.exist?
522
+
523
+ path.parent.ascend.each do |dir|
524
+ target = dir.join(*args)
525
+ return target if target.exist?
526
+ break if (ascend.is_a?(String) && dir.join(ascend).exist?) || workspace.root == dir || parent&.path == dir
527
+ end
528
+ ret
529
+ end
530
+
531
+ def inspect
532
+ "#<#{self.class}: #{name} => #{self}>"
533
+ end
534
+
535
+ def to_s
536
+ path.to_s
520
537
  end
521
538
 
539
+ def to_sym
540
+ name.to_sym
541
+ end
542
+
543
+ private
544
+
522
545
  def puts(*args)
523
546
  puts_oe(*args, pipe: pipe)
524
547
  end
@@ -533,7 +556,7 @@ module Squared
533
556
  task_invoke(cmd, exception: exception, warning: warning?)
534
557
  else
535
558
  print_item format_banner(cmd, banner: banner) if sync
536
- args = var.is_a?(::Hash) ? [var, cmd] : [cmd]
559
+ args = var.is_a?(Hash) ? [var, cmd] : [cmd]
537
560
  shell(*args, chdir: path, exception: exception)
538
561
  end
539
562
  rescue StandardError => e
@@ -546,9 +569,25 @@ module Squared
546
569
  cmd.each { |val| run(val, env, banner: banner, **kwargs) }
547
570
  end
548
571
 
549
- def run_graph(data, start, out = nil, sync: true, pass: [], done: [], depth: 0, last: false)
572
+ def run_graph(data, start, tasks = nil, out = nil, sync: true, pass: [], done: [], depth: 0, last: false,
573
+ single: false)
550
574
  check = ->(deps) { deps.reject { |val| done.include?(val) } }
551
- items = check.(data[start])
575
+ dedupe = lambda do |name|
576
+ next [] unless (ret = data[name])
577
+
578
+ ret.dup.each do |proj|
579
+ next if proj.name == name
580
+
581
+ data[proj.name]&.each { |dep| ret.delete(dep) if ret.include?(dep) }
582
+ end
583
+ ret
584
+ end
585
+ if depth == 0
586
+ items = check.(dedupe.(start))
587
+ single = items.size == 1
588
+ else
589
+ items = check.(data[start])
590
+ end
552
591
  if out
553
592
  a, b, c, d, e = ARG[:GRAPH]
554
593
  out << case depth
@@ -561,22 +600,30 @@ module Squared
561
600
  "#{last ? d : c}#{b * 3}#{e} #{start}"
562
601
  end
563
602
  else
564
- "#{a}#{' ' * (depth - 1)}#{d}#{b * 2}#{items.empty? ? b : e} #{start}"
603
+ "#{single ? ' ' : a}#{' ' * (depth - 1)}#{last ? d : c}#{b * 3}#{items.empty? ? b : e} #{start}"
565
604
  end
566
605
  end
567
606
  items.each_with_index do |proj, i|
568
607
  next if done.include?(proj)
569
608
 
570
- j = out ? i == items.size - 1 || check.(items[i + 1..-1]).empty? : false
571
609
  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)
610
+ t = dedupe.(s)
611
+ j = if out
612
+ if i == items.size - 1 || check.(post = items[i + 1..-1]).empty?
613
+ true
614
+ elsif !t.empty? && depth > 0
615
+ post.reject { |pr| t.include?(pr) }.empty?
616
+ end
617
+ end
618
+ unless start == s || (none = check.(t).empty?)
619
+ run_graph(data, s, tasks, out, sync: sync, pass: pass, done: done, depth: depth.succ, single: single,
620
+ last: j == true)
574
621
  end
575
622
  if !out
576
- if (script = workspace.script_get(group: proj.group, ref: proj.ref))
577
- tasks = script[:graph]
623
+ unless tasks || !(script = workspace.script_get(:graph, group: proj.group, ref: proj.allref))
624
+ group = script[:graph]
578
625
  end
579
- (tasks || (dev? ? %w[build copy] : %w[depend build])).each do |meth|
626
+ (tasks || group || (dev? ? ['build', 'copy'] : ['depend', 'build'])).each do |meth|
580
627
  next if pass.include?(meth)
581
628
 
582
629
  if workspace.task_defined?(cmd = task_join(s, meth))
@@ -590,17 +637,24 @@ module Squared
590
637
  out << if depth == 0
591
638
  "#{i == items.size - 1 ? d : c}#{b * 4} #{s}"
592
639
  else
593
- "#{last && depth == 1 ? ' ' : a}#{' ' * depth}#{j ? d : c}#{b * 3} #{s}"
640
+ f = ''.dup
641
+ k = 0
642
+ while k < depth
643
+ f += "#{(last && !j) || (j && k > 0 && k == depth - 1) || single ? ' ' : a} "
644
+ k += 1
645
+ end
646
+ f + "#{j ? d : c}#{b * 3} #{s}"
594
647
  end
595
648
  end
596
649
  done << proj
597
650
  end
651
+ done
598
652
  end
599
653
 
600
- def graph_collect(target, data: {})
654
+ def graph_collect(target, start = [], data: {})
601
655
  deps = []
602
- target.instance_variable_get(:@graph)&.each do |val|
603
- next unless (proj = workspace.find(name: val))
656
+ (start.empty? ? target.instance_variable_get(:@graph) : start)&.each do |val|
657
+ next unless (proj = workspace.find(name: val)) && proj.enabled?
604
658
 
605
659
  deps << proj
606
660
  graph_collect(proj, data: data) if proj.graph? && !data.key?(proj.name)
@@ -614,9 +668,11 @@ module Squared
614
668
  a = "#{key}_#{@envname}"
615
669
  ret = if suffix
616
670
  ENV.fetch("#{a}_#{suffix}", '')
671
+ elsif strict
672
+ ENV[a].to_s
617
673
  else
618
- ignore = ['0'].freeze if ignore.nil? && !strict
619
- ENV[a] || (strict ? '' : ENV.fetch(key, ''))
674
+ ignore = ['0'].freeze if ignore.nil?
675
+ ENV[a] || ENV.fetch(key, '')
620
676
  end
621
677
  return ret == equals.to_s unless equals.nil?
622
678
 
@@ -694,7 +750,7 @@ module Squared
694
750
  end
695
751
 
696
752
  def format_desc(action, flag, opts = nil, req: nil, arg: 'opts*')
697
- opts = "#{arg}=#{opts.join(',')}" if opts.is_a?(::Array)
753
+ opts = "#{arg}=#{opts.join(',')}" if opts.is_a?(Array)
698
754
  out = [@desc]
699
755
  if flag
700
756
  out << action
@@ -718,11 +774,11 @@ module Squared
718
774
  out = []
719
775
  out << cmd.sub(/^\S+/, &:upcase) if data[:command]
720
776
  data[:order].each do |val|
721
- if val.is_a?(::Array)
777
+ if val.is_a?(Array)
722
778
  s = ' '
723
779
  found = false
724
780
  val = val.map do |meth|
725
- if meth.is_a?(::String)
781
+ if meth.is_a?(String)
726
782
  s = ''
727
783
  meth
728
784
  elsif respond_to?(meth)
@@ -782,7 +838,11 @@ module Squared
782
838
  end
783
839
 
784
840
  def append_value(opts)
785
- opts.each { |val| @session << val }
841
+ opts.each { |val| @session << shell_escape(val) }
842
+ end
843
+
844
+ def append_option(list, **kwargs)
845
+ list.each { |val| @session << "--#{val}" if option(val, **kwargs) }
786
846
  end
787
847
 
788
848
  def append_nocolor(flag = nil)
@@ -796,13 +856,50 @@ module Squared
796
856
 
797
857
  @session = nil
798
858
  raise_error(action, "#{flag}[#{key}]", hint: val.nil? ? 'missing' : 'invalid')
799
- elsif args.is_a?(::Array) && args.empty?
859
+ elsif args.is_a?(Array) && args.empty?
800
860
  @session = nil
801
861
  raise_error(action, "#{flag}+", hint: 'empty')
802
862
  end
803
863
  args
804
864
  end
805
865
 
866
+ def projectmap(files, parent: false)
867
+ files = files.select { |val| projectpath?(val) } unless parent
868
+ files.map { |val| val == '.' ? '.' : shell_quote(basepath(val.strip)) }
869
+ end
870
+
871
+ def semver(val)
872
+ return val if val[3]
873
+
874
+ val[3] = '.'
875
+ val[4] = '0'
876
+ unless val[1]
877
+ val[1] = '.'
878
+ val[2] = '0'
879
+ end
880
+ val
881
+ end
882
+
883
+ def semscan(val, fill: true)
884
+ ret = val.scan(SEM_VER).first
885
+ fill ? semver(ret) : ret
886
+ end
887
+
888
+ def indexitem(val)
889
+ return unless (data = /\A\^(\d+)(:.+)?\z/.match(val))
890
+
891
+ [data[1].to_i, data[2] ? data[2][1..-1] : nil]
892
+ end
893
+
894
+ def indexerror(val, list = nil)
895
+ raise_error("requested index #{val}", hint: list && "of #{list.size}")
896
+ end
897
+
898
+ def color(val)
899
+ ret = theme[val]
900
+ ret && !ret.empty? ? ret : [val]
901
+ end
902
+
806
903
  def pwd_set(done = nil, &blk)
807
904
  pwd = Pathname.pwd
808
905
  if block_given?
@@ -837,7 +934,7 @@ module Squared
837
934
  end
838
935
  end
839
936
  unless @output[2] == false
840
- if val.is_a?(::Hash)
937
+ if val.is_a?(Hash)
841
938
  @output[2] = val
842
939
  elsif val == false
843
940
  @output[2] = false
@@ -850,7 +947,7 @@ module Squared
850
947
  return if @output[1] == false && @output[0].nil?
851
948
 
852
949
  @output[0] = nil
853
- @output[1] = if @global && cmd.is_a?(::Array)
950
+ @output[1] = if @global && cmd.is_a?(Array)
854
951
  cmd[prod == true ? 1 : 0]
855
952
  else
856
953
  cmd
@@ -867,10 +964,10 @@ module Squared
867
964
 
868
965
  def runnable?(val)
869
966
  case val
870
- when ::String
967
+ when String
871
968
  true
872
- when ::Enumerable
873
- !val.is_a?(::Hash)
969
+ when Enumerable
970
+ !val.is_a?(Hash)
874
971
  else
875
972
  false
876
973
  end
@@ -905,31 +1002,14 @@ module Squared
905
1002
  workspace.warning
906
1003
  end
907
1004
 
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
1005
+ def variables
1006
+ VAR_SET
927
1007
  end
928
1008
 
929
1009
  def borderstyle
930
- if (data = workspace.banner_get(*@ref, group: group))
931
- data[:border]
932
- end
1010
+ return unless (data = workspace.banner_get(*@ref, group: group))
1011
+
1012
+ data[:border]
933
1013
  end
934
1014
 
935
1015
  def headerstyle
@@ -939,16 +1019,6 @@ module Squared
939
1019
  def scriptargs
940
1020
  { target: script? ? @output[1] : @output[0], ref: ref, group: group, global: @global }
941
1021
  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
952
1022
  end
953
1023
 
954
1024
  Application.impl_project = Base