basechip 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/.document +5 -0
  2. data/Gemfile +20 -0
  3. data/Gemfile.lock +32 -0
  4. data/LICENSE.txt +14 -0
  5. data/README.rdoc +19 -0
  6. data/VERSION +1 -0
  7. data/bin/basechip +25 -0
  8. data/collateral/block/block.rb.erb +20 -0
  9. data/collateral/configuration/configuration.rb.erb +19 -0
  10. data/collateral/project/Gemfile +19 -0
  11. data/collateral/project/gitignore +6 -0
  12. data/collateral/project/project.rb.erb +76 -0
  13. data/collateral/project/settings.rb.erb +26 -0
  14. data/collateral/recipes/icarus.rb +73 -0
  15. data/collateral/recipes/icarus.rb.erb +88 -0
  16. data/collateral/recipes/local_cluster.rb +27 -0
  17. data/lib/array.rb +26 -0
  18. data/lib/base_chip/action.rb +309 -0
  19. data/lib/base_chip/base.rb +105 -0
  20. data/lib/base_chip/block.rb +75 -0
  21. data/lib/base_chip/bom.rb +75 -0
  22. data/lib/base_chip/bom_file.rb +45 -0
  23. data/lib/base_chip/cli.rb +371 -0
  24. data/lib/base_chip/cluster.rb +93 -0
  25. data/lib/base_chip/cluster_type.rb +45 -0
  26. data/lib/base_chip/code_area.rb +30 -0
  27. data/lib/base_chip/configuration.rb +257 -0
  28. data/lib/base_chip/dsl.rb +593 -0
  29. data/lib/base_chip/exit_error.rb +20 -0
  30. data/lib/base_chip/generator_menu.rb +64 -0
  31. data/lib/base_chip/git.rb +32 -0
  32. data/lib/base_chip/hierarchy.rb +84 -0
  33. data/lib/base_chip/install_menu.rb +89 -0
  34. data/lib/base_chip/ipc.rb +50 -0
  35. data/lib/base_chip/list_menu.rb +134 -0
  36. data/lib/base_chip/menu.rb +70 -0
  37. data/lib/base_chip/out_file.rb +68 -0
  38. data/lib/base_chip/permutation.rb +26 -0
  39. data/lib/base_chip/permute.rb +25 -0
  40. data/lib/base_chip/post.rb +26 -0
  41. data/lib/base_chip/problem.rb +33 -0
  42. data/lib/base_chip/project.rb +472 -0
  43. data/lib/base_chip/requirement.rb +26 -0
  44. data/lib/base_chip/runable.rb +36 -0
  45. data/lib/base_chip/source_language.rb +40 -0
  46. data/lib/base_chip/source_type.rb +24 -0
  47. data/lib/base_chip/statistic.rb +27 -0
  48. data/lib/base_chip/task.rb +21 -0
  49. data/lib/base_chip/task_master.rb +50 -0
  50. data/lib/base_chip/taskable.rb +77 -0
  51. data/lib/base_chip/tasker.rb +260 -0
  52. data/lib/base_chip/test.rb +202 -0
  53. data/lib/base_chip/test_list.rb +120 -0
  54. data/lib/base_chip/tool.rb +51 -0
  55. data/lib/base_chip/tool_version.rb +34 -0
  56. data/lib/base_chip/top_menu.rb +203 -0
  57. data/lib/base_chip/track_state.rb +61 -0
  58. data/lib/base_chip.rb +215 -0
  59. data/lib/dir.rb +30 -0
  60. data/lib/reporting.rb +97 -0
  61. metadata +215 -0
