generate_module.rb 10.6 KB
Newer Older
M
Mark VanderVoord 已提交
1 2 3 4
# ==========================================
#   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]
5
# ==========================================
M
Mark VanderVoord 已提交
6 7 8 9 10 11 12 13 14

# 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'

#TEMPLATE_TST
15
TEMPLATE_TST ||= %q[#include "unity.h"
M
Mark VanderVoord 已提交
16 17 18 19 20 21 22 23 24 25 26 27
%2$s#include "%1$s.h"

void setUp(void)
{
}

void tearDown(void)
{
}

void test_%1$s_NeedToImplement(void)
{
28
    TEST_IGNORE_MESSAGE("Need to Implement %1$s");
M
Mark VanderVoord 已提交
29 30 31 32
}
]

#TEMPLATE_SRC
33
TEMPLATE_SRC ||= %q[%2$s#include "%1$s.h"
M
Mark VanderVoord 已提交
34 35 36
]

#TEMPLATE_INC
37
TEMPLATE_INC ||= %q[#ifndef _%3$s_H
M
Mark VanderVoord 已提交
38 39
#define _%3$s_H
%2$s
M
Mark VanderVoord 已提交
40 41 42 43

#endif // _%3$s_H
]

44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
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 => [] } },
M
Mark VanderVoord 已提交
69 70
                  'test'=> {''         => { :inc => [] } },
                  'dh'  => {'Driver'   => { :inc => [create_filename('%1$s','Hardware.h')] },
71 72
                            'Hardware' => { :inc => [] }
                           },
M
Mark VanderVoord 已提交
73 74
                  'dih' => {'Driver'   => { :inc => [create_filename('%1$s','Hardware.h'), create_filename('%1$s','Interrupt.h')] },
                            'Interrupt'=> { :inc => [create_filename('%1$s','Hardware.h')] },
75 76 77
                            'Hardware' => { :inc => [] }
                           },
                  'mch' => {'Model'    => { :inc => [] },
M
Mark VanderVoord 已提交
78
                            'Conductor'=> { :inc => [create_filename('%1$s','Model.h'), create_filename('%1$s','Hardware.h')] },
79 80 81
                            'Hardware' => { :inc => [] }
                           },
                  'mvp' => {'Model'    => { :inc => [] },
M
Mark VanderVoord 已提交
82
                            'Presenter'=> { :inc => [create_filename('%1$s','Model.h'), create_filename('%1$s','View.h')] },
83 84 85
                            'View'     => { :inc => [] }
                           }
                }
M
Mark VanderVoord 已提交
86 87
  end

88 89 90 91 92 93 94 95 96 97 98 99 100
  ############################
  def self.default_options
    {
      :pattern         => "src",
      :includes        =>
      {
          :src         => [],
          :inc         => [],
          :tst         => [],
      },
      :update_svn      => false,
      :boilerplates    => {},
      :test_prefix     => 'Test',
101
      :mock_prefix     => 'Mock',
M
Mark VanderVoord 已提交
102 103 104
    }
  end

105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
  ############################
  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)
    #create triad definition
    prefix = @options[:test_prefix] || 'Test'
    triad = [ { :ext => '.c', :path => @options[:path_src],        :template => TEMPLATE_SRC, :inc => :src, :boilerplate => @options[:boilerplates][:src] },
              { :ext => '.h', :path => @options[:path_inc],        :template => TEMPLATE_INC, :inc => :inc, :boilerplate => @options[:boilerplates][:inc] },
              { :ext => '.c', :path => @options[:path_tst]+prefix, :template => TEMPLATE_TST, :inc => :tst, :boilerplate => @options[:boilerplates][:tst] },
            ]

    #prepare the pattern for use
M
Mark VanderVoord 已提交
127 128
    pattern = (pattern || @options[:pattern] || 'src').downcase
    patterns = @patterns[pattern]
129 130
    raise "ERROR: The design pattern '#{pattern}' specified isn't one that I recognize!" if patterns.nil?

M
Mark VanderVoord 已提交
131 132 133 134 135
    #single file patterns (currently just 'test') can reject the other parts of the triad
    if (pattern == 'test')
      triad.reject!{|v| v[:inc] != :tst }
    end

136 137
    # Assemble the path/names of the files we need to work with.
    files = []
M
Mark VanderVoord 已提交
138
    triad.each do |cfg|
139
      patterns.each_pair do |pattern_file, pattern_traits|
M
Mark VanderVoord 已提交
140
        submodule_name = create_filename(module_name, pattern_file)
141
        files << {
M
Mark VanderVoord 已提交
142 143 144 145 146 147 148 149
          :path => "#{cfg[:path]}#{submodule_name}#{cfg[:ext]}",
          :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]}
150 151 152 153 154 155 156 157
                       end
        }
      end
    end

    return files
  end

M
Mark VanderVoord 已提交
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
  ############################
  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

179 180 181 182 183 184 185 186 187 188 189 190 191
  ############################
  def generate(module_name, pattern=nil)

    files = files_to_operate_on(module_name, pattern)

    #Abort if any module already exists
    files.each do |file|
      raise "ERROR: File #{file[:name]} already exists. Exiting." if File.exist?(file[:path])
    end

    # Create Source Modules
    files.each_with_index do |file, i|
      File.open(file[:path], 'w') do |f|
M
Mark VanderVoord 已提交
192
        f.write("#{file[:boilerplate]}\n" % [file[:name]]) unless file[:boilerplate].nil?
193 194 195 196 197 198 199 200 201 202 203 204
        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
M
Mark VanderVoord 已提交
205
      else
206
        puts "File #{file[:path]} created"
M
Mark VanderVoord 已提交
207 208
      end
    end
209
    puts 'Generate Complete'
M
Mark VanderVoord 已提交
210 211
  end

212 213
  ############################
  def destroy(module_name, pattern=nil)
M
Mark VanderVoord 已提交
214

215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
    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"
M
Mark VanderVoord 已提交
230
  end
231 232 233 234 235 236 237 238 239 240 241 242 243

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)
M
Mark VanderVoord 已提交
244 245 246 247 248 249 250 251
      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)
252 253 254 255 256 257 258
      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}'"
M
Mark VanderVoord 已提交
259
    end
260 261 262 263 264 265 266 267 268
  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.",
M
Mark VanderVoord 已提交
269 270 271 272 273 274
           "              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.",
275
           "  -d          destroy module instead of creating it.",
M
Mark VanderVoord 已提交
276 277 278 279 280
           "  -n\"camel\"   sets the file naming convention.",
           "              bumpy - BumpyCaseFilenames.",
           "              camel - camelCaseFilenames.",
           "              snake - snake_case_filenames. (DEFAULT)",
           "              caps  - CAPS_CASE_FILENAMES.",
281 282 283 284 285 286 287 288 289
           "  -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)
M
Mark VanderVoord 已提交
290
  else
291
    UnityModuleGenerator.new(options).generate(module_name)
M
Mark VanderVoord 已提交
292
  end
293

M
Mark VanderVoord 已提交
294 295
end

296