origen 0.34.3 → 0.52.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (207) hide show
  1. checksums.yaml +5 -5
  2. data/bin/origen +1 -231
  3. data/config/application.rb +12 -3
  4. data/config/boot.rb +2 -7
  5. data/config/commands.rb +3 -74
  6. data/config/rubocop/easy_disabled.yml +4 -0
  7. data/config/rubocop/easy_enabled.yml +0 -4
  8. data/config/rubocop/strict_disabled.yml +4 -0
  9. data/config/rubocop/strict_enabled.yml +0 -4
  10. data/config/version.rb +2 -3
  11. data/lib/origen.rb +27 -14
  12. data/lib/origen/application.rb +88 -2
  13. data/lib/origen/application/deployer.rb +3 -1
  14. data/lib/origen/application/release.rb +2 -2
  15. data/lib/origen/application/runner.rb +35 -20
  16. data/lib/origen/boot.rb +302 -0
  17. data/lib/origen/boot/api.rb +13 -0
  18. data/lib/origen/boot/app.rb +284 -0
  19. data/lib/origen/code_generators.rb +30 -10
  20. data/lib/origen/code_generators/actions.rb +244 -34
  21. data/lib/origen/code_generators/base.rb +9 -2
  22. data/lib/origen/code_generators/block.rb +203 -0
  23. data/lib/origen/code_generators/block_common.rb +100 -0
  24. data/lib/origen/code_generators/dut.rb +62 -0
  25. data/lib/origen/code_generators/feature.rb +50 -0
  26. data/lib/origen/code_generators/klass.rb +41 -0
  27. data/lib/origen/code_generators/model.rb +60 -0
  28. data/lib/origen/code_generators/module.rb +92 -0
  29. data/lib/origen/commands.rb +30 -13
  30. data/lib/origen/commands/archive.rb +175 -0
  31. data/lib/origen/commands/extract.rb +43 -0
  32. data/lib/origen/commands/generate.rb +1 -0
  33. data/lib/origen/commands/lint.rb +6 -1
  34. data/lib/origen/commands/new.rb +48 -24
  35. data/lib/origen/commands/new_resource.rb +41 -0
  36. data/lib/origen/commands/site.rb +52 -0
  37. data/lib/origen/commands/web.rb +11 -6
  38. data/lib/origen/commands_global.rb +9 -7
  39. data/lib/origen/core_ext/numeric.rb +20 -0
  40. data/lib/{option_parser → origen/core_ext/option_parser}/optparse.rb +0 -0
  41. data/lib/origen/dependencies.rb +0 -0
  42. data/lib/origen/file_handler.rb +18 -6
  43. data/lib/origen/generator.rb +19 -10
  44. data/lib/origen/generator/comparator.rb +2 -1
  45. data/lib/origen/generator/flow.rb +3 -1
  46. data/lib/origen/generator/job.rb +60 -16
  47. data/lib/origen/generator/pattern.rb +132 -72
  48. data/lib/origen/generator/pattern_finder.rb +3 -3
  49. data/lib/origen/generator/pattern_sequence.rb +201 -0
  50. data/lib/origen/generator/pattern_sequencer.rb +99 -0
  51. data/lib/origen/generator/pattern_thread.rb +175 -0
  52. data/lib/origen/loader.rb +381 -0
  53. data/lib/origen/log.rb +250 -108
  54. data/lib/origen/model.rb +22 -1
  55. data/lib/origen/model/exporter.rb +50 -10
  56. data/lib/origen/model_initializer.rb +5 -1
  57. data/lib/origen/operating_systems.rb +4 -0
  58. data/lib/origen/parameters.rb +96 -4
  59. data/lib/origen/parameters/set.rb +4 -3
  60. data/lib/origen/pins.rb +10 -8
  61. data/lib/origen/pins/pin.rb +61 -46
  62. data/lib/origen/ports/port.rb +5 -0
  63. data/lib/origen/registers.rb +5 -0
  64. data/lib/origen/registers/bit.rb +57 -53
  65. data/lib/origen/registers/bit_collection.rb +100 -43
  66. data/lib/origen/registers/msb0_delegator.rb +47 -0
  67. data/lib/origen/registers/reg.rb +114 -99
  68. data/lib/origen/revision_control.rb +1 -1
  69. data/lib/origen/revision_control/git.rb +23 -3
  70. data/lib/origen/site_config.rb +251 -60
  71. data/lib/origen/site_config/config.rb +217 -0
  72. data/lib/origen/sub_blocks.rb +106 -31
  73. data/lib/origen/top_level.rb +11 -0
  74. data/lib/origen/users/user.rb +3 -2
  75. data/lib/origen/utility/mailer.rb +42 -9
  76. data/lib/origen/value/bin_str_val.rb +1 -1
  77. data/lib/origen/value/hex_str_val.rb +1 -1
  78. data/lib/origen/version_string.rb +6 -1
  79. data/lib/tasks/gem.rake +6 -1
  80. data/origen_app_generators/Gemfile +19 -0
  81. data/origen_app_generators/Gemfile.lock +152 -0
  82. data/origen_app_generators/LICENSE +21 -0
  83. data/origen_app_generators/README.md +368 -0
  84. data/{templates/code_generators/rakefile.rb → origen_app_generators/Rakefile} +0 -0
  85. data/origen_app_generators/bin/boot.rb +39 -0
  86. data/origen_app_generators/config/application.rb +153 -0
  87. data/origen_app_generators/config/boot.rb +1 -0
  88. data/origen_app_generators/config/commands.rb +63 -0
  89. data/origen_app_generators/config/shared_commands.rb +177 -0
  90. data/origen_app_generators/config/version.rb +8 -0
  91. data/origen_app_generators/doc/history +223 -0
  92. data/origen_app_generators/lbin/bundle +105 -0
  93. data/origen_app_generators/lbin/byebug +29 -0
  94. data/origen_app_generators/lbin/coderay +29 -0
  95. data/origen_app_generators/lbin/htmldiff +29 -0
  96. data/origen_app_generators/lbin/httparty +29 -0
  97. data/origen_app_generators/lbin/httpclient +29 -0
  98. data/origen_app_generators/lbin/kramdown +29 -0
  99. data/origen_app_generators/lbin/ldiff +29 -0
  100. data/origen_app_generators/lbin/nanoc +29 -0
  101. data/origen_app_generators/lbin/nokogiri +29 -0
  102. data/origen_app_generators/lbin/origen +62 -0
  103. data/origen_app_generators/lbin/pry +29 -0
  104. data/origen_app_generators/lbin/rackup +29 -0
  105. data/origen_app_generators/lbin/rake +29 -0
  106. data/origen_app_generators/lbin/rspec +29 -0
  107. data/origen_app_generators/lbin/rubocop +29 -0
  108. data/origen_app_generators/lbin/ruby-parse +29 -0
  109. data/origen_app_generators/lbin/ruby-rewrite +29 -0
  110. data/origen_app_generators/lbin/thor +29 -0
  111. data/origen_app_generators/lbin/tilt +29 -0
  112. data/origen_app_generators/lbin/yard +29 -0
  113. data/origen_app_generators/lbin/yardoc +29 -0
  114. data/origen_app_generators/lbin/yri +29 -0
  115. data/origen_app_generators/lib/origen_app_generators.rb +125 -0
  116. data/origen_app_generators/lib/origen_app_generators/application.rb +62 -0
  117. data/origen_app_generators/lib/origen_app_generators/base.rb +257 -0
  118. data/origen_app_generators/lib/origen_app_generators/empty_application.rb +15 -0
  119. data/origen_app_generators/lib/origen_app_generators/empty_plugin.rb +15 -0
  120. data/origen_app_generators/lib/origen_app_generators/new.rb +170 -0
  121. data/origen_app_generators/lib/origen_app_generators/new_app_tests.rb +4 -0
  122. data/origen_app_generators/lib/origen_app_generators/origen_infrastructure/app_generator_plugin.rb +107 -0
  123. data/origen_app_generators/lib/origen_app_generators/plugin.rb +55 -0
  124. data/origen_app_generators/lib/origen_app_generators/test_engineering/common.rb +29 -0
  125. data/origen_app_generators/lib/origen_app_generators/test_engineering/stand_alone_application.rb +64 -0
  126. data/origen_app_generators/lib/origen_app_generators/test_engineering/test_block.rb +61 -0
  127. data/origen_app_generators/origen_app_generators.gemspec +33 -0
  128. data/{templates/code_generators → origen_app_generators/spec}/spec_helper.rb +0 -0
  129. data/origen_app_generators/target/debug.rb +8 -0
  130. data/origen_app_generators/target/default.rb +8 -0
  131. data/origen_app_generators/target/production.rb +0 -0
  132. data/origen_app_generators/templates/app_generators/application/.gitignore +37 -0
  133. data/origen_app_generators/templates/app_generators/application/.irbrc +9 -0
  134. data/origen_app_generators/templates/app_generators/application/.rspec +1 -0
  135. data/origen_app_generators/templates/app_generators/application/.travis.yml +11 -0
  136. data/origen_app_generators/templates/app_generators/application/Gemfile +34 -0
  137. data/origen_app_generators/templates/app_generators/application/Rakefile +7 -0
  138. data/origen_app_generators/templates/app_generators/application/app/blocks/top_level.rb +12 -0
  139. data/origen_app_generators/templates/app_generators/application/app/lib/module.rb +6 -0
  140. data/origen_app_generators/templates/app_generators/application/app/templates/web/index.md.erb +19 -0
  141. data/origen_app_generators/templates/app_generators/application/app/templates/web/layouts/_basic.html.erb +13 -0
  142. data/origen_app_generators/templates/app_generators/application/app/templates/web/partials/_navbar.html.erb +20 -0
  143. data/origen_app_generators/templates/app_generators/application/app/templates/web/release_notes.md.erb +5 -0
  144. data/origen_app_generators/templates/app_generators/application/config/application.rb +121 -0
  145. data/origen_app_generators/templates/app_generators/application/config/boot.rb +4 -0
  146. data/origen_app_generators/templates/app_generators/application/config/commands.rb +79 -0
  147. data/origen_app_generators/templates/app_generators/application/config/maillist_dev.txt +4 -0
  148. data/origen_app_generators/templates/app_generators/application/config/maillist_prod.txt +3 -0
  149. data/origen_app_generators/templates/app_generators/application/config/version.rb +8 -0
  150. data/origen_app_generators/templates/app_generators/application/doc/history +0 -0
  151. data/origen_app_generators/templates/app_generators/application/dot_keep +0 -0
  152. data/origen_app_generators/templates/app_generators/application/origen_core_session +2 -0
  153. data/origen_app_generators/templates/app_generators/application/spec/spec_helper.rb +44 -0
  154. data/origen_app_generators/templates/app_generators/application/target/debug.rb +8 -0
  155. data/origen_app_generators/templates/app_generators/application/target/default.rb +1 -0
  156. data/origen_app_generators/templates/app_generators/application/target/production.rb +4 -0
  157. data/origen_app_generators/templates/app_generators/new/generator.rb +102 -0
  158. data/origen_app_generators/templates/app_generators/new/info.md.erb +9 -0
  159. data/origen_app_generators/templates/app_generators/origen_infrastructure/app_generator_plugin/app/lib/application.rb +54 -0
  160. data/origen_app_generators/templates/app_generators/origen_infrastructure/app_generator_plugin/app/lib/base.rb +55 -0
  161. data/origen_app_generators/templates/app_generators/origen_infrastructure/app_generator_plugin/app/lib/module.rb +28 -0
  162. data/origen_app_generators/templates/app_generators/origen_infrastructure/app_generator_plugin/app/lib/plugin.rb +64 -0
  163. data/origen_app_generators/templates/app_generators/origen_infrastructure/app_generator_plugin/config/load_generators.rb +6 -0
  164. data/origen_app_generators/templates/app_generators/plugin/Gemfile +32 -0
  165. data/origen_app_generators/templates/app_generators/plugin/Rakefile +10 -0
  166. data/origen_app_generators/templates/app_generators/plugin/app/templates/web/index.md.erb +37 -0
  167. data/origen_app_generators/templates/app_generators/plugin/app/templates/web/partials/_navbar_external.html.erb +20 -0
  168. data/origen_app_generators/templates/app_generators/plugin/app/templates/web/partials/_navbar_internal.html.erb +20 -0
  169. data/origen_app_generators/templates/app_generators/plugin/config/boot.rb +24 -0
  170. data/origen_app_generators/templates/app_generators/plugin/gemspec.rb +43 -0
  171. data/origen_app_generators/templates/app_generators/test_engineering/environment/j750.rb +1 -0
  172. data/origen_app_generators/templates/app_generators/test_engineering/environment/uflex.rb +1 -0
  173. data/origen_app_generators/templates/app_generators/test_engineering/environment/v93k.rb +1 -0
  174. data/origen_app_generators/templates/app_generators/test_engineering/stand_alone_application/.keep +0 -0
  175. data/origen_app_generators/templates/app_generators/test_engineering/test_block/.keep +0 -0
  176. data/origen_site_config.yml +55 -5
  177. data/templates/code_generators/attributes.rb +20 -0
  178. data/templates/code_generators/class.rb +9 -0
  179. data/templates/code_generators/controller.rb +87 -0
  180. data/templates/code_generators/model.rb +21 -0
  181. data/templates/code_generators/module.rb +4 -0
  182. data/templates/code_generators/parameters.rb +19 -0
  183. data/templates/code_generators/pins.rb +28 -0
  184. data/templates/code_generators/registers.rb +20 -0
  185. data/templates/code_generators/sub_blocks.rb +24 -0
  186. data/templates/code_generators/timesets.rb +24 -0
  187. data/templates/code_generators/version.rb +0 -1
  188. data/templates/git/gitignore.erb +0 -1
  189. data/vendor/lib/models/origen/export1.rb +77 -0
  190. data/vendor/lib/models/origen/export1/block1.rb +13 -0
  191. data/vendor/lib/models/origen/export1/block1/x.rb +36 -0
  192. data/vendor/lib/models/origen/non_origen_meta_data.md +1 -0
  193. metadata +149 -68
  194. data/bin/fix_my_workspace +0 -100
  195. data/lib/c99/ate_interface.rb +0 -77
  196. data/lib/c99/nvm.rb +0 -110
  197. data/lib/c99/target/mock2.rb +0 -1
  198. data/lib/c99/target/subdir/mock3.rb +0 -1
  199. data/lib/origen/code_generators/bundler.rb +0 -17
  200. data/lib/origen/code_generators/gem_setup.rb +0 -49
  201. data/lib/origen/code_generators/rake.rb +0 -13
  202. data/lib/origen/code_generators/rspec.rb +0 -12
  203. data/lib/origen/commands/add.rb +0 -12
  204. data/lib/tasks/private/build.rake +0 -8
  205. data/templates/code_generators/gemfile_app.rb +0 -4
  206. data/templates/code_generators/gemfile_plugin.rb +0 -6
  207. data/templates/code_generators/gemspec.rb +0 -33
@@ -0,0 +1,99 @@
1
+ require 'concurrent'
2
+ module Origen
3
+ class Generator
4
+ # Provides APIs to enable applications to support concurrency
5
+ class PatternSequencer
6
+ class << self
7
+ def serialize(id = nil)
8
+ if active?
9
+ s = nil
10
+ id ||= caller[0]
11
+ @semaphores ||= {}
12
+ @semaphores[id] ||= Concurrent::Semaphore.new(1)
13
+ s = @semaphores[id]
14
+ completed = false
15
+ blocked = false
16
+ until completed
17
+ # If already acquired or available
18
+ if (thread.reservations[id] && thread.reservations[id][:semaphore]) || s.try_acquire
19
+ thread.record_active if blocked
20
+ yield
21
+ completed = true
22
+ else
23
+ thread.waiting_for_serialize(id, blocked)
24
+ blocked = true
25
+ end
26
+ end
27
+ # If the thread has reserved access to this serialized resource then don't release it now, but
28
+ # store a reference to the semaphore and it will be released at the end of the reserve block
29
+ if thread.reservations[id]
30
+ thread.reservations[id][:semaphore] = s
31
+ else
32
+ s.release
33
+ end
34
+ else
35
+ yield
36
+ end
37
+ end
38
+
39
+ # Once a lock is acquired on a serialize block with the given ID, it won't be released to
40
+ # other parallel threads until the end of this block
41
+ def reserve(id)
42
+ if active?
43
+ if thread.reservations[id]
44
+ thread.reservations[id][:count] += 1
45
+ else
46
+ thread.reservations[id] = { count: 1, semaphore: nil }
47
+ end
48
+ yield
49
+ if thread.reservations[id][:count] == 1
50
+ # May not be set if the application reserved the resource but never hit it
51
+ if s = thread.reservations[id][:semaphore]
52
+ s.release
53
+ end
54
+ thread.reservations[id] = nil
55
+ else
56
+ thread.reservations[id][:count] -= 1
57
+ end
58
+ else
59
+ yield
60
+ end
61
+ end
62
+
63
+ # Returns true if a pattern sequence is currently open/active
64
+ def active?
65
+ !!@active
66
+ end
67
+ alias_method :open?, :active?
68
+ alias_method :runnng?, :active?
69
+
70
+ # Returns the PatternThread object for the current thread
71
+ def thread
72
+ @thread.value
73
+ end
74
+
75
+ # Prepends the given string with "[<current thread ID>] " unless it already contains it
76
+ def add_thread(str)
77
+ if active? && thread
78
+ id = "[#{thread.id}] "
79
+ str.prepend(id) unless str =~ /#{id}/
80
+ end
81
+ str
82
+ end
83
+
84
+ private
85
+
86
+ def active=(val)
87
+ @active = val
88
+ end
89
+
90
+ def thread=(t)
91
+ @thread ||= Concurrent::ThreadLocalVar.new(nil)
92
+ @thread.value = t
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ PatSeq = Origen::Generator::PatternSequencer
99
+ PatSeq.send(:thread=, nil)
@@ -0,0 +1,175 @@
1
+ module Origen
2
+ class Generator
3
+ # An instance of PatternThread is created for each parallel thread of execution
4
+ # in a pattern sequence. One instance of this class is also created to represent
5
+ # the original main thread in addition to those created by calling seq.in_parallel
6
+ class PatternThread
7
+ # Returns the parent pattern sequence object
8
+ attr_reader :sequence
9
+ attr_reader :pending_cycles
10
+ attr_reader :id
11
+ attr_reader :reservations
12
+ attr_reader :cycle_count_start
13
+ attr_reader :cycle_count_stop
14
+ # A record of when the thread is active to construct the execution profile
15
+ attr_reader :events
16
+
17
+ def initialize(id, sequence, block, primary = false)
18
+ if primary
19
+ @cycle_count_start = 0
20
+ else
21
+ @cycle_count_start = current_cycle_count
22
+ end
23
+ @events = [[:active, cycle_count_start]]
24
+ @id = id.to_sym
25
+ @sequence = sequence
26
+ @block = block
27
+ @primary = primary
28
+ @running = Concurrent::Event.new
29
+ @waiting = Concurrent::Event.new
30
+ @pending_cycles = nil
31
+ @completed = false
32
+ @reservations = {}
33
+ end
34
+
35
+ # Returns true if this is main thread (the one from which all in_parallel threads
36
+ # have been branched from)
37
+ def primary?
38
+ @primary
39
+ end
40
+
41
+ # @api private
42
+ #
43
+ # This method is called once by the pattern sequence to start a new thread. It will block until
44
+ # the thread is in the waiting state.
45
+ def start
46
+ @thread = Thread.new do
47
+ PatSeq.send(:thread=, self)
48
+ wait
49
+ @block.call(sequence)
50
+ sequence.send(:thread_completed, self)
51
+ record_cycle_count_stop
52
+ @completed = true
53
+ wait
54
+ end
55
+ @waiting.wait
56
+ end
57
+
58
+ def record_cycle_count_stop
59
+ @cycle_count_stop = current_cycle_count
60
+ events << [:stopped, cycle_count_stop]
61
+ events.freeze
62
+ end
63
+
64
+ def record_active
65
+ events << [:active, current_cycle_count]
66
+ end
67
+
68
+ def current_cycle_count
69
+ tester.try(:cycle_count) || 0
70
+ end
71
+
72
+ def execution_profile(start, stop, step)
73
+ events = @events.dup
74
+ cycles = start
75
+ state = :inactive
76
+ line = ''
77
+ ((stop - start) / step).times do |i|
78
+ active_cycles = 0
79
+ while events.first && events.first[1] >= cycles && events.first[1] < cycles + step
80
+ event = events.shift
81
+ # Bring the current cycles up to this event point applying the current state
82
+ if state == :active
83
+ active_cycles += event[1] - cycles
84
+ end
85
+ state = event[0] == :active ? :active : :inactive
86
+ cycles = event[1]
87
+ end
88
+
89
+ # Bring the current cycles up to the end of this profile tick
90
+ if state == :active
91
+ active_cycles += ((i + 1) * step) - cycles
92
+ end
93
+ cycles = ((i + 1) * step)
94
+
95
+ if active_cycles == 0
96
+ line += '_'
97
+ elsif active_cycles > (step * 0.5)
98
+ line += '█'
99
+ else
100
+ line += '▄'
101
+ end
102
+ end
103
+ line
104
+ end
105
+
106
+ # Will be called when the thread can't execute its next cycle because it is waiting to obtain a
107
+ # lock on a serialized block
108
+ def waiting_for_serialize(serialize_id, skip_event = false)
109
+ # puts "Thread #{id} is blocked waiting for #{serialize_id}"
110
+ events << [:waiting, current_cycle_count] unless skip_event
111
+ wait
112
+ end
113
+
114
+ # Will be called when the thread can't execute its next cycle because it is waiting for another
115
+ # thread to complete
116
+ def waiting_for_thread(skip_event = false)
117
+ events << [:waiting, current_cycle_count] unless skip_event
118
+ wait
119
+ end
120
+
121
+ # Will be called when the thread is ready for the next cycle
122
+ def cycle(options)
123
+ @pending_cycles = options[:repeat] || 1
124
+ # If there are threads pending start and we are about to enter a long delay, block for only
125
+ # one cycle to give them a change to get underway and make use of this delay
126
+ if @pending_cycles > 1 && sequence.send(:threads_waiting_to_start?)
127
+ remainder = @pending_cycles - 1
128
+ @pending_cycles = 1
129
+ end
130
+ wait
131
+ @pending_cycles = remainder if remainder
132
+ # If the sequence did not do enough cycles in that round to satisfy this thread, then go back
133
+ # around to complete the remainder before continuing with the rest of the pattern
134
+ if @pending_cycles == 0
135
+ @pending_cycles = nil
136
+ elsif @pending_cycles > 0
137
+ @pending_cycles.cycles
138
+ else
139
+ fail "Something has gone wrong @pending_cycles is #{@pending_cycles}"
140
+ end
141
+ end
142
+
143
+ # @api private
144
+ def executed_cycles(cycles)
145
+ @pending_cycles -= cycles if @pending_cycles
146
+ end
147
+
148
+ def completed?
149
+ @completed
150
+ end
151
+
152
+ # Returns true if the thread is currently waiting for the pattern sequence to advance it
153
+ def waiting?
154
+ @waiting.set?
155
+ end
156
+
157
+ # This should be called only by the pattern thread itself, and will block it until it is told to
158
+ # advance by the pattern sequence running in the main thread
159
+ def wait
160
+ @running.reset
161
+ @waiting.set
162
+ @running.wait
163
+ end
164
+
165
+ # This should be called only by the pattern sequence running in the main thread, it will un-block the
166
+ # pattern thread which is currently waiting, and it will block the main thread until the pattern thread
167
+ # reaches the next wait point (or completes)
168
+ def advance(completed_cycles = nil)
169
+ @waiting.reset
170
+ @running.set # Release the pattern thread
171
+ @waiting.wait # And wait for it to reach the next wait point
172
+ end
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,381 @@
1
+ module Origen
2
+ # This module is responsible for enhancing how Ruby requires and loads files to support loading of
3
+ # classes and modules from an application's app dir without having to require them.
4
+ #
5
+ # It also implements the <model>.load_block method that loads files from app/blocks.
6
+ module Loader
7
+ # @api private
8
+ #
9
+ # Unload all constants (classes and modules) that have been auto-loaded since this was last called
10
+ def self.unload
11
+ # puts "******** LOADED CONSTS@ #{@loaded_consts}"
12
+ path = []
13
+ (@consts_hierarchy || {}).each do |name, children|
14
+ _unload(path, name, children)
15
+ end
16
+ @consts_hierarchy = {}
17
+ @loaded_consts = {}
18
+ (Origen.app.plugins + [Origen.app]).each do |app|
19
+ app.instance_variable_set(:@blocks_files, nil)
20
+ end
21
+ nil
22
+ end
23
+
24
+ # @api private
25
+ def self._unload(path, name, children)
26
+ path << name
27
+ children.each do |name, children|
28
+ _unload(path, name, children)
29
+ end
30
+ const = path.join('::')
31
+ if @loaded_consts[const]
32
+ path[0...-1].join('::').constantize.send :remove_const, path.last
33
+ # puts "******** Unloading: #{const}"
34
+ end
35
+ path.pop
36
+ end
37
+
38
+ # @api private
39
+ def self.record_const(name)
40
+ @consts_hierarchy ||= {}
41
+ @loaded_consts ||= {}
42
+ @loaded_consts[name] = true
43
+ pointer = nil
44
+ name.split('::').each do |name|
45
+ if pointer
46
+ pointer[name] ||= {}
47
+ pointer = pointer[name]
48
+ else
49
+ @consts_hierarchy[name] ||= {}
50
+ pointer = @consts_hierarchy[name]
51
+ end
52
+ end
53
+ end
54
+
55
+ # @api private
56
+ def self.load_attributes(file, model)
57
+ if model.respond_to?(:is_an_origen_model?)
58
+ attributes = model.attributes.dup
59
+ else
60
+ attributes = {}
61
+ end
62
+ vars = model.instance_variables
63
+ if load_block_file(file, model)
64
+ # Update the value of any pre-existing attribute that could have just changed
65
+ attributes.each do |a, v|
66
+ attributes[a] = model.instance_variable_get("@#{a}")
67
+ end
68
+ # And add any new ones that were encountered for the first time
69
+ (model.instance_variables - vars).each do |var|
70
+ val = model.instance_variable_get(var)
71
+ attribute = var.to_s.sub('@', '')
72
+ attributes[attribute.to_sym] = val
73
+ unless model.respond_to?(attribute)
74
+ model.define_singleton_method(attribute) do
75
+ instance_variable_get(var)
76
+ end
77
+ end
78
+ if val == true || val == false
79
+ attribute += '?'
80
+ unless model.respond_to?(attribute)
81
+ model.define_singleton_method(attribute) do
82
+ instance_variable_get(var)
83
+ end
84
+ end
85
+ end
86
+ end
87
+ if model.respond_to?(:is_an_origen_model?)
88
+ attributes.freeze
89
+ model.instance_variable_set(:@attributes, attributes)
90
+ end
91
+ true
92
+ end
93
+ end
94
+
95
+ # @api private
96
+ def self.load_block_file(file, model)
97
+ file = file.to_s
98
+ if File.exist?(file)
99
+ File.open(file, 'r') do |f|
100
+ model.instance_eval(f.read, file)
101
+ end
102
+ end
103
+ true
104
+ end
105
+
106
+ # @api private
107
+ def self.with_parameters_transaction(type)
108
+ if type == :parameters
109
+ Origen::Parameters.transaction do
110
+ yield
111
+ end
112
+ else
113
+ yield
114
+ end
115
+ end
116
+
117
+ # If a block definition exists for the given model, then this will load it and apply it to
118
+ # the model.
119
+ # Returns true if a model is found and loaded, otherwise nil.
120
+ def self.load_block(model, options = {})
121
+ model = model.model # Ensure we have a handle on the model and not its controller
122
+ loaded = nil
123
+ if app = options[:app] || model.app
124
+ if options[:path]
125
+ full_paths = Array(options[:path])
126
+ else
127
+ full_paths = model.class.to_s.split('::')
128
+ full_paths.shift # Throw away the app namespace
129
+ full_paths = [full_paths.join('/')]
130
+ end
131
+ full_paths.each do |full_path|
132
+ paths = full_path.to_s.split('/')
133
+ key = ''
134
+ only = Array(options[:only]) if options[:only]
135
+ except = Array(options[:except]) if options[:except]
136
+ path = paths.map(&:underscore).join('/')
137
+ # If the path refers to a nested sub-block then don't load the full hierarchy since they
138
+ # don't support inheritance or derivatives, modify the paths array so that only the sub-block
139
+ # level will be loaded and nothing else.
140
+ paths = [path] if app.blocks_files[path] && app.blocks_files[path][:_sub_block]
141
+ # These will be loaded first, followed by the rest in an undefined order.
142
+ # Attributes and parameters are first so that they may be referenced in the other files.
143
+ # Sub-blocks was added early due to a corner case issue that could be encountered if the pins or
144
+ # regs imported an Origen exported file that defined a module with the same name as a sub-block
145
+ # class, in that case the sub-block class would not be auto-loaded.
146
+ load_first = [:attributes, :parameters, :sub_blocks]
147
+
148
+ load_first.each do |type|
149
+ unless (only && !only.include?(type)) || (except && except.include?(type))
150
+ with_parameters_transaction(type) do
151
+ paths.each_with_index do |path, i|
152
+ key = i == 0 ? path.underscore : "#{key}/#{path.underscore}"
153
+ if app.blocks_files[key] && app.blocks_files[key][type]
154
+ app.blocks_files[key][type].each do |f|
155
+ if type == :attributes
156
+ success = load_attributes(f, model)
157
+ else
158
+ success = load_block_file(f, model)
159
+ end
160
+ loaded ||= success
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end
166
+ end
167
+
168
+ # Now load the rest
169
+ paths.each_with_index do |path, i|
170
+ key = i == 0 ? path.underscore : "#{key}/#{path.underscore}"
171
+ if app.blocks_files[key]
172
+ app.blocks_files[key].each do |type, files|
173
+ unless type == :_sub_block || load_first.include?(type) || (only && !only.include?(type)) || (except && except.include?(type))
174
+ files.each { |f| success = load_block_file(f, model); loaded ||= success }
175
+ end
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end
181
+ loaded
182
+ end
183
+
184
+ # This is inspired by Rails' ActiveSupport::Dependencies module.
185
+ module ModuleConstMissing
186
+ def self.append_features(base)
187
+ base.class_eval do
188
+ # Emulate #exclude via an ivar
189
+ return if defined?(@_const_missing) && @_const_missing
190
+ @_const_missing = instance_method(:const_missing)
191
+ remove_method(:const_missing)
192
+ end
193
+ super
194
+ end
195
+
196
+ def self.exclude_from(base)
197
+ base.class_eval do
198
+ define_method :const_missing, @_const_missing
199
+ @_const_missing = nil
200
+ end
201
+ end
202
+
203
+ # @api private
204
+ # Substitutes a single occurrence of 'derivatives' in the given dirs array, starting
205
+ # from the end of it and replacing it with the given new value.
206
+ # Returns true if a substitution is made, else false.
207
+ def _sub_derivatives_from_end(new_val, dirs)
208
+ subbed = false
209
+ size = dirs.size - 1
210
+ dirs.reverse_each.with_index do |val, i|
211
+ if val == 'derivatives'
212
+ dirs[size - i] = new_val
213
+ subbed = true
214
+ break
215
+ end
216
+ end
217
+ dirs if subbed
218
+ end
219
+
220
+ # Allows classes and modules to be defined in app/blocks and app/lib without needing to
221
+ # require them and in the case of app/blocks to use a custom directory structure.
222
+ #
223
+ # The first time a reference is made to a class or module name it will trigger this hook,
224
+ # and we then work out what the file name should be and require it.
225
+ def const_missing(name)
226
+ if Origen.in_app_workspace?
227
+ if self == Object
228
+ name = name.to_s
229
+ else
230
+ name = "#{self}::#{name}"
231
+ end
232
+ return nil if @_checking_name == name
233
+ names = name.split('::')
234
+ namespace = names.shift
235
+ if app = Origen::Application.from_namespace(namespace)
236
+ # First we are going to check for a match in the app/blocks directory, this needs to be handled
237
+ # specially since it follows a non-std structure, e.g. use of derivatives/ and sub_blocks/ folders
238
+ # for organization without having them as part of the class name-spacing
239
+ altname = nil
240
+ dirs = [app.root, 'app', 'blocks']
241
+ names.each_with_index do |name, i|
242
+ dirs << 'derivatives' unless i == 0
243
+ dirs << name.underscore
244
+ end
245
+
246
+ # Is this a reference to a model?
247
+ if File.exist?(f = File.join(*dirs, 'model.rb'))
248
+ model = _load_const(f, name)
249
+ # Also load the model's controller if it exists
250
+ if File.exist?(f = File.join(*dirs, 'controller.rb'))
251
+ controller = _load_const(f, name + 'Controller')
252
+ end
253
+ return model
254
+ end
255
+
256
+ # Is this a reference to a controller?
257
+ if dirs.last.to_s =~ /_controller$/
258
+ controller_reference = true
259
+ dirs << dirs.pop.sub(/_controller$/, '')
260
+ if File.exist?(f = File.join(*dirs, 'controller.rb'))
261
+ return _load_const(f, name)
262
+ end
263
+ end
264
+
265
+ # Is this a reference to a sub-block model or controller that is nested within a block?
266
+ dirs_ = dirs.dup
267
+ while dirs_ = _sub_derivatives_from_end('sub_blocks', dirs_)
268
+ if controller_reference
269
+ if File.exist?(f = File.join(*dirs_, 'controller.rb'))
270
+ return _load_const(f, name)
271
+ end
272
+ elsif File.exist?(f = File.join(*dirs_, 'model.rb'))
273
+ model = _load_const(f, name)
274
+ # Also load the model's controller if it exists
275
+ if File.exist?(f = File.join(*dirs_, 'controller.rb'))
276
+ controller = _load_const(f, name + 'Controller')
277
+ end
278
+ return model
279
+ end
280
+ end
281
+
282
+ # Is this a reference to a module that has been added to a model or controller?
283
+ # In this case dirs contains something like:
284
+ # [..., "my_model", "derivatives", "my_module"]
285
+ # [..., "my_model_controller", "derivatives", "my_module"]
286
+ # So let's try by transforming these into:
287
+ # [..., "my_model", "model"] + "my_module.rb"
288
+ # [..., "my_model", "controller"] + "my_module.rb"
289
+ filename = dirs.pop + '.rb'
290
+ dirs.pop # Lose 'derivatives'
291
+ if dirs.last.to_s =~ /_controller$/
292
+ dirs << dirs.pop.sub(/_controller$/, '')
293
+ dirs << 'controller'
294
+ else
295
+ dirs << 'model'
296
+ end
297
+ if File.exist?(f = File.join(*dirs, filename))
298
+ return _load_const(f, name)
299
+ end
300
+
301
+ # Now that we have established that it is not a reference to a block (which has a non-std code
302
+ # organization structure), we can now check for a match in the app/lib directory following std
303
+ # Ruby code organization conventions
304
+ until names.empty?
305
+ path = File.join(*names.map(&:underscore)) + '.rb'
306
+
307
+ f = File.join(app.root, 'app', 'lib', namespace.underscore, path)
308
+ if File.exist?(f)
309
+ model = _load_const(f, name, altname)
310
+ # Try and reference the controller to load it too, though don't raise an error if it
311
+ # doesn't exist
312
+ @@pre_loading_controller = true
313
+ eval "#{altname || name}Controller"
314
+ return model
315
+ # If a folder exists that is named after this constant, then assume it is an otherwise
316
+ # undeclared namespace module and declare it now
317
+ elsif File.exist?(f.sub('.rb', ''))
318
+ return const_set path.sub('.rb', '').camelcase, Module.new
319
+ end
320
+
321
+ # Don't waste time looking up the namespace hierarchy for the controller, if it exists it
322
+ # should be within the exact same namespace as the model
323
+ return nil if defined?(@@pre_loading_controller) && @@pre_loading_controller
324
+
325
+ # Remove the highest level namespace and then search again in the parent namespace
326
+ if discarded_namespace = names.delete_at(-2)
327
+ altname ||= name
328
+ altname = altname.sub("#{discarded_namespace}::", '')
329
+ else
330
+ names.pop
331
+ end
332
+ end
333
+
334
+ _raise_uninitialized_constant_error(name)
335
+ else
336
+ _raise_uninitialized_constant_error(name)
337
+ end
338
+ else
339
+ _raise_uninitialized_constant_error(name)
340
+ end
341
+ ensure
342
+ @@pre_loading_controller = false
343
+ end
344
+
345
+ # @api_private
346
+ def _load_const(file, name, altname = nil)
347
+ load file
348
+ if defined?(@@pre_loading_controller)
349
+ return if @@pre_loading_controller
350
+ end
351
+ @_checking_name = altname || name
352
+ const = eval(altname || name)
353
+ @_checking_name = nil
354
+ if const
355
+ Origen::Loader.record_const(altname || name)
356
+ return const
357
+ end
358
+ msg ||= "uninitialized constant #{name} (expected it to be defined in: #{file})"
359
+ _raise_uninitialized_constant_error(name, msg)
360
+ end
361
+
362
+ # @api private
363
+ def _raise_uninitialized_constant_error(name, msg = nil)
364
+ msg ||= "uninitialized constant #{name}"
365
+ name_error = NameError.new(msg, name)
366
+ name_error.set_backtrace(caller.reject { |l| l =~ /^#{__FILE__}/ })
367
+ fail name_error
368
+ end
369
+ end
370
+
371
+ def self.enable_origen_load_extensions!
372
+ Module.class_eval { include ModuleConstMissing }
373
+ end
374
+
375
+ def self.disable_origen_load_extensions!
376
+ ModuleConstMissing.exclude_from(Module)
377
+ end
378
+ end
379
+ end
380
+
381
+ Origen::Loader.enable_origen_load_extensions!