squared 0.0.12 → 0.1.0

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