19750e409Sopenharmony_ci# ==========================================
29750e409Sopenharmony_ci#   Unity Project - A Test Framework for C
39750e409Sopenharmony_ci#   Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams
49750e409Sopenharmony_ci#   [Released under MIT License. Please refer to license.txt for details]
59750e409Sopenharmony_ci# ==========================================
69750e409Sopenharmony_ci
79750e409Sopenharmony_ci# This script creates all the files with start code necessary for a new module.
89750e409Sopenharmony_ci# A simple module only requires a source file, header file, and test file.
99750e409Sopenharmony_ci# Triad modules require a source, header, and test file for each triad type (like model, conductor, and hardware).
109750e409Sopenharmony_ci
119750e409Sopenharmony_cirequire 'rubygems'
129750e409Sopenharmony_cirequire 'fileutils'
139750e409Sopenharmony_cirequire 'pathname'
149750e409Sopenharmony_ci
159750e409Sopenharmony_ci# TEMPLATE_TST
169750e409Sopenharmony_ciTEMPLATE_TST ||= '#include "unity.h"
179750e409Sopenharmony_ci%2$s#include "%1$s.h"
189750e409Sopenharmony_ci
199750e409Sopenharmony_civoid setUp(void)
209750e409Sopenharmony_ci{
219750e409Sopenharmony_ci}
229750e409Sopenharmony_ci
239750e409Sopenharmony_civoid tearDown(void)
249750e409Sopenharmony_ci{
259750e409Sopenharmony_ci}
269750e409Sopenharmony_ci
279750e409Sopenharmony_civoid test_%1$s_NeedToImplement(void)
289750e409Sopenharmony_ci{
299750e409Sopenharmony_ci    TEST_IGNORE_MESSAGE("Need to Implement %1$s");
309750e409Sopenharmony_ci}
319750e409Sopenharmony_ci'.freeze
329750e409Sopenharmony_ci
339750e409Sopenharmony_ci# TEMPLATE_SRC
349750e409Sopenharmony_ciTEMPLATE_SRC ||= '%2$s#include "%1$s.h"
359750e409Sopenharmony_ci'.freeze
369750e409Sopenharmony_ci
379750e409Sopenharmony_ci# TEMPLATE_INC
389750e409Sopenharmony_ciTEMPLATE_INC ||= '#ifndef _%3$s_H
399750e409Sopenharmony_ci#define _%3$s_H
409750e409Sopenharmony_ci%2$s
419750e409Sopenharmony_ci
429750e409Sopenharmony_ci#endif // _%3$s_H
439750e409Sopenharmony_ci'.freeze
449750e409Sopenharmony_ci
459750e409Sopenharmony_ciclass UnityModuleGenerator
469750e409Sopenharmony_ci  ############################
479750e409Sopenharmony_ci  def initialize(options = nil)
489750e409Sopenharmony_ci    here = File.expand_path(File.dirname(__FILE__)) + '/'
499750e409Sopenharmony_ci
509750e409Sopenharmony_ci    @options = UnityModuleGenerator.default_options
519750e409Sopenharmony_ci    case options
529750e409Sopenharmony_ci    when NilClass then @options
539750e409Sopenharmony_ci    when String   then @options.merge!(UnityModuleGenerator.grab_config(options))
549750e409Sopenharmony_ci    when Hash     then @options.merge!(options)
559750e409Sopenharmony_ci    else raise 'If you specify arguments, it should be a filename or a hash of options'
569750e409Sopenharmony_ci    end
579750e409Sopenharmony_ci
589750e409Sopenharmony_ci    # Create default file paths if none were provided
599750e409Sopenharmony_ci    @options[:path_src] = here + '../src/'    if @options[:path_src].nil?
609750e409Sopenharmony_ci    @options[:path_inc] = @options[:path_src] if @options[:path_inc].nil?
619750e409Sopenharmony_ci    @options[:path_tst] = here + '../test/'   if @options[:path_tst].nil?
629750e409Sopenharmony_ci    @options[:path_src] += '/'                unless @options[:path_src][-1] == 47
639750e409Sopenharmony_ci    @options[:path_inc] += '/'                unless @options[:path_inc][-1] == 47
649750e409Sopenharmony_ci    @options[:path_tst] += '/'                unless @options[:path_tst][-1] == 47
659750e409Sopenharmony_ci
669750e409Sopenharmony_ci    # Built in patterns
679750e409Sopenharmony_ci    @patterns = {
689750e409Sopenharmony_ci      'src'  =>  {
699750e409Sopenharmony_ci        '' =>  { inc: [] }
709750e409Sopenharmony_ci      },
719750e409Sopenharmony_ci      'test' =>  {
729750e409Sopenharmony_ci        '' =>  { inc: [] }
739750e409Sopenharmony_ci      },
749750e409Sopenharmony_ci      'dh'   =>  {
759750e409Sopenharmony_ci        'Driver'    =>  { inc: [create_filename('%1$s', 'Hardware.h')] },
769750e409Sopenharmony_ci        'Hardware'  =>  { inc: [] }
779750e409Sopenharmony_ci      },
789750e409Sopenharmony_ci      'dih'  =>  {
799750e409Sopenharmony_ci        'Driver'    =>  { inc: [create_filename('%1$s', 'Hardware.h'), create_filename('%1$s', 'Interrupt.h')] },
809750e409Sopenharmony_ci        'Interrupt' =>  { inc: [create_filename('%1$s', 'Hardware.h')] },
819750e409Sopenharmony_ci        'Hardware'  =>  { inc: [] }
829750e409Sopenharmony_ci      },
839750e409Sopenharmony_ci      'mch'  =>  {
849750e409Sopenharmony_ci        'Model'     =>  { inc: [] },
859750e409Sopenharmony_ci        'Conductor' =>  { inc: [create_filename('%1$s', 'Model.h'), create_filename('%1$s', 'Hardware.h')] },
869750e409Sopenharmony_ci        'Hardware'  =>  { inc: [] }
879750e409Sopenharmony_ci      },
889750e409Sopenharmony_ci      'mvp'  =>  {
899750e409Sopenharmony_ci        'Model'     =>  { inc: [] },
909750e409Sopenharmony_ci        'Presenter' =>  { inc: [create_filename('%1$s', 'Model.h'), create_filename('%1$s', 'View.h')] },
919750e409Sopenharmony_ci        'View'      =>  { inc: [] }
929750e409Sopenharmony_ci      }
939750e409Sopenharmony_ci    }
949750e409Sopenharmony_ci  end
959750e409Sopenharmony_ci
969750e409Sopenharmony_ci  ############################
979750e409Sopenharmony_ci  def self.default_options
989750e409Sopenharmony_ci    {
999750e409Sopenharmony_ci      pattern: 'src',
1009750e409Sopenharmony_ci      includes: {
1019750e409Sopenharmony_ci        src: [],
1029750e409Sopenharmony_ci        inc: [],
1039750e409Sopenharmony_ci        tst: []
1049750e409Sopenharmony_ci      },
1059750e409Sopenharmony_ci      update_svn: false,
1069750e409Sopenharmony_ci      boilerplates: {},
1079750e409Sopenharmony_ci      test_prefix: 'Test',
1089750e409Sopenharmony_ci      mock_prefix: 'Mock'
1099750e409Sopenharmony_ci    }
1109750e409Sopenharmony_ci  end
1119750e409Sopenharmony_ci
1129750e409Sopenharmony_ci  ############################
1139750e409Sopenharmony_ci  def self.grab_config(config_file)
1149750e409Sopenharmony_ci    options = default_options
1159750e409Sopenharmony_ci    unless config_file.nil? || config_file.empty?
1169750e409Sopenharmony_ci      require 'yaml'
1179750e409Sopenharmony_ci      yaml_guts = YAML.load_file(config_file)
1189750e409Sopenharmony_ci      options.merge!(yaml_guts[:unity] || yaml_guts[:cmock])
1199750e409Sopenharmony_ci      raise "No :unity or :cmock section found in #{config_file}" unless options
1209750e409Sopenharmony_ci    end
1219750e409Sopenharmony_ci    options
1229750e409Sopenharmony_ci  end
1239750e409Sopenharmony_ci
1249750e409Sopenharmony_ci  ############################
1259750e409Sopenharmony_ci  def files_to_operate_on(module_name, pattern = nil)
1269750e409Sopenharmony_ci    # strip any leading path information from the module name and save for later
1279750e409Sopenharmony_ci    subfolder = File.dirname(module_name)
1289750e409Sopenharmony_ci    module_name = File.basename(module_name)
1299750e409Sopenharmony_ci
1309750e409Sopenharmony_ci    # create triad definition
1319750e409Sopenharmony_ci    prefix = @options[:test_prefix] || 'Test'
1329750e409Sopenharmony_ci    triad = [{ ext: '.c', path: @options[:path_src], prefix: '', template: TEMPLATE_SRC, inc: :src, boilerplate: @options[:boilerplates][:src] },
1339750e409Sopenharmony_ci             { ext: '.h', path: @options[:path_inc], prefix: '',     template: TEMPLATE_INC, inc: :inc, boilerplate: @options[:boilerplates][:inc] },
1349750e409Sopenharmony_ci             { ext: '.c', path: @options[:path_tst], prefix: prefix, template: TEMPLATE_TST, inc: :tst, boilerplate: @options[:boilerplates][:tst] }]
1359750e409Sopenharmony_ci
1369750e409Sopenharmony_ci    # prepare the pattern for use
1379750e409Sopenharmony_ci    pattern = (pattern || @options[:pattern] || 'src').downcase
1389750e409Sopenharmony_ci    patterns = @patterns[pattern]
1399750e409Sopenharmony_ci    raise "ERROR: The design pattern '#{pattern}' specified isn't one that I recognize!" if patterns.nil?
1409750e409Sopenharmony_ci
1419750e409Sopenharmony_ci    # single file patterns (currently just 'test') can reject the other parts of the triad
1429750e409Sopenharmony_ci    triad.select! { |v| v[:inc] == :tst } if pattern == 'test'
1439750e409Sopenharmony_ci
1449750e409Sopenharmony_ci    # Assemble the path/names of the files we need to work with.
1459750e409Sopenharmony_ci    files = []
1469750e409Sopenharmony_ci    triad.each do |cfg|
1479750e409Sopenharmony_ci      patterns.each_pair do |pattern_file, pattern_traits|
1489750e409Sopenharmony_ci        submodule_name = create_filename(module_name, pattern_file)
1499750e409Sopenharmony_ci        filename = cfg[:prefix] + submodule_name + cfg[:ext]
1509750e409Sopenharmony_ci        files << {
1519750e409Sopenharmony_ci          path: (Pathname.new("#{cfg[:path]}#{subfolder}") + filename).cleanpath,
1529750e409Sopenharmony_ci          name: submodule_name,
1539750e409Sopenharmony_ci          template: cfg[:template],
1549750e409Sopenharmony_ci          boilerplate: cfg[:boilerplate],
1559750e409Sopenharmony_ci          includes: case (cfg[:inc])
1569750e409Sopenharmony_ci                    when :src then (@options[:includes][:src] || []) | (pattern_traits[:inc].map { |f| format(f, module_name) })
1579750e409Sopenharmony_ci                    when :inc then (@options[:includes][:inc] || [])
1589750e409Sopenharmony_ci                    when :tst then (@options[:includes][:tst] || []) | (pattern_traits[:inc].map { |f| format("#{@options[:mock_prefix]}#{f}", module_name) })
1599750e409Sopenharmony_ci                    end
1609750e409Sopenharmony_ci        }
1619750e409Sopenharmony_ci      end
1629750e409Sopenharmony_ci    end
1639750e409Sopenharmony_ci
1649750e409Sopenharmony_ci    files
1659750e409Sopenharmony_ci  end
1669750e409Sopenharmony_ci
1679750e409Sopenharmony_ci  ############################
1689750e409Sopenharmony_ci  def create_filename(part1, part2 = '')
1699750e409Sopenharmony_ci    if part2.empty?
1709750e409Sopenharmony_ci      case (@options[:naming])
1719750e409Sopenharmony_ci      when 'bumpy' then part1
1729750e409Sopenharmony_ci      when 'camel' then part1
1739750e409Sopenharmony_ci      when 'snake' then part1.downcase
1749750e409Sopenharmony_ci      when 'caps'  then part1.upcase
1759750e409Sopenharmony_ci      else              part1
1769750e409Sopenharmony_ci      end
1779750e409Sopenharmony_ci    else
1789750e409Sopenharmony_ci      case (@options[:naming])
1799750e409Sopenharmony_ci      when 'bumpy' then part1 + part2
1809750e409Sopenharmony_ci      when 'camel' then part1 + part2
1819750e409Sopenharmony_ci      when 'snake' then part1.downcase + '_' + part2.downcase
1829750e409Sopenharmony_ci      when 'caps'  then part1.upcase + '_' + part2.upcase
1839750e409Sopenharmony_ci      else              part1 + '_' + part2
1849750e409Sopenharmony_ci      end
1859750e409Sopenharmony_ci    end
1869750e409Sopenharmony_ci  end
1879750e409Sopenharmony_ci
1889750e409Sopenharmony_ci  ############################
1899750e409Sopenharmony_ci  def generate(module_name, pattern = nil)
1909750e409Sopenharmony_ci    files = files_to_operate_on(module_name, pattern)
1919750e409Sopenharmony_ci
1929750e409Sopenharmony_ci    # Abort if all of the module files already exist
1939750e409Sopenharmony_ci    all_files_exist = true
1949750e409Sopenharmony_ci    files.each do |file|
1959750e409Sopenharmony_ci      all_files_exist = false unless File.exist?(file[:path])
1969750e409Sopenharmony_ci    end
1979750e409Sopenharmony_ci    raise "ERROR: File #{files[0][:name]} already exists. Exiting." if all_files_exist
1989750e409Sopenharmony_ci
1999750e409Sopenharmony_ci    # Create Source Modules
2009750e409Sopenharmony_ci    files.each_with_index do |file, _i|
2019750e409Sopenharmony_ci      # If this file already exists, don't overwrite it.
2029750e409Sopenharmony_ci      if File.exist?(file[:path])
2039750e409Sopenharmony_ci        puts "File #{file[:path]} already exists!"
2049750e409Sopenharmony_ci        next
2059750e409Sopenharmony_ci      end
2069750e409Sopenharmony_ci      # Create the path first if necessary.
2079750e409Sopenharmony_ci      FileUtils.mkdir_p(File.dirname(file[:path]), verbose: false)
2089750e409Sopenharmony_ci      File.open(file[:path], 'w') do |f|
2099750e409Sopenharmony_ci        f.write("#{file[:boilerplate]}\n" % [file[:name]]) unless file[:boilerplate].nil?
2109750e409Sopenharmony_ci        f.write(file[:template] % [file[:name],
2119750e409Sopenharmony_ci                                   file[:includes].map { |ff| "#include \"#{ff}\"\n" }.join,
2129750e409Sopenharmony_ci                                   file[:name].upcase])
2139750e409Sopenharmony_ci      end
2149750e409Sopenharmony_ci      if @options[:update_svn]
2159750e409Sopenharmony_ci        `svn add \"#{file[:path]}\"`
2169750e409Sopenharmony_ci        if $!.exitstatus.zero?
2179750e409Sopenharmony_ci          puts "File #{file[:path]} created and added to source control"
2189750e409Sopenharmony_ci        else
2199750e409Sopenharmony_ci          puts "File #{file[:path]} created but FAILED adding to source control!"
2209750e409Sopenharmony_ci        end
2219750e409Sopenharmony_ci      else
2229750e409Sopenharmony_ci        puts "File #{file[:path]} created"
2239750e409Sopenharmony_ci      end
2249750e409Sopenharmony_ci    end
2259750e409Sopenharmony_ci    puts 'Generate Complete'
2269750e409Sopenharmony_ci  end
2279750e409Sopenharmony_ci
2289750e409Sopenharmony_ci  ############################
2299750e409Sopenharmony_ci  def destroy(module_name, pattern = nil)
2309750e409Sopenharmony_ci    files_to_operate_on(module_name, pattern).each do |filespec|
2319750e409Sopenharmony_ci      file = filespec[:path]
2329750e409Sopenharmony_ci      if File.exist?(file)
2339750e409Sopenharmony_ci        if @options[:update_svn]
2349750e409Sopenharmony_ci          `svn delete \"#{file}\" --force`
2359750e409Sopenharmony_ci          puts "File #{file} deleted and removed from source control"
2369750e409Sopenharmony_ci        else
2379750e409Sopenharmony_ci          FileUtils.remove(file)
2389750e409Sopenharmony_ci          puts "File #{file} deleted"
2399750e409Sopenharmony_ci        end
2409750e409Sopenharmony_ci      else
2419750e409Sopenharmony_ci        puts "File #{file} does not exist so cannot be removed."
2429750e409Sopenharmony_ci      end
2439750e409Sopenharmony_ci    end
2449750e409Sopenharmony_ci    puts 'Destroy Complete'
2459750e409Sopenharmony_ci  end
2469750e409Sopenharmony_ciend
2479750e409Sopenharmony_ci
2489750e409Sopenharmony_ci############################
2499750e409Sopenharmony_ci# Handle As Command Line If Called That Way
2509750e409Sopenharmony_ciif $0 == __FILE__
2519750e409Sopenharmony_ci  destroy = false
2529750e409Sopenharmony_ci  options = {}
2539750e409Sopenharmony_ci  module_name = nil
2549750e409Sopenharmony_ci
2559750e409Sopenharmony_ci  # Parse the command line parameters.
2569750e409Sopenharmony_ci  ARGV.each do |arg|
2579750e409Sopenharmony_ci    case arg
2589750e409Sopenharmony_ci    when /^-d/            then destroy = true
2599750e409Sopenharmony_ci    when /^-u/            then options[:update_svn] = true
2609750e409Sopenharmony_ci    when /^-p\"?(\w+)\"?/ then options[:pattern] = Regexp.last_match(1)
2619750e409Sopenharmony_ci    when /^-s\"?(.+)\"?/  then options[:path_src] = Regexp.last_match(1)
2629750e409Sopenharmony_ci    when /^-i\"?(.+)\"?/  then options[:path_inc] = Regexp.last_match(1)
2639750e409Sopenharmony_ci    when /^-t\"?(.+)\"?/  then options[:path_tst] = Regexp.last_match(1)
2649750e409Sopenharmony_ci    when /^-n\"?(.+)\"?/  then options[:naming] = Regexp.last_match(1)
2659750e409Sopenharmony_ci    when /^-y\"?(.+)\"?/  then options = UnityModuleGenerator.grab_config(Regexp.last_match(1))
2669750e409Sopenharmony_ci    when /^(\w+)/
2679750e409Sopenharmony_ci      raise "ERROR: You can't have more than one Module name specified!" unless module_name.nil?
2689750e409Sopenharmony_ci      module_name = arg
2699750e409Sopenharmony_ci    when /^-(h|-help)/
2709750e409Sopenharmony_ci      ARGV = [].freeze
2719750e409Sopenharmony_ci    else
2729750e409Sopenharmony_ci      raise "ERROR: Unknown option specified '#{arg}'"
2739750e409Sopenharmony_ci    end
2749750e409Sopenharmony_ci  end
2759750e409Sopenharmony_ci
2769750e409Sopenharmony_ci  unless ARGV[0]
2779750e409Sopenharmony_ci    puts ["\nGENERATE MODULE\n-------- ------",
2789750e409Sopenharmony_ci          "\nUsage: ruby generate_module [options] module_name",
2799750e409Sopenharmony_ci          "  -i\"include\" sets the path to output headers to 'include' (DEFAULT ../src)",
2809750e409Sopenharmony_ci          "  -s\"../src\"  sets the path to output source to '../src'   (DEFAULT ../src)",
2819750e409Sopenharmony_ci          "  -t\"C:/test\" sets the path to output source to 'C:/test'  (DEFAULT ../test)",
2829750e409Sopenharmony_ci          '  -p"MCH"     sets the output pattern to MCH.',
2839750e409Sopenharmony_ci          '              dh   - driver hardware.',
2849750e409Sopenharmony_ci          '              dih  - driver interrupt hardware.',
2859750e409Sopenharmony_ci          '              mch  - model conductor hardware.',
2869750e409Sopenharmony_ci          '              mvp  - model view presenter.',
2879750e409Sopenharmony_ci          '              src  - just a source module, header and test. (DEFAULT)',
2889750e409Sopenharmony_ci          '              test - just a test file.',
2899750e409Sopenharmony_ci          '  -d          destroy module instead of creating it.',
2909750e409Sopenharmony_ci          '  -n"camel"   sets the file naming convention.',
2919750e409Sopenharmony_ci          '              bumpy - BumpyCaseFilenames.',
2929750e409Sopenharmony_ci          '              camel - camelCaseFilenames.',
2939750e409Sopenharmony_ci          '              snake - snake_case_filenames.',
2949750e409Sopenharmony_ci          '              caps  - CAPS_CASE_FILENAMES.',
2959750e409Sopenharmony_ci          '  -u          update subversion too (requires subversion command line)',
2969750e409Sopenharmony_ci          '  -y"my.yml"  selects a different yaml config file for module generation',
2979750e409Sopenharmony_ci          ''].join("\n")
2989750e409Sopenharmony_ci    exit
2999750e409Sopenharmony_ci  end
3009750e409Sopenharmony_ci
3019750e409Sopenharmony_ci  raise 'ERROR: You must have a Module name specified! (use option -h for help)' if module_name.nil?
3029750e409Sopenharmony_ci  if destroy
3039750e409Sopenharmony_ci    UnityModuleGenerator.new(options).destroy(module_name)
3049750e409Sopenharmony_ci  else
3059750e409Sopenharmony_ci    UnityModuleGenerator.new(options).generate(module_name)
3069750e409Sopenharmony_ci  end
3079750e409Sopenharmony_ci
3089750e409Sopenharmony_ciend
309