@@ -0,0 +1,309 @@
1
+ # Copyright 2011 Tommy Poulter
2
+ #
3
+ # This file is part of basechip.
4
+ #
5
+ # basechip is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License version 3 as
7
+ # published by the Free Software Foundation.
8
+ #
9
+ # basechip is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with basechip. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ require 'zlib'
18
+ require 'base_chip/runable'
19
+ require 'base_chip/statistic'
20
+ require 'base_chip/out_file'
21
+
22
+ module BaseChip
23
+ class Action
24
+ include Taskable
25
+ include TrackState
26
+ include Runable
27
+ include Dsl
28
+ include Base
29
+
30
+ define_settings({:zip_failing_action => [ Boolean ],
31
+ :zip_passing_action => [ Boolean ],
32
+ :clean_passing_action => [ Boolean ],
33
+ :run_action_in_tmp => [ Boolean ],
34
+ :depends => [ String , Array, Symbol ],
35
+ :before => [ Proc ],
36
+ :after => [ Proc ],
37
+ :environment => [ Hash ],
38
+ :command => [ String ],
39
+ :command_prefix => [ String ],
40
+ :command_suffix => [ String ],
41
+ :pre_commands => [ Array ],
42
+ :post_commands => [ Array ],
43
+ :name_regex => [ Regexp ],
44
+ :seed_options => [ String , Array ],
45
+ :args => [ String ],
46
+ :log_to => [ String ],
47
+ :stdout_to => [ String ],
48
+ :stderr_to => [ String ],
49
+ :echo_stdin => [ Boolean ],
50
+ :needs_clobber => [ Boolean ],
51
+ :clean_proc => [ Proc ]})
52
+
53
+ define_children({:out_file => OutFile })
54
+
55
+ def configure
56
+ return if @configured
57
+ unless @test
58
+ @bundle_name ||= if BaseChip.options.random_bundle_names
59
+ random_string
60
+ else
61
+ @name.to_s
62
+ end
63
+ @directory ||= @configuration.out_directory + "/#{@bundle_name}"
64
+ end
65
+ super
66
+ end
67
+ def run
68
+ if @run_action_in_tmp
69
+ bundle_name = random_string
70
+ run_directory = "/tmp/#{bundle_name}"
71
+ else
72
+ bundle_name = @bundle_name
73
+ run_directory = @directory
74
+ end
75
+ FileUtils.rm_rf run_directory
76
+ FileUtils.mkdir_p run_directory
77
+ Dir.pushd run_directory
78
+
79
+ File.open('seed','w') do |f|
80
+ f.write random_generator.seed
81
+ end
82
+ File.open('rerun.yaml','w') do |f|
83
+ f.write rerun_data.to_yaml
84
+ end
85
+
86
+ # Where the magic happens
87
+ inner_run
88
+
89
+ FileUtils.touch "#{run_directory}/.fail_detected" unless self.pass?
90
+
91
+ Dir.chdir '..'
92
+ if (@clean_passing_action && pass? && !@foreground)
93
+ FileUtils.rm_rf run_directory
94
+ else
95
+ bundle_tgz = nil
96
+ if @zip_failing_action || (@zip_passing_action && pass?)
97
+ bundle_tgz = run_directory + ".tgz"
98
+ @bundle_tgz = @directory + ".tgz"
99
+ tgz = Zlib::GzipWriter.new(File.open(bundle_tgz, 'wb'))
100
+ Archive::Tar::Minitar.pack(bundle_name, tgz)
101
+ FileUtils.rm_rf bundle_name
102
+ end
103
+ if @run_action_in_tmp
104
+ FileUtils.mkdir_p "#{@directory}/.."
105
+ FileUtils.mv((bundle_tgz || run_directory), "#{@directory}/..")
106
+ end
107
+ end
108
+
109
+ puts "Debug information here: #{@bundle_tgz || @directory}" if @foreground
110
+
111
+ # p @totals if @foreground
112
+ Dir.popd
113
+ end
114
+ def inner_run
115
+ # puts "#{full_name} started"
116
+ script_file = "#{@name}.bash"
117
+ script = File.open(script_file,'w')
118
+ script.puts "#!/bin/bash"
119
+ # $environment.each do |n,v|
120
+ # script.puts "export #{n}=#{v}"
121
+ # end
122
+ if @environment
123
+ @environment.each do |n,v|
124
+ script.puts "export #{n}=#{v}"
125
+ end
126
+ end
127
+
128
+ script.puts "NAME=#{@name}"
129
+ script.puts "BLOCK=#{@block.name}"
130
+ if @args
131
+ script.puts "ARGS='#{@args.gsub(/'/,"\\'")}'"
132
+ end
133
+
134
+ # FIXME fault if commands and command both exist
135
+ if @commands
136
+ script.puts @commands
137
+ elsif @command
138
+ script.puts @pre_commands if @pre_commands
139
+ script.puts "#{@command_prefix} #{@command} #{@command_suffix} $*"
140
+ script.puts @post_commands if @post_commands
141
+ else
142
+ fault "Action #{@name} has no command(s) specified to run"
143
+ end
144
+
145
+ script.chmod 0777
146
+ script.close
147
+ process_status = nil
148
+ errmsg = ''
149
+
150
+ STDOUT.sync = true
151
+ STDERR.sync = true
152
+
153
+ @before.call if @before
154
+
155
+ script_call = (@foreground || @log_to || @stdout_to) ? script.path : "#{script.path} > /dev/null"
156
+
157
+ log_storage = ( @log_to ) ? File.open( @log_to, 'w') : nil
158
+ stdout_storage = (@stdout_to && @stdout_to != @log_to) ? File.open(@stdout_to, 'w') : nil
159
+ stderr_storage = (@stderr_to && @stderr_to != @log_to) ? File.open(@stderr_to, 'w') : nil
160
+
161
+ # stdin, stdout, stderr, wait_thr = Open3.popen3([env,] cmd... [, opts])
162
+ Open3.popen3(script_call) do |stdin, stdout, stderr, wait_thr|
163
+
164
+ to = Thread.new(stdout) do |pipe|
165
+ out_data = nil
166
+ loop do
167
+ begin
168
+ out_data = pipe.readpartial(4096)
169
+ rescue EOFError, IOError
170
+ break
171
+ end
172
+ STDOUT.print out_data if @foreground
173
+ log_storage.print out_data if log_storage
174
+ stderr_storage.print out_data if stderr_storage
175
+ end
176
+ end
177
+ ti = Thread.new(STDIN) do |pipe|
178
+ in_data = nil
179
+ loop do
180
+ begin
181
+ in_data = pipe.readpartial(4096)
182
+ rescue EOFError, IOError
183
+ break
184
+ end
185
+ stdin.print in_data if @foreground && !stdin.closed?
186
+ if @echo_stdin
187
+ STDOUT.print in_data if @foreground
188
+ log_storage.print in_data if log_storage
189
+ stderr_storage.print in_data if stdout_storage
190
+ end
191
+ end
192
+ end
193
+ te = Thread.new(stderr) do |pipe|
194
+ err_data = nil
195
+ loop do
196
+ begin
197
+ err_data = pipe.readpartial(4096)
198
+ rescue EOFError, IOError
199
+ break
200
+ end
201
+ errmsg += err_data
202
+ STDERR.print err_data if @foreground
203
+ log_storage.print err_data if log_storage
204
+ stderr_storage.print err_data if stderr_storage
205
+ end
206
+ end
207
+
208
+ begin
209
+ wait_thr.join
210
+ if @foreground
211
+ ti.kill
212
+ to.kill unless te.join(10)
213
+ end
214
+ te .kill unless te.join(10)
215
+ rescue Interrupt
216
+ if @foreground # && @int_for_shell
217
+ Process.kill 'INT', wait_thr.pid
218
+ retry
219
+ else
220
+ raise
221
+ end
222
+ end
223
+ process_status = wait_thr.value # Process::Status object returned.
224
+ end
225
+
226
+ log_storage.close if log_storage
227
+ stdout_storage.close if stdout_storage
228
+ stderr_storage.close if stderr_storage
229
+
230
+ @after.call if @after
231
+
232
+ statistics = {}
233
+ totals = {}
234
+ if @out_files
235
+ @out_files.each_value do |f|
236
+ f.go(@name)
237
+
238
+ take_state(f)
239
+
240
+ statistics.merge! f.statistics if f.statistics
241
+ totals .merge! f.totals if f.totals
242
+
243
+ # warning!(pf,ps) if f.warning?
244
+ # error!( pf,ps) if f.error?
245
+ # fault!( pf,ps) if f.fault?
246
+ end
247
+ end
248
+ error!(nil, errmsg) if !process_status.success? || errmsg.size > 0
249
+
250
+ @state ||= 'pass'
251
+ @state = 'fail' unless self.pass? # state is error, warning or fault up to this point
252
+
253
+ # puts "log file produced #{totals}"
254
+ totals.each do |k,v|
255
+ if statistics[k].for_states.include? @state
256
+ if statistics[k].across_states
257
+ @totals[k] = v
258
+ end
259
+ if statistics[k].per_state
260
+ @totals["#{@state}_#{k}"] = v
261
+ end
262
+ # FIXME error_if_missing
263
+ end
264
+ end
265
+ # puts "we collected #{@totals}"
266
+
267
+ unless pass?
268
+ @problem.bundle=(@bundle_tgz || @directory)
269
+ # error(@problem.to_s)
270
+ end
271
+ # puts "#{full_name} ended"
272
+ end
273
+
274
+ def clean
275
+ return if @needs_clobber
276
+ clobber
277
+ end
278
+ def clobber
279
+ @clean_proc.call(self) if @clean_proc
280
+ FileUtils.rm_rf @directory
281
+ FileUtils.rm_rf "#{@directory}.tgz"
282
+ end
283
+
284
+ attr_writer :random_generator
285
+ def random_generator
286
+ @random_generator ||= BaseChip.new_random_generator
287
+ end
288
+ def generate_seed(format, bits=32)
289
+ case format
290
+ when :num
291
+ @random_generator.rand(2 ** bits)
292
+ when :hex
293
+ (@random_generator.rand(2 ** bits)).to_hex
294
+ when :dec
295
+ (@random_generator.rand(2 ** bits)).to_s
296
+ when :alphanumeric
297
+ random_string(bits)
298
+ end
299
+ end
300
+
301
+ def rerun_data
302
+ { 'full_name' => full_name ,
303
+ 'seed' => random_generator.seed ,
304
+ 'modes' => modes ,
305
+ 'append_arguments' => BaseChip.options. append_arguments ,
306
+ 'replace_arguments' => BaseChip.options.replace_arguments }
307
+ end
308
+ end
309
+ end
@@ -0,0 +1,105 @@
1
+ # Copyright 2011 Tommy Poulter
2
+ #
3
+ # This file is part of basechip.
4
+ #
5
+ # basechip is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License version 3 as
7
+ # published by the Free Software Foundation.
8
+ #
9
+ # basechip is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with basechip. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ require 'reporting'
18
+ module BaseChip
19
+ module Base
20
+ def self.included mod
21
+ mod.extend Base::ClassMethods
22
+ mod.class_eval do
23
+ include Reporting
24
+ include Base::InstanceMethods
25
+
26
+ define_setting :directory, [ String ]
27
+
28
+ # chain together projects, blocks, and actions
29
+ attr_reader :project
30
+ attr_reader :block
31
+ attr_reader :configuration
32
+ attr_reader :action
33
+ attr_reader :test_list
34
+ attr_reader :test
35
+ attr_reader :tool
36
+ attr_reader :version
37
+ attr_reader :source_type
38
+ attr_reader :source_language
39
+ end
40
+ end
41
+ module ClassMethods
42
+ end
43
+ module InstanceMethods
44
+ def parent=(parent); @parent = parent; update_chaining; end
45
+ def foster=(parent); @foster = parent; update_chaining; end
46
+ def update_chaining
47
+ # chain blocks, projects, and actions together
48
+ @project ||= (self.is_a? BaseChip::Project ) ? self : ((parent.project if parent) || (foster.project if foster))
49
+ @block ||= (self.is_a? BaseChip::Block ) ? self : ((parent.block if parent) || (foster.block if foster))
50
+ @configuration ||= (self.is_a? BaseChip::Configuration ) ? self : ((parent.configuration if parent) || (foster.configuration if foster))
51
+ @action ||= (self.is_a? BaseChip::Action ) ? self : ((parent.action if parent) || (foster.action if foster))
52
+ @test_list ||= (self.is_a? BaseChip::TestList ) ? self : ((parent.test_list if parent) || (foster.test_list if foster))
53
+ @test ||= (self.is_a? BaseChip::Test ) ? self : ((parent.test if parent) || (foster.test if foster))
54
+ @tool ||= (self.is_a? BaseChip::Tool ) ? self : ((parent.tool if parent) || (foster.tool if foster))
55
+ @tool_version ||= (self.is_a? BaseChip::ToolVersion ) ? self : ((parent.tool_version if parent) || (foster.tool_version if foster))
56
+ @source_type ||= (self.is_a? BaseChip::SourceType ) ? self : ((parent.source_type if parent) || (foster.source_type if foster))
57
+ @source_language ||= (self.is_a? BaseChip::SourceLanguage) ? self : ((parent.source_language if parent) || (foster.source_language if foster))
58
+ end
59
+
60
+ def configure
61
+ return if @configured
62
+ super
63
+ # if self.class.children_types.keys.include?(:tool) && self.tools
64
+ # self.tools.each_value do |t|
65
+ # t.configure
66
+ # t.selected_version.configure
67
+ # t.selected_version.actions.each do |n,a|
68
+ # self.actions ||= {}
69
+ # if self.actions[n]
70
+ # self.actions[n].blks = a.blks + self.actions[n].blks
71
+ # self.actions[n].foster ||= a.parent
72
+ # else
73
+ # self.actions[n] = a.clone
74
+ # self.actions[n].blks = self.actions[n].blks.dup
75
+ # self.actions[n].foster = a.parent
76
+ # self.actions[n].parent = self
77
+ # end
78
+ # end
79
+ # end
80
+ # end
81
+ end
82
+
83
+ def discover_configurations
84
+ Dir.glob("#{BaseChip.root}/*/base_chip/configuration.rb").each do |f|
85
+ if f =~ /(\w+)\/base_chip\/configuration\.rb$/
86
+ self.block($1.to_sym) {|b| b.file f}
87
+ end
88
+ end
89
+ end
90
+ def file_glob(glob, regex = nil, type = nil)
91
+ Dir.glob(glob).each do |f|
92
+ if regex
93
+ if f =~ regex
94
+ self.send(type, $1.to_sym) { |t| t.file(f) }
95
+ else
96
+ fault "fileglob '#{glob}' did not match regex /#{regex}/ in #{self.name}"
97
+ end
98
+ else
99
+ self.file(f)
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,75 @@
1
+ # Copyright 2011 Tommy Poulter
2
+ #
3
+ # This file is part of basechip.
4
+ #
5
+ # basechip is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License version 3 as
7
+ # published by the Free Software Foundation.
8
+ #
9
+ # basechip is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with basechip. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ require 'base_chip/configuration'
18
+
19
+ module BaseChip
20
+ class Block
21
+ include Dsl
22
+ include Base
23
+ include Hierarchy
24
+ define_children({:configuration => Configuration})
25
+
26
+ def configuration_dereference(name,names,passive)
27
+ if configuration = @configurations[name]
28
+ configuration.configure
29
+ configuration.dereference_workload(names,passive)
30
+ elsif passive
31
+ []
32
+ else
33
+ fault "Could not find configuration #{name} in block #{@name}" # FIXME say who wanted it, and if shortcut occurred
34
+ end
35
+ end
36
+ def dereference_workload(names = nil,passive=false)
37
+ configure
38
+ fault "No configurations specified for block #{@name}" unless @configurations # || passive || names.nil?
39
+ names ||= ['all']
40
+ out = Array.new
41
+ names.each do |n|
42
+ n = n.to_s
43
+ case n
44
+ when 'all', 'gate', 'upgate', 'downgate', 'diffgate' # FIXME , 'alltests', 'allactions'
45
+ next unless @configurations
46
+ @configurations.keys.each { |k| out += configuration_dereference(k,[n],true) }
47
+ when /^all:(.*)$/
48
+ next unless @configurations
49
+ tmp = $1
50
+ @configurations.keys.each { |k| out += configuration_dereference(k,[tmp],true) }
51
+ when /^([^:]*?):(.*)$/
52
+ out += configuration_dereference($1.to_sym,[$2],passive)
53
+ else
54
+ fault "No configurations specified for block #{@name}" unless @configurations || passive
55
+ if @configurations[:default]
56
+ out += configuration_dereference(:default,[n],passive)
57
+ elsif @configurations.size == 1
58
+ out += configuration_dereference(@configurations.keys[0],[n],passive)
59
+ end
60
+ end
61
+ end
62
+ out.uniq!
63
+ out
64
+ end
65
+
66
+ def shortcut(name, array)
67
+ @shortcuts ||= {}
68
+ @shortcuts[name] = array
69
+ end
70
+
71
+ def discover_configurations ; file_glob("#{@directory}/*/base_chip/configuration.rb" , /(\w+)\/base_chip\/configuration\.rb$/ , :configuration)
72
+ file_glob("#{@directory}/base_chip/configurations/*.rb", /base_chip\/configurations\/(\w+)\.rb$/, :configuration) end
73
+
74
+ end
75
+ end
@@ -0,0 +1,75 @@
1
+ # Copyright 2011 Tommy Poulter
2
+ #
3
+ # This file is part of basechip.
4
+ #
5
+ # basechip is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License version 3 as
7
+ # published by the Free Software Foundation.
8
+ #
9
+ # basechip is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with basechip. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ require 'base_chip/bom_file'
18
+
19
+ class Hash
20
+ def deep_values
21
+ self.values.map do |v|
22
+ if v.respond_to? :values
23
+ v.values
24
+ else
25
+ v
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ module BaseChip
32
+ class Bom
33
+ attr_reader :hash
34
+ attr_reader :name1
35
+ attr_reader :name2
36
+ def initialize(hash)
37
+ @hash = hash.with_indifferent_access
38
+ @name1 = 'all'
39
+ @name2 = 'all'
40
+ end
41
+ def verilog ; @verilog ||= trim(2,:verilog ) end
42
+ def classes ; @classes ||= trim(2,:classes ) end
43
+ def vhdl ; @vhdl ||= trim(2,:vhdl ) end
44
+ def systemc ; @systemc ||= trim(2,:systemc ) end
45
+ def headers ; @headers ||= trim(2,:headers ) end
46
+
47
+ def rtl ; @rtl ||= trim(1,:rtl ) end
48
+ def gates ; @gates ||= trim(1,:gates ) end
49
+ def testbench ; @testbench ||= trim(1,:testbench) end
50
+ def stimulus ; @stimulus ||= trim(1,:stimulus ) end
51
+
52
+ def to_a ; @to_a ||= @hash.deep_values.flatten; end
53
+ def list ; @list ||= to_a.join(' ') ; end
54
+ def to_s ; @to_s ||= list ; end
55
+
56
+ def +(bom)
57
+ Bom.new(@hash.merge bom.hash)
58
+ end
59
+
60
+ protected
61
+ def trim(depth,key)
62
+ hash = HashWithIndifferentAccess.new
63
+ @hash.each do |k1,l1|
64
+ case depth
65
+ when 1; hash[k1] = l1 if k1.to_s == key.to_s
66
+ when 2; hash[k1] = tmp = HashWithIndifferentAccess.new
67
+ l1.each do |k2,l2|
68
+ tmp[k2] = l2 if k2.to_s == key.to_s
69
+ end
70
+ end
71
+ end
72
+ Bom.new(hash)
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,45 @@
1
+ # Copyright 2011 Tommy Poulter
2
+ #
3
+ # This file is part of basechip.
4
+ #
5
+ # basechip is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License version 3 as
7
+ # published by the Free Software Foundation.
8
+ #
9
+ # basechip is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with basechip. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ module BaseChip
18
+ class BomType
19
+ def initialize(bom,type)
20
+ @bom = bom
21
+ @type = type
22
+ end
23
+ def to_s
24
+ "#{@bom.name1}.#{@bom.name2}.#{@type}"
25
+ end
26
+ def make
27
+ f = File.open(to_s,'w')
28
+ case @type
29
+ when :text ; f.puts @bom.to_a.join("\n")
30
+ when :tcl ; fault "TCL formatted bom files not yet implemented"
31
+ else ; fault "#{@type.inspect} was not understood by #{self.class}"
32
+ end
33
+ f.close
34
+ end
35
+ end
36
+
37
+ class Bom
38
+ def text
39
+ @text ||= BomType.new(self,:text)
40
+ end
41
+ def tcl
42
+ @tcl ||= BomType.new(self,:tcl )
43
+ end
44
+ end
45
+ end