scout-essentials 1.0.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.
Files changed (107) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.vimproject +78 -0
  4. data/Gemfile +14 -0
  5. data/LICENSE.txt +20 -0
  6. data/README.rdoc +18 -0
  7. data/Rakefile +47 -0
  8. data/VERSION +1 -0
  9. data/lib/scout/cmd.rb +348 -0
  10. data/lib/scout/concurrent_stream.rb +284 -0
  11. data/lib/scout/config.rb +168 -0
  12. data/lib/scout/exceptions.rb +77 -0
  13. data/lib/scout/indiferent_hash/case_insensitive.rb +30 -0
  14. data/lib/scout/indiferent_hash/options.rb +115 -0
  15. data/lib/scout/indiferent_hash.rb +96 -0
  16. data/lib/scout/log/color.rb +224 -0
  17. data/lib/scout/log/color_class.rb +269 -0
  18. data/lib/scout/log/fingerprint.rb +69 -0
  19. data/lib/scout/log/progress/report.rb +244 -0
  20. data/lib/scout/log/progress/util.rb +173 -0
  21. data/lib/scout/log/progress.rb +106 -0
  22. data/lib/scout/log/trap.rb +107 -0
  23. data/lib/scout/log.rb +441 -0
  24. data/lib/scout/meta_extension.rb +100 -0
  25. data/lib/scout/misc/digest.rb +63 -0
  26. data/lib/scout/misc/filesystem.rb +25 -0
  27. data/lib/scout/misc/format.rb +255 -0
  28. data/lib/scout/misc/helper.rb +31 -0
  29. data/lib/scout/misc/insist.rb +56 -0
  30. data/lib/scout/misc/monitor.rb +66 -0
  31. data/lib/scout/misc/system.rb +73 -0
  32. data/lib/scout/misc.rb +10 -0
  33. data/lib/scout/named_array.rb +138 -0
  34. data/lib/scout/open/lock/lockfile.rb +587 -0
  35. data/lib/scout/open/lock.rb +68 -0
  36. data/lib/scout/open/remote.rb +135 -0
  37. data/lib/scout/open/stream.rb +491 -0
  38. data/lib/scout/open/util.rb +244 -0
  39. data/lib/scout/open.rb +170 -0
  40. data/lib/scout/path/find.rb +204 -0
  41. data/lib/scout/path/tmpfile.rb +8 -0
  42. data/lib/scout/path/util.rb +127 -0
  43. data/lib/scout/path.rb +51 -0
  44. data/lib/scout/persist/open.rb +17 -0
  45. data/lib/scout/persist/path.rb +15 -0
  46. data/lib/scout/persist/serialize.rb +157 -0
  47. data/lib/scout/persist.rb +104 -0
  48. data/lib/scout/resource/open.rb +8 -0
  49. data/lib/scout/resource/path.rb +80 -0
  50. data/lib/scout/resource/produce/rake.rb +69 -0
  51. data/lib/scout/resource/produce.rb +151 -0
  52. data/lib/scout/resource/scout.rb +3 -0
  53. data/lib/scout/resource/software.rb +178 -0
  54. data/lib/scout/resource/util.rb +59 -0
  55. data/lib/scout/resource.rb +40 -0
  56. data/lib/scout/simple_opt/accessor.rb +54 -0
  57. data/lib/scout/simple_opt/doc.rb +126 -0
  58. data/lib/scout/simple_opt/get.rb +57 -0
  59. data/lib/scout/simple_opt/parse.rb +67 -0
  60. data/lib/scout/simple_opt/setup.rb +26 -0
  61. data/lib/scout/simple_opt.rb +5 -0
  62. data/lib/scout/tmpfile.rb +129 -0
  63. data/lib/scout-essentials.rb +10 -0
  64. data/scout-essentials.gemspec +143 -0
  65. data/share/color/color_names +507 -0
  66. data/share/color/diverging_colors.hex +12 -0
  67. data/share/software/install_helpers +523 -0
  68. data/test/scout/indiferent_hash/test_case_insensitive.rb +16 -0
  69. data/test/scout/indiferent_hash/test_options.rb +46 -0
  70. data/test/scout/log/test_color.rb +0 -0
  71. data/test/scout/log/test_progress.rb +108 -0
  72. data/test/scout/misc/test_digest.rb +30 -0
  73. data/test/scout/misc/test_filesystem.rb +30 -0
  74. data/test/scout/misc/test_insist.rb +13 -0
  75. data/test/scout/misc/test_system.rb +21 -0
  76. data/test/scout/open/test_lock.rb +52 -0
  77. data/test/scout/open/test_remote.rb +25 -0
  78. data/test/scout/open/test_stream.rb +676 -0
  79. data/test/scout/open/test_util.rb +73 -0
  80. data/test/scout/path/test_find.rb +110 -0
  81. data/test/scout/path/test_util.rb +22 -0
  82. data/test/scout/persist/test_open.rb +37 -0
  83. data/test/scout/persist/test_path.rb +37 -0
  84. data/test/scout/persist/test_serialize.rb +114 -0
  85. data/test/scout/resource/test_path.rb +58 -0
  86. data/test/scout/resource/test_produce.rb +94 -0
  87. data/test/scout/resource/test_software.rb +24 -0
  88. data/test/scout/resource/test_util.rb +38 -0
  89. data/test/scout/simple_opt/test_doc.rb +16 -0
  90. data/test/scout/simple_opt/test_get.rb +11 -0
  91. data/test/scout/simple_opt/test_parse.rb +10 -0
  92. data/test/scout/simple_opt/test_setup.rb +77 -0
  93. data/test/scout/test_cmd.rb +85 -0
  94. data/test/scout/test_concurrent_stream.rb +29 -0
  95. data/test/scout/test_config.rb +66 -0
  96. data/test/scout/test_indiferent_hash.rb +26 -0
  97. data/test/scout/test_log.rb +32 -0
  98. data/test/scout/test_meta_extension.rb +80 -0
  99. data/test/scout/test_misc.rb +6 -0
  100. data/test/scout/test_named_array.rb +43 -0
  101. data/test/scout/test_open.rb +146 -0
  102. data/test/scout/test_path.rb +54 -0
  103. data/test/scout/test_persist.rb +186 -0
  104. data/test/scout/test_resource.rb +26 -0
  105. data/test/scout/test_tmpfile.rb +53 -0
  106. data/test/test_helper.rb +50 -0
  107. metadata +247 -0
@@ -0,0 +1,151 @@
1
+ require_relative '../open'
2
+ require_relative '../tmpfile'
3
+ require_relative 'produce/rake'
4
+
5
+ module Resource
6
+ def claim(path, type, content = nil, &block)
7
+ if type == :rake
8
+ @rake_dirs ||= {}
9
+ @rake_dirs[path] = content || block
10
+ else
11
+ @resources ||= {}
12
+ @resources[path] = [type, content || block]
13
+ end
14
+ end
15
+
16
+ def rake_for(path)
17
+ @rake_dirs ||= {}
18
+ @rake_dirs.select{|dir, content|
19
+ Misc.path_relative_to(dir, path)
20
+ }.sort_by{|dir, content|
21
+ dir.length
22
+ }.last
23
+ end
24
+
25
+ def has_rake(path)
26
+ !! rake_for(path)
27
+ end
28
+
29
+ def run_rake(path, rakefile, rake_dir)
30
+ task = Misc.path_relative_to rake_dir, path
31
+ rakefile = rakefile.produce if rakefile.respond_to? :produce
32
+ rakefile = rakefile.find if rakefile.respond_to? :find
33
+
34
+ rake_dir = rake_dir.find(:user) if rake_dir.respond_to? :find
35
+
36
+ begin
37
+ if Proc === rakefile
38
+ ScoutRake.run(nil, rake_dir, task, &rakefile)
39
+ else
40
+ ScoutRake.run(rakefile, rake_dir, task)
41
+ end
42
+ rescue ScoutRake::TaskNotFound
43
+ if rake_dir.nil? or rake_dir.empty? or rake_dir == "/" or rake_dir == "./"
44
+ raise $!
45
+ end
46
+ task = File.join(File.basename(rake_dir), task)
47
+ rake_dir = File.dirname(rake_dir)
48
+ retry
49
+ end
50
+ end
51
+
52
+ def produce(path, force = false)
53
+ case
54
+ when (@resources && @resources.include?(path))
55
+ type, content = @resources[path]
56
+ when (Path === path && @resources && @resources.include?(path.original))
57
+ type, content = @resources[path.original]
58
+ when has_rake(path)
59
+ type = :rake
60
+ rake_dir, content = rake_for(path)
61
+ rake_dir = Path.setup(rake_dir.dup, self.pkgdir, self)
62
+ else
63
+ if path !~ /\.(gz|bgz)$/
64
+ begin
65
+ produce(path.annotate(path + '.gz'), force)
66
+ rescue ResourceNotFound
67
+ begin
68
+ produce(path.annotate(path + '.bgz'), force)
69
+ rescue ResourceNotFound
70
+ raise ResourceNotFound, "Resource is missing and does not seem to be claimed: #{ self } -- #{ path } "
71
+ end
72
+ end
73
+ else
74
+ raise ResourceNotFound, "Resource is missing and does not seem to be claimed: #{ self } -- #{ path } "
75
+ end
76
+ end
77
+
78
+ if path.respond_to?(:find)
79
+ final_path = force ? path.find(:default) : path.find
80
+ else
81
+ final_path = path
82
+ end
83
+
84
+ if type and not File.exist?(final_path) or force
85
+ Log.medium "Producing: (#{self.to_s}) #{ final_path }"
86
+ lock_filename = TmpFile.tmp_for_file(final_path, :dir => lock_dir)
87
+
88
+ Open.lock lock_filename do
89
+ FileUtils.rm_rf final_path if force and File.exist? final_path
90
+
91
+ if ! File.exist?(final_path) || force
92
+
93
+ begin
94
+ case type
95
+ when :string
96
+ Open.sensible_write(final_path, content)
97
+ when :csv
98
+ raise "TSV/CSV Not implemented yet"
99
+ #require 'rbbt/tsv/csv'
100
+ #tsv = TSV.csv Open.open(content)
101
+ #Open.sensible_write(final_path, tsv.to_s)
102
+ when :url
103
+ options = {}
104
+ options[:noz] = true if Open.gzip?(final_path) || Open.bgzip?(final_path) || Open.zip?(final_path)
105
+ Open.sensible_write(final_path, Open.open(content, options))
106
+ when :proc
107
+ data = case content.arity
108
+ when 0
109
+ content.call
110
+ when 1
111
+ content.call final_path
112
+ end
113
+ case data
114
+ when String, IO, StringIO
115
+ Open.sensible_write(final_path, data)
116
+ when Array
117
+ Open.sensible_write(final_path, data * "\n")
118
+ when TSV
119
+ Open.sensible_write(final_path, data.dumper_stream)
120
+ when TSV::Dumper
121
+ Open.sensible_write(final_path, data.stream)
122
+ when nil
123
+ else
124
+ raise "Unkown object produced: #{Log.fingerprint data}"
125
+ end
126
+ when :rake
127
+ run_rake(path, content, rake_dir)
128
+ when :install
129
+ software_dir = self.root.software
130
+ name = File.basename(path)
131
+ Resource.install(content, name, software_dir)
132
+ set_software_env(software_dir)
133
+ else
134
+ raise "Could not produce #{ resource }. (#{ type }, #{ content })"
135
+ end
136
+ rescue
137
+ FileUtils.rm_rf final_path if File.exist? final_path
138
+ raise $!
139
+ end
140
+ end
141
+ end
142
+ end
143
+
144
+ # After producing a file, make sure we recheck all locations, the file
145
+ # might have appeared with '.gz' extension for instance
146
+ path.instance_variable_set("@path", {})
147
+
148
+ path
149
+ end
150
+
151
+ end
@@ -0,0 +1,3 @@
1
+ module Scout
2
+ extend Resource
3
+ end
@@ -0,0 +1,178 @@
1
+ module Resource
2
+
3
+ def self.install_helpers
4
+ File.expand_path(Scout.share.software.install_helpers.find(:lib))
5
+ end
6
+
7
+ def self.install(content, name, software_dir = Path.setup('software'), &block)
8
+ software_dir ||= Path.setup('software')
9
+ software_dir = software_dir.find(:user) if Path === software_dir
10
+
11
+ content = block if block_given?
12
+
13
+ preamble = <<-EOF
14
+ #!/bin/bash
15
+
16
+ SOFTWARE_DIR="#{software_dir}"
17
+
18
+ INSTALL_HELPER_FILE="#{install_helpers}"
19
+ source "$INSTALL_HELPER_FILE"
20
+ EOF
21
+
22
+ content = content.call if Proc === content
23
+
24
+ name = content[:name] if Hash === content && content.include?(:name)
25
+ content =
26
+ if content =~ /git:|\.git$/
27
+ {:git => content}
28
+ else
29
+ {:src => content}
30
+ end if String === content and Open.remote?(content)
31
+
32
+ script_text =
33
+ case content
34
+ when nil
35
+ raise "No way to install #{name}"
36
+ when Path
37
+ Open.read(content)
38
+ when String
39
+ if Path.is_filename?(content) and Open.exists?(content)
40
+ Open.read(content)
41
+ else
42
+ content
43
+ end
44
+ when Hash
45
+ name = content[:name] || name
46
+ git = content[:git]
47
+ src = content[:src]
48
+ url = content[:url]
49
+ jar = content[:jar]
50
+ extra = content[:extra]
51
+ commands = content[:commands]
52
+ if git
53
+ <<-EOF
54
+
55
+ name='#{name}'
56
+ url='#{git}'
57
+
58
+ install_git "$name" "$url" #{extra}
59
+
60
+ #{commands}
61
+ EOF
62
+ elsif src
63
+ <<-EOF
64
+
65
+ name='#{name}'
66
+ url='#{src}'
67
+
68
+ install_src "$name" "$url" #{extra}
69
+
70
+ #{commands}
71
+ EOF
72
+ elsif jar
73
+ <<-EOF
74
+
75
+ name='#{name}'
76
+ url='#{jar}'
77
+
78
+ install_jar "$name" "$url" #{extra}
79
+
80
+ #{commands}
81
+ EOF
82
+ else
83
+ <<-EOF
84
+
85
+ name='#{name}'
86
+ url='#{url}'
87
+
88
+ #{commands}
89
+ EOF
90
+ end
91
+ end
92
+
93
+ script = preamble + "\n" + script_text
94
+ Log.debug "Installing software #{name} into #{software_dir} with script:\n" << script
95
+ CMD.cmd_log('bash', :in => script)
96
+ Resource.set_software_env(software_dir)
97
+ end
98
+
99
+ def self.set_software_env(software_dir = Path.setup('software'))
100
+ software_dir.opt.find_all.collect{|d| d.annotate(File.dirname(d)) }.reverse.each do |software_dir|
101
+ next unless software_dir.exists?
102
+ Log.medium "Preparing software env at #{software_dir}"
103
+
104
+ software_dir = File.expand_path(software_dir)
105
+ opt_dir = File.join(software_dir, 'opt')
106
+ bin_dir = File.join(opt_dir, 'bin')
107
+
108
+ Misc.env_add 'PATH', bin_dir
109
+
110
+ FileUtils.mkdir_p opt_dir unless File.exist? opt_dir
111
+
112
+ %w(.ld-paths .c-paths .pkgconfig-paths .aclocal-paths .java-classpaths).each do |file|
113
+ filename = File.join(opt_dir, file)
114
+ begin
115
+ FileUtils.touch filename unless File.exist? filename
116
+ rescue
117
+ Log.warn("Could not touch #{ filename }")
118
+ end
119
+ end
120
+
121
+ Open.read(File.join opt_dir, '.c-paths').split(/\n/).each do |line|
122
+ dir = line.chomp
123
+ dir = File.join(opt_dir, dir) unless dir[0] == "/"
124
+ Misc.env_add('CPLUS_INCLUDE_PATH',dir)
125
+ Misc.env_add('C_INCLUDE_PATH',dir)
126
+ end if File.exist? File.join(opt_dir, '.c-paths')
127
+
128
+ Open.read(File.join opt_dir, '.ld-paths').split(/\n/).each do |line|
129
+ dir = line.chomp
130
+ dir = File.join(opt_dir, dir) unless dir[0] == "/"
131
+ Misc.env_add('LIBRARY_PATH',dir)
132
+ Misc.env_add('LD_LIBRARY_PATH',dir)
133
+ Misc.env_add('LD_RUN_PATH',dir)
134
+ end if File.exist? File.join(opt_dir, '.ld-paths')
135
+
136
+ Open.read(File.join opt_dir, '.pkgconfig-paths').split(/\n/).each do |line|
137
+ dir = line.chomp
138
+ dir = File.join(opt_dir, dir) unless dir[0] == "/"
139
+ Misc.env_add('PKG_CONFIG_PATH',dir)
140
+ end if File.exist? File.join(opt_dir, '.pkgconfig-paths')
141
+
142
+ Open.read(File.join opt_dir, '.aclocal-paths').split(/\n/).each do |line|
143
+ dir = line.chomp
144
+ dir = File.join(opt_dir, dir) unless dir[0] == "/"
145
+ Misc.env_add('ACLOCAL_FLAGS', "-I #{dir}", ' ')
146
+ end if File.exist? File.join(opt_dir, '.aclocal-paths')
147
+
148
+ Open.read(File.join opt_dir, '.java-classpaths').split(/\n/).each do |line|
149
+ dir = line.chomp
150
+ dir = File.join(opt_dir, dir) unless dir[0] == "/"
151
+ Misc.env_add('CLASSPATH', "#{dir}")
152
+ end if File.exist? File.join(opt_dir, '.java-classpaths')
153
+
154
+ Dir.glob(File.join opt_dir, 'jars', '*.jar').each do |file|
155
+ Misc.env_add('CLASSPATH', "#{file}")
156
+ end
157
+
158
+ if File.exist?(File.join(opt_dir, '.post_install')) and File.directory?(File.join(opt_dir, '.post_install'))
159
+ Dir.glob(File.join(opt_dir, '.post_install','*')).each do |file|
160
+
161
+ # Load exports
162
+ Open.read(file).split("\n").each do |line|
163
+ next unless line =~ /^\s*export\s+([^=]+)=(.*)/
164
+ var = $1.strip
165
+ value = $2.strip
166
+ value.sub!(/^['"]/,'')
167
+ value.sub!(/['"]$/,'')
168
+ value.gsub!(/\$[a-z_0-9]+/i){|var| ENV[var[1..-1]] }
169
+ Log.debug "Set variable export from .post_install: #{Log.fingerprint [var,value]*"="}"
170
+ ENV[var] = value
171
+ end
172
+ end
173
+ end
174
+ end
175
+ end
176
+
177
+ self.set_software_env
178
+ end
@@ -0,0 +1,59 @@
1
+ module Resource
2
+ def identify(path)
3
+ return path unless path.start_with?("/")
4
+ path_maps = path.path_maps if Path === path
5
+ path_maps ||= self.path_maps || Path.path_maps
6
+ path = File.expand_path(path)
7
+ path += "/" if File.directory?(path)
8
+
9
+ map_order ||= (path_maps.keys & Path.basic_map_order) + (path_maps.keys - Path.basic_map_order)
10
+ map_order -= [:current, "current"]
11
+
12
+ choices = []
13
+ map_order.uniq.each do |name|
14
+ pattern = path_maps[name]
15
+ pattern = path_maps[pattern] while Symbol === pattern
16
+ next if pattern.nil?
17
+
18
+ pattern = pattern.sub('{PWD}', Dir.pwd)
19
+ pattern = pattern.sub('{HOME}', ENV["HOME"])
20
+ if String === pattern and pattern.include?('{')
21
+ regexp = "^" + pattern
22
+ .gsub(/{(TOPLEVEL)}/,'(?<\1>[^/]+)')
23
+ .gsub(/{([^}]+)}/,'(?<\1>[^/]+)?') +
24
+ "(?:/(?<REST>.*))?/?$"
25
+ if m = path.match(regexp)
26
+ if ! m.named_captures.include?("PKGDIR") || m["PKGDIR"] == self.pkgdir
27
+ unlocated = %w(TOPLEVEL SUBPATH PATH REST).collect{|c|
28
+ m.named_captures.include?(c) ? m[c] : nil
29
+ }.compact * "/"
30
+ unlocated.gsub!(/\/+/,'/')
31
+ if self.subdir && ! self.subdir.empty?
32
+ subdir = self.subdir
33
+ subdir += "/" unless subdir.end_with?("/")
34
+ unlocated[subdir] = ""
35
+ end
36
+ choices << self.annotate(unlocated)
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ identified = choices.sort_by{|s| s.length }.first
43
+
44
+ Path.setup(identified || path, self, nil, path_maps)
45
+ end
46
+
47
+ def self.identify(path)
48
+ resource = path.pkgdir if Path === path
49
+ resource = Scout unless Resource === resource
50
+ unlocated = resource.identify path
51
+ end
52
+
53
+ def self.relocate(path)
54
+ return path if Open.exists?(path)
55
+ unlocated = identify(path)
56
+ unlocated.find
57
+ end
58
+ end
59
+
@@ -0,0 +1,40 @@
1
+ require_relative 'log'
2
+ require_relative 'path'
3
+ require_relative 'resource/produce'
4
+ require_relative 'resource/path'
5
+ require_relative 'resource/open'
6
+ require_relative 'resource/util'
7
+ require_relative 'resource/software'
8
+
9
+ module Resource
10
+ extend MetaExtension
11
+ extension_attr :pkgdir, :libdir, :subdir, :resources, :rake_dirs, :path_maps, :lock_dir
12
+
13
+ def self.default_lock_dir
14
+ Path.setup('tmp/produce_locks').find
15
+ end
16
+
17
+ def subdir
18
+ @subdir ||= ""
19
+ end
20
+
21
+ def lock_dir
22
+ @lock_dir ||= Resource.default_lock_dir
23
+ end
24
+
25
+ def pkgdir
26
+ @pkgdir ||= Path.default_pkgdir
27
+ end
28
+
29
+ def root
30
+ Path.setup(subdir, self, self.libdir, @path_maps)
31
+ end
32
+
33
+ def method_missing(name, prev = nil, *args)
34
+ if prev.nil?
35
+ root.send(name, *args)
36
+ else
37
+ root.send(name, prev, *args)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,54 @@
1
+ module SOPT
2
+ class << self
3
+ attr_writer :inputs, :input_shortcuts, :input_types, :input_descriptions, :input_defaults
4
+ end
5
+
6
+ def self.all
7
+ @all ||= {}
8
+ end
9
+
10
+ def self.shortcuts
11
+ @shortcuts ||= {}
12
+ end
13
+
14
+ def self.inputs
15
+ @inputs ||= []
16
+ end
17
+
18
+ def self.input_shortcuts
19
+ @input_shortcuts ||= {}
20
+ end
21
+
22
+ def self.input_types
23
+ @input_types ||= {}
24
+ end
25
+
26
+ def self.input_descriptions
27
+ @input_descriptions ||= {}
28
+ end
29
+
30
+ def self.input_defaults
31
+ @input_defaults ||= {}
32
+ end
33
+
34
+ def self.reset
35
+ @shortcuts = {}
36
+ @all = {}
37
+ end
38
+
39
+ def self.delete_inputs(inputs)
40
+ inputs.each do |input|
41
+ input = input.to_s
42
+ self.shortcuts.delete self.input_shortcuts.delete(input)
43
+ self.inputs.delete input
44
+ self.input_types.delete input
45
+ self.input_defaults.delete input
46
+ self.input_descriptions.delete input
47
+ end
48
+ end
49
+
50
+ def self.usage
51
+ puts SOPT.doc
52
+ exit 0
53
+ end
54
+ end
@@ -0,0 +1,126 @@
1
+ require_relative '../log'
2
+ module SOPT
3
+
4
+ class << self
5
+ attr_writer :command, :summary, :synopsys, :description
6
+ end
7
+
8
+ def self.command
9
+ @command ||= File.basename($0)
10
+ end
11
+
12
+ def self.summary
13
+ @summary ||= ""
14
+ end
15
+
16
+ def self.synopsys
17
+ @synopsys ||= begin
18
+ "#{command} " <<
19
+ inputs.collect{|name|
20
+ "[" << input_format(name, input_types[name] || :string, input_defaults[name], input_shortcuts[name]).sub(/:$/,'') << "]"
21
+ } * " "
22
+ end
23
+ end
24
+
25
+ def self.description
26
+ @description ||= "Missing"
27
+ end
28
+
29
+ def self.input_format(name, type = nil, default = nil, short = nil)
30
+ input_str = (short.nil? or short.empty?) ? "--#{name}" : "-#{short},--#{name}"
31
+ input_str = Log.color(:blue, input_str)
32
+ extra = case type
33
+ when nil
34
+ ""
35
+ when :boolean
36
+ "[=false]"
37
+ when :tsv, :text
38
+ "=<file|->"
39
+ when :array
40
+ "=<list|file|->"
41
+ else
42
+ "=<#{ type }>"
43
+ end
44
+ #extra << " (default: #{Array === default ? (default.length > 3 ? default[0..2]*", " + ', ...' : default*", " ): default})" if default != nil
45
+ extra << " (default: #{Log.fingerprint(default)})" if default != nil
46
+ input_str << Log.color(:green, extra)
47
+ end
48
+
49
+ def self.input_array_doc(input_array)
50
+ input_array.collect do |name,type,description,default,options|
51
+ type = :string if type.nil?
52
+
53
+ name = name.to_s
54
+ shortcut, options = options, nil if String === options || Symbol === options
55
+
56
+ case options && options[:shortcut]
57
+ when FalseClass
58
+ shortcut = nil
59
+ when TrueClass, nil
60
+ shortcut = fix_shortcut(name[0], name)
61
+ else
62
+ shortcut = options[:shortcut]
63
+ end unless shortcut
64
+
65
+ shortcut = fix_shortcut(shortcut, name)
66
+ register(shortcut, name, type, description) unless self.inputs.include? name
67
+ name = SOPT.input_format(name, type.to_sym, default, shortcut )
68
+ Misc.format_definition_list_item(name, description)
69
+ end * "\n"
70
+ end
71
+
72
+ def self.input_doc(inputs, input_types = nil, input_descriptions = nil, input_defaults = nil, input_shortcuts = nil)
73
+ type = description = default = nil
74
+ shortcut = ""
75
+ seen = []
76
+ inputs.collect do |name|
77
+ next if seen.include? name
78
+ seen << name
79
+
80
+ type = input_types[name] unless input_types.nil?
81
+ description = input_descriptions[name] unless input_descriptions.nil?
82
+ default = input_defaults[name] unless input_defaults.nil?
83
+
84
+ name = name.to_s
85
+
86
+ case input_shortcuts
87
+ when nil, FalseClass
88
+ shortcut = nil
89
+ when Hash
90
+ shortcut = input_shortcuts[name]
91
+ when TrueClass
92
+ shortcut = fix_shortcut(name[0], name)
93
+ end
94
+
95
+ type = :string if type.nil?
96
+ register(shortcut, name, type, description) unless self.inputs.include? name
97
+
98
+ name = SOPT.input_format(name, type.to_sym, default, shortcut)
99
+ Misc.format_definition_list_item(name, description)
100
+ end * "\n"
101
+ end
102
+
103
+
104
+ def self.doc
105
+ doc =<<-EOF
106
+ #{Log.color :magenta}#{command}(1) -- #{summary}
107
+ #{"=" * (command.length + summary.length + 7)}#{Log.color :reset}
108
+
109
+ EOF
110
+
111
+ if synopsys and not synopsys.empty?
112
+ doc << Log.color(:magenta, "## SYNOPSYS") << "\n\n"
113
+ doc << Log.color(:blue, synopsys) << "\n\n"
114
+ end
115
+
116
+ if description and not description.empty?
117
+ doc << Log.color(:magenta, "## DESCRIPTION") << "\n\n"
118
+ doc << Misc.format_paragraph(description) << "\n\n"
119
+ end
120
+
121
+ doc << Log.color(:magenta, "## OPTIONS") << "\n\n"
122
+ doc << input_doc(inputs, input_types, input_descriptions, input_defaults, input_shortcuts)
123
+
124
+ doc
125
+ end
126
+ end
@@ -0,0 +1,57 @@
1
+ module SOPT
2
+ GOT_OPTIONS= IndiferentHash.setup({})
3
+ def self.current_options=(options)
4
+ @@current_options = options
5
+ end
6
+ def self.consume(args = ARGV)
7
+ i = 0
8
+ @@current_options ||= {}
9
+ while i < args.length do
10
+ current = args[i]
11
+ break if current == "--"
12
+ if m = current.match(/--?(.+?)(?:=(.+))?$/)
13
+ key = $1
14
+ value = $2
15
+
16
+ input = inputs.include?(key)? key : shortcuts[key]
17
+
18
+ if input.nil?
19
+ i += 1
20
+ next
21
+ else
22
+ args.delete_at i
23
+ end
24
+ else
25
+ i += 1
26
+ next
27
+ end
28
+
29
+ if input_types[input] == :string
30
+ value = args.delete_at(i) if value.nil?
31
+ @@current_options[input] = value
32
+ else
33
+ if value.nil? and %w(F false FALSE no).include?(args[i])
34
+ Log.warn "Boolean values are best specified as #{current}=[true|false], not #{ current } [true|false]. Token '#{args[i]}' following '#{current}' automatically assigned as value"
35
+ value = args.delete_at(i)
36
+ end
37
+ @@current_options[input] = %w(F false FALSE no).include?(value)? false : true
38
+ end
39
+ end
40
+
41
+ IndiferentHash.setup @@current_options
42
+ GOT_OPTIONS.merge! @@current_options
43
+
44
+ @@current_options
45
+ end
46
+
47
+ def self.get(opt_str)
48
+ SOPT.parse(opt_str)
49
+ SOPT.consume(ARGV)
50
+ end
51
+
52
+ def self.require(options, *parameters)
53
+ parameters.flatten.each do |parameter|
54
+ raise ParameterException, "Parameter '#{ Log.color :blue, parameter }' not given" if options[parameter].nil?
55
+ end
56
+ end
57
+ end