generate_module.rb 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. # ==========================================
  2. # Unity Project - A Test Framework for C
  3. # Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams
  4. # [Released under MIT License. Please refer to license.txt for details]
  5. # ==========================================
  6. # This script creates all the files with start code necessary for a new module.
  7. # A simple module only requires a source file, header file, and test file.
  8. # Triad modules require a source, header, and test file for each triad type (like model, conductor, and hardware).
  9. require 'rubygems'
  10. require 'fileutils'
  11. require 'pathname'
  12. #TEMPLATE_TST
  13. TEMPLATE_TST ||= %q[#include "unity.h"
  14. %2$s#include "%1$s.h"
  15. void setUp(void)
  16. {
  17. }
  18. void tearDown(void)
  19. {
  20. }
  21. void test_%1$s_NeedToImplement(void)
  22. {
  23. TEST_IGNORE_MESSAGE("Need to Implement %1$s");
  24. }
  25. ]
  26. #TEMPLATE_SRC
  27. TEMPLATE_SRC ||= %q[%2$s#include "%1$s.h"
  28. ]
  29. #TEMPLATE_INC
  30. TEMPLATE_INC ||= %q[#ifndef _%3$s_H
  31. #define _%3$s_H
  32. %2$s
  33. #endif // _%3$s_H
  34. ]
  35. class UnityModuleGenerator
  36. ############################
  37. def initialize(options=nil)
  38. here = File.expand_path(File.dirname(__FILE__)) + '/'
  39. @options = UnityModuleGenerator.default_options
  40. case(options)
  41. when NilClass then @options
  42. when String then @options.merge!(UnityModuleGenerator.grab_config(options))
  43. when Hash then @options.merge!(options)
  44. else raise "If you specify arguments, it should be a filename or a hash of options"
  45. end
  46. # Create default file paths if none were provided
  47. @options[:path_src] = here + "../src/" if @options[:path_src].nil?
  48. @options[:path_inc] = @options[:path_src] if @options[:path_inc].nil?
  49. @options[:path_tst] = here + "../test/" if @options[:path_tst].nil?
  50. @options[:path_src] += '/' unless (@options[:path_src][-1] == 47)
  51. @options[:path_inc] += '/' unless (@options[:path_inc][-1] == 47)
  52. @options[:path_tst] += '/' unless (@options[:path_tst][-1] == 47)
  53. #Built in patterns
  54. @patterns = { 'src' => {'' => { :inc => [] } },
  55. 'test'=> {'' => { :inc => [] } },
  56. 'dh' => {'Driver' => { :inc => [create_filename('%1$s','Hardware.h')] },
  57. 'Hardware' => { :inc => [] }
  58. },
  59. 'dih' => {'Driver' => { :inc => [create_filename('%1$s','Hardware.h'), create_filename('%1$s','Interrupt.h')] },
  60. 'Interrupt'=> { :inc => [create_filename('%1$s','Hardware.h')] },
  61. 'Hardware' => { :inc => [] }
  62. },
  63. 'mch' => {'Model' => { :inc => [] },
  64. 'Conductor'=> { :inc => [create_filename('%1$s','Model.h'), create_filename('%1$s','Hardware.h')] },
  65. 'Hardware' => { :inc => [] }
  66. },
  67. 'mvp' => {'Model' => { :inc => [] },
  68. 'Presenter'=> { :inc => [create_filename('%1$s','Model.h'), create_filename('%1$s','View.h')] },
  69. 'View' => { :inc => [] }
  70. }
  71. }
  72. end
  73. ############################
  74. def self.default_options
  75. {
  76. :pattern => "src",
  77. :includes =>
  78. {
  79. :src => [],
  80. :inc => [],
  81. :tst => [],
  82. },
  83. :update_svn => false,
  84. :boilerplates => {},
  85. :test_prefix => 'Test',
  86. :mock_prefix => 'Mock',
  87. }
  88. end
  89. ############################
  90. def self.grab_config(config_file)
  91. options = self.default_options
  92. unless (config_file.nil? or config_file.empty?)
  93. require 'yaml'
  94. yaml_guts = YAML.load_file(config_file)
  95. options.merge!(yaml_guts[:unity] || yaml_guts[:cmock])
  96. raise "No :unity or :cmock section found in #{config_file}" unless options
  97. end
  98. return(options)
  99. end
  100. ############################
  101. def files_to_operate_on(module_name, pattern=nil)
  102. #strip any leading path information from the module name and save for later
  103. subfolder = File.dirname(module_name)
  104. module_name = File.basename(module_name)
  105. #create triad definition
  106. prefix = @options[:test_prefix] || 'Test'
  107. triad = [ { :ext => '.c', :path => @options[:path_src], :prefix => "", :template => TEMPLATE_SRC, :inc => :src, :boilerplate => @options[:boilerplates][:src] },
  108. { :ext => '.h', :path => @options[:path_inc], :prefix => "", :template => TEMPLATE_INC, :inc => :inc, :boilerplate => @options[:boilerplates][:inc] },
  109. { :ext => '.c', :path => @options[:path_tst], :prefix => prefix, :template => TEMPLATE_TST, :inc => :tst, :boilerplate => @options[:boilerplates][:tst] },
  110. ]
  111. #prepare the pattern for use
  112. pattern = (pattern || @options[:pattern] || 'src').downcase
  113. patterns = @patterns[pattern]
  114. raise "ERROR: The design pattern '#{pattern}' specified isn't one that I recognize!" if patterns.nil?
  115. #single file patterns (currently just 'test') can reject the other parts of the triad
  116. if (pattern == 'test')
  117. triad.reject!{|v| v[:inc] != :tst }
  118. end
  119. # Assemble the path/names of the files we need to work with.
  120. files = []
  121. triad.each do |cfg|
  122. patterns.each_pair do |pattern_file, pattern_traits|
  123. submodule_name = create_filename(module_name, pattern_file)
  124. filename = cfg[:prefix] + submodule_name + cfg[:ext]
  125. files << {
  126. :path => (Pathname.new("#{cfg[:path]}#{subfolder}") + filename).cleanpath,
  127. :name => submodule_name,
  128. :template => cfg[:template],
  129. :boilerplate => cfg[:boilerplate],
  130. :includes => case(cfg[:inc])
  131. when :src then (@options[:includes][:src] || []) | pattern_traits[:inc].map{|f| f % [module_name]}
  132. when :inc then (@options[:includes][:inc] || [])
  133. when :tst then (@options[:includes][:tst] || []) | pattern_traits[:inc].map{|f| "#{@options[:mock_prefix]}#{f}" % [module_name]}
  134. end
  135. }
  136. end
  137. end
  138. return files
  139. end
  140. ############################
  141. def create_filename(part1, part2="")
  142. if part2.empty?
  143. case(@options[:naming])
  144. when 'bumpy' then part1
  145. when 'camel' then part1
  146. when 'snake' then part1.downcase
  147. when 'caps' then part1.upcase
  148. else part1.downcase
  149. end
  150. else
  151. case(@options[:naming])
  152. when 'bumpy' then part1 + part2
  153. when 'camel' then part1 + part2
  154. when 'snake' then part1.downcase + "_" + part2.downcase
  155. when 'caps' then part1.upcase + "_" + part2.upcase
  156. else part1.downcase + "_" + part2.downcase
  157. end
  158. end
  159. end
  160. ############################
  161. def generate(module_name, pattern=nil)
  162. files = files_to_operate_on(module_name, pattern)
  163. #Abort if all of the module files already exist
  164. all_files_exist = true
  165. files.each do |file|
  166. if not File.exist?(file[:path])
  167. all_files_exist = false
  168. end
  169. end
  170. raise "ERROR: File #{files[0][:name]} already exists. Exiting." if all_files_exist
  171. # Create Source Modules
  172. files.each_with_index do |file, i|
  173. # If this file already exists, don't overwrite it.
  174. if File.exist?(file[:path])
  175. puts "File #{file[:path]} already exists!"
  176. next
  177. end
  178. # Create the path first if necessary.
  179. FileUtils.mkdir_p(File.dirname(file[:path]), :verbose => false)
  180. File.open(file[:path], 'w') do |f|
  181. f.write("#{file[:boilerplate]}\n" % [file[:name]]) unless file[:boilerplate].nil?
  182. f.write(file[:template] % [ file[:name],
  183. file[:includes].map{|f| "#include \"#{f}\"\n"}.join,
  184. file[:name].upcase ]
  185. )
  186. end
  187. if (@options[:update_svn])
  188. `svn add \"#{file[:path]}\"`
  189. if $?.exitstatus == 0
  190. puts "File #{file[:path]} created and added to source control"
  191. else
  192. puts "File #{file[:path]} created but FAILED adding to source control!"
  193. end
  194. else
  195. puts "File #{file[:path]} created"
  196. end
  197. end
  198. puts 'Generate Complete'
  199. end
  200. ############################
  201. def destroy(module_name, pattern=nil)
  202. files_to_operate_on(module_name, pattern).each do |filespec|
  203. file = filespec[:path]
  204. if File.exist?(file)
  205. if @options[:update_svn]
  206. `svn delete \"#{file}\" --force`
  207. puts "File #{file} deleted and removed from source control"
  208. else
  209. FileUtils.remove(file)
  210. puts "File #{file} deleted"
  211. end
  212. else
  213. puts "File #{file} does not exist so cannot be removed."
  214. end
  215. end
  216. puts "Destroy Complete"
  217. end
  218. end
  219. ############################
  220. #Handle As Command Line If Called That Way
  221. if ($0 == __FILE__)
  222. destroy = false
  223. options = { }
  224. module_name = nil
  225. # Parse the command line parameters.
  226. ARGV.each do |arg|
  227. case(arg)
  228. when /^-d/ then destroy = true
  229. when /^-u/ then options[:update_svn] = true
  230. when /^-p\"?(\w+)\"?/ then options[:pattern] = $1
  231. when /^-s\"?(.+)\"?/ then options[:path_src] = $1
  232. when /^-i\"?(.+)\"?/ then options[:path_inc] = $1
  233. when /^-t\"?(.+)\"?/ then options[:path_tst] = $1
  234. when /^-n\"?(.+)\"?/ then options[:naming] = $1
  235. when /^-y\"?(.+)\"?/ then options = UnityModuleGenerator.grab_config($1)
  236. when /^(\w+)/
  237. raise "ERROR: You can't have more than one Module name specified!" unless module_name.nil?
  238. module_name = arg
  239. when /^-(h|-help)/
  240. ARGV = []
  241. else
  242. raise "ERROR: Unknown option specified '#{arg}'"
  243. end
  244. end
  245. if (!ARGV[0])
  246. puts [ "\nGENERATE MODULE\n-------- ------",
  247. "\nUsage: ruby generate_module [options] module_name",
  248. " -i\"include\" sets the path to output headers to 'include' (DEFAULT ../src)",
  249. " -s\"../src\" sets the path to output source to '../src' (DEFAULT ../src)",
  250. " -t\"C:/test\" sets the path to output source to 'C:/test' (DEFAULT ../test)",
  251. " -p\"MCH\" sets the output pattern to MCH.",
  252. " dh - driver hardware.",
  253. " dih - driver interrupt hardware.",
  254. " mch - model conductor hardware.",
  255. " mvp - model view presenter.",
  256. " src - just a source module, header and test. (DEFAULT)",
  257. " test - just a test file.",
  258. " -d destroy module instead of creating it.",
  259. " -n\"camel\" sets the file naming convention.",
  260. " bumpy - BumpyCaseFilenames.",
  261. " camel - camelCaseFilenames.",
  262. " snake - snake_case_filenames. (DEFAULT)",
  263. " caps - CAPS_CASE_FILENAMES.",
  264. " -u update subversion too (requires subversion command line)",
  265. " -y\"my.yml\" selects a different yaml config file for module generation",
  266. "" ].join("\n")
  267. exit
  268. end
  269. raise "ERROR: You must have a Module name specified! (use option -h for help)" if module_name.nil?
  270. if (destroy)
  271. UnityModuleGenerator.new(options).destroy(module_name)
  272. else
  273. UnityModuleGenerator.new(options).generate(module_name)
  274. end
  275. end