123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- # ==========================================
- # Unity Project - A Test Framework for C
- # Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams
- # [Released under MIT License. Please refer to license.txt for details]
- # ==========================================
- # This script creates all the files with start code necessary for a new module.
- # A simple module only requires a source file, header file, and test file.
- # Triad modules require a source, header, and test file for each triad type (like model, conductor, and hardware).
- require 'rubygems'
- require 'fileutils'
- require 'pathname'
- #TEMPLATE_TST
- TEMPLATE_TST ||= %q[#include "unity.h"
- %2$s#include "%1$s.h"
- void setUp(void)
- {
- }
- void tearDown(void)
- {
- }
- void test_%1$s_NeedToImplement(void)
- {
- TEST_IGNORE_MESSAGE("Need to Implement %1$s");
- }
- ]
- #TEMPLATE_SRC
- TEMPLATE_SRC ||= %q[%2$s#include "%1$s.h"
- ]
- #TEMPLATE_INC
- TEMPLATE_INC ||= %q[#ifndef _%3$s_H
- #define _%3$s_H
- %2$s
- #endif // _%3$s_H
- ]
- class UnityModuleGenerator
- ############################
- def initialize(options=nil)
- here = File.expand_path(File.dirname(__FILE__)) + '/'
- @options = UnityModuleGenerator.default_options
- case(options)
- when NilClass then @options
- when String then @options.merge!(UnityModuleGenerator.grab_config(options))
- when Hash then @options.merge!(options)
- else raise "If you specify arguments, it should be a filename or a hash of options"
- end
- # Create default file paths if none were provided
- @options[:path_src] = here + "../src/" if @options[:path_src].nil?
- @options[:path_inc] = @options[:path_src] if @options[:path_inc].nil?
- @options[:path_tst] = here + "../test/" if @options[:path_tst].nil?
- @options[:path_src] += '/' unless (@options[:path_src][-1] == 47)
- @options[:path_inc] += '/' unless (@options[:path_inc][-1] == 47)
- @options[:path_tst] += '/' unless (@options[:path_tst][-1] == 47)
- #Built in patterns
- @patterns = { 'src' => {'' => { :inc => [] } },
- 'test'=> {'' => { :inc => [] } },
- 'dh' => {'Driver' => { :inc => [create_filename('%1$s','Hardware.h')] },
- 'Hardware' => { :inc => [] }
- },
- 'dih' => {'Driver' => { :inc => [create_filename('%1$s','Hardware.h'), create_filename('%1$s','Interrupt.h')] },
- 'Interrupt'=> { :inc => [create_filename('%1$s','Hardware.h')] },
- 'Hardware' => { :inc => [] }
- },
- 'mch' => {'Model' => { :inc => [] },
- 'Conductor'=> { :inc => [create_filename('%1$s','Model.h'), create_filename('%1$s','Hardware.h')] },
- 'Hardware' => { :inc => [] }
- },
- 'mvp' => {'Model' => { :inc => [] },
- 'Presenter'=> { :inc => [create_filename('%1$s','Model.h'), create_filename('%1$s','View.h')] },
- 'View' => { :inc => [] }
- }
- }
- end
- ############################
- def self.default_options
- {
- :pattern => "src",
- :includes =>
- {
- :src => [],
- :inc => [],
- :tst => [],
- },
- :update_svn => false,
- :boilerplates => {},
- :test_prefix => 'Test',
- :mock_prefix => 'Mock',
- }
- end
- ############################
- def self.grab_config(config_file)
- options = self.default_options
- unless (config_file.nil? or config_file.empty?)
- require 'yaml'
- yaml_guts = YAML.load_file(config_file)
- options.merge!(yaml_guts[:unity] || yaml_guts[:cmock])
- raise "No :unity or :cmock section found in #{config_file}" unless options
- end
- return(options)
- end
- ############################
- def files_to_operate_on(module_name, pattern=nil)
- #strip any leading path information from the module name and save for later
- subfolder = File.dirname(module_name)
- module_name = File.basename(module_name)
- #create triad definition
- prefix = @options[:test_prefix] || 'Test'
- triad = [ { :ext => '.c', :path => @options[:path_src], :prefix => "", :template => TEMPLATE_SRC, :inc => :src, :boilerplate => @options[:boilerplates][:src] },
- { :ext => '.h', :path => @options[:path_inc], :prefix => "", :template => TEMPLATE_INC, :inc => :inc, :boilerplate => @options[:boilerplates][:inc] },
- { :ext => '.c', :path => @options[:path_tst], :prefix => prefix, :template => TEMPLATE_TST, :inc => :tst, :boilerplate => @options[:boilerplates][:tst] },
- ]
- #prepare the pattern for use
- pattern = (pattern || @options[:pattern] || 'src').downcase
- patterns = @patterns[pattern]
- raise "ERROR: The design pattern '#{pattern}' specified isn't one that I recognize!" if patterns.nil?
- #single file patterns (currently just 'test') can reject the other parts of the triad
- if (pattern == 'test')
- triad.reject!{|v| v[:inc] != :tst }
- end
- # Assemble the path/names of the files we need to work with.
- files = []
- triad.each do |cfg|
- patterns.each_pair do |pattern_file, pattern_traits|
- submodule_name = create_filename(module_name, pattern_file)
- filename = cfg[:prefix] + submodule_name + cfg[:ext]
- files << {
- :path => (Pathname.new("#{cfg[:path]}#{subfolder}") + filename).cleanpath,
- :name => submodule_name,
- :template => cfg[:template],
- :boilerplate => cfg[:boilerplate],
- :includes => case(cfg[:inc])
- when :src then (@options[:includes][:src] || []) | pattern_traits[:inc].map{|f| f % [module_name]}
- when :inc then (@options[:includes][:inc] || [])
- when :tst then (@options[:includes][:tst] || []) | pattern_traits[:inc].map{|f| "#{@options[:mock_prefix]}#{f}" % [module_name]}
- end
- }
- end
- end
- return files
- end
- ############################
- def create_filename(part1, part2="")
- if part2.empty?
- case(@options[:naming])
- when 'bumpy' then part1
- when 'camel' then part1
- when 'snake' then part1.downcase
- when 'caps' then part1.upcase
- else part1.downcase
- end
- else
- case(@options[:naming])
- when 'bumpy' then part1 + part2
- when 'camel' then part1 + part2
- when 'snake' then part1.downcase + "_" + part2.downcase
- when 'caps' then part1.upcase + "_" + part2.upcase
- else part1.downcase + "_" + part2.downcase
- end
- end
- end
- ############################
- def generate(module_name, pattern=nil)
- files = files_to_operate_on(module_name, pattern)
- #Abort if all of the module files already exist
- all_files_exist = true
- files.each do |file|
- if not File.exist?(file[:path])
- all_files_exist = false
- end
- end
- raise "ERROR: File #{files[0][:name]} already exists. Exiting." if all_files_exist
- # Create Source Modules
- files.each_with_index do |file, i|
- # If this file already exists, don't overwrite it.
- if File.exist?(file[:path])
- puts "File #{file[:path]} already exists!"
- next
- end
- # Create the path first if necessary.
- FileUtils.mkdir_p(File.dirname(file[:path]), :verbose => false)
- File.open(file[:path], 'w') do |f|
- f.write("#{file[:boilerplate]}\n" % [file[:name]]) unless file[:boilerplate].nil?
- f.write(file[:template] % [ file[:name],
- file[:includes].map{|f| "#include \"#{f}\"\n"}.join,
- file[:name].upcase ]
- )
- end
- if (@options[:update_svn])
- `svn add \"#{file[:path]}\"`
- if $?.exitstatus == 0
- puts "File #{file[:path]} created and added to source control"
- else
- puts "File #{file[:path]} created but FAILED adding to source control!"
- end
- else
- puts "File #{file[:path]} created"
- end
- end
- puts 'Generate Complete'
- end
- ############################
- def destroy(module_name, pattern=nil)
- files_to_operate_on(module_name, pattern).each do |filespec|
- file = filespec[:path]
- if File.exist?(file)
- if @options[:update_svn]
- `svn delete \"#{file}\" --force`
- puts "File #{file} deleted and removed from source control"
- else
- FileUtils.remove(file)
- puts "File #{file} deleted"
- end
- else
- puts "File #{file} does not exist so cannot be removed."
- end
- end
- puts "Destroy Complete"
- end
- end
- ############################
- #Handle As Command Line If Called That Way
- if ($0 == __FILE__)
- destroy = false
- options = { }
- module_name = nil
- # Parse the command line parameters.
- ARGV.each do |arg|
- case(arg)
- when /^-d/ then destroy = true
- when /^-u/ then options[:update_svn] = true
- when /^-p\"?(\w+)\"?/ then options[:pattern] = $1
- when /^-s\"?(.+)\"?/ then options[:path_src] = $1
- when /^-i\"?(.+)\"?/ then options[:path_inc] = $1
- when /^-t\"?(.+)\"?/ then options[:path_tst] = $1
- when /^-n\"?(.+)\"?/ then options[:naming] = $1
- when /^-y\"?(.+)\"?/ then options = UnityModuleGenerator.grab_config($1)
- when /^(\w+)/
- raise "ERROR: You can't have more than one Module name specified!" unless module_name.nil?
- module_name = arg
- when /^-(h|-help)/
- ARGV = []
- else
- raise "ERROR: Unknown option specified '#{arg}'"
- end
- end
- if (!ARGV[0])
- puts [ "\nGENERATE MODULE\n-------- ------",
- "\nUsage: ruby generate_module [options] module_name",
- " -i\"include\" sets the path to output headers to 'include' (DEFAULT ../src)",
- " -s\"../src\" sets the path to output source to '../src' (DEFAULT ../src)",
- " -t\"C:/test\" sets the path to output source to 'C:/test' (DEFAULT ../test)",
- " -p\"MCH\" sets the output pattern to MCH.",
- " dh - driver hardware.",
- " dih - driver interrupt hardware.",
- " mch - model conductor hardware.",
- " mvp - model view presenter.",
- " src - just a source module, header and test. (DEFAULT)",
- " test - just a test file.",
- " -d destroy module instead of creating it.",
- " -n\"camel\" sets the file naming convention.",
- " bumpy - BumpyCaseFilenames.",
- " camel - camelCaseFilenames.",
- " snake - snake_case_filenames. (DEFAULT)",
- " caps - CAPS_CASE_FILENAMES.",
- " -u update subversion too (requires subversion command line)",
- " -y\"my.yml\" selects a different yaml config file for module generation",
- "" ].join("\n")
- exit
- end
- raise "ERROR: You must have a Module name specified! (use option -h for help)" if module_name.nil?
- if (destroy)
- UnityModuleGenerator.new(options).destroy(module_name)
- else
- UnityModuleGenerator.new(options).generate(module_name)
- end
- end
|