rakefile_helper.rb 7.8 KB
Newer Older
W
Warwick Stone 已提交
1 2
require 'yaml'
require 'fileutils'
J
John Lindgren 已提交
3 4 5
require_relative '../../auto/unity_test_summary'
require_relative '../../auto/generate_test_runner'
require_relative '../../auto/colour_reporter'
W
Warwick Stone 已提交
6

M
mvandervoord 已提交
7
C_EXTENSION = '.c'.freeze
W
Warwick Stone 已提交
8

M
mvandervoord 已提交
9 10 11 12
def load_configuration(config_file)
  $cfg_file = config_file
  $cfg = YAML.load(File.read($cfg_file))
end
W
Warwick Stone 已提交
13

M
mvandervoord 已提交
14 15 16
def configure_clean
  CLEAN.include($cfg['compiler']['build_path'] + '*.*') unless $cfg['compiler']['build_path'].nil?
end
W
Warwick Stone 已提交
17

M
mvandervoord 已提交
18 19 20 21 22
def configure_toolchain(config_file = DEFAULT_CONFIG_FILE)
  config_file += '.yml' unless config_file =~ /\.yml$/
  load_configuration(config_file)
  configure_clean
end
W
Warwick Stone 已提交
23

M
mvandervoord 已提交
24 25 26 27 28
def unit_test_files
  path = $cfg['compiler']['unit_tests_path'] + 'Test*' + C_EXTENSION
  path.tr!('\\', '/')
  FileList.new(path)
end
W
Warwick Stone 已提交
29

M
mvandervoord 已提交
30 31 32 33 34
def local_include_dirs
  include_dirs = $cfg['compiler']['includes']['items'].dup
  include_dirs.delete_if { |dir| dir.is_a?(Array) }
  include_dirs
end
W
Warwick Stone 已提交
35

M
mvandervoord 已提交
36 37 38 39 40 41
def extract_headers(filename)
  includes = []
  lines = File.readlines(filename)
  lines.each do |line|
    m = line.match(/^\s*#include\s+\"\s*(.+\.[hH])\s*\"/)
    includes << m[1] unless m.nil?
W
Warwick Stone 已提交
42
  end
M
mvandervoord 已提交
43 44
  includes
end
W
Warwick Stone 已提交
45

M
mvandervoord 已提交
46 47 48 49
def find_source_file(header, paths)
  paths.each do |dir|
    src_file = dir + header.ext(C_EXTENSION)
    return src_file if File.exist?(src_file)
W
Warwick Stone 已提交
50
  end
M
mvandervoord 已提交
51 52 53 54 55 56 57 58 59 60 61
  nil
end

def tackit(strings)
  result = if strings.is_a?(Array)
             "\"#{strings.join}\""
           else
             strings
           end
  result
end
W
Warwick Stone 已提交
62

M
mvandervoord 已提交
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
def squash(prefix, items)
  result = ''
  items.each { |item| result += " #{prefix}#{tackit(item)}" }
  result
end

def build_compiler_fields
  command = tackit($cfg['compiler']['path'])
  defines = if $cfg['compiler']['defines']['items'].nil?
              ''
            else
              squash($cfg['compiler']['defines']['prefix'], $cfg['compiler']['defines']['items'])
            end
  options = squash('', $cfg['compiler']['options'])
  includes = squash($cfg['compiler']['includes']['prefix'], $cfg['compiler']['includes']['items'])
  includes = includes.gsub(/\\ /, ' ').gsub(/\\\"/, '"').gsub(/\\$/, '') # Remove trailing slashes (for IAR)

  { command: command, defines: defines, options: options, includes: includes }
end

def compile(file, _defines = [])
  compiler = build_compiler_fields
  cmd_str  = "#{compiler[:command]}#{compiler[:defines]}#{compiler[:options]}#{compiler[:includes]} #{file} " \
             "#{$cfg['compiler']['object_files']['prefix']}#{$cfg['compiler']['object_files']['destination']}"
  obj_file = "#{File.basename(file, C_EXTENSION)}#{$cfg['compiler']['object_files']['extension']}"
  execute(cmd_str + obj_file)
  obj_file
end

def build_linker_fields
  command = tackit($cfg['linker']['path'])
  options = if $cfg['linker']['options'].nil?
              ''
            else
              squash('', $cfg['linker']['options'])
            end
  includes = if $cfg['linker']['includes'].nil? || $cfg['linker']['includes']['items'].nil?
               ''
101
             else
M
mvandervoord 已提交
102 103
               squash($cfg['linker']['includes']['prefix'], $cfg['linker']['includes']['items'])
             end.gsub(/\\ /, ' ').gsub(/\\\"/, '"').gsub(/\\$/, '') # Remove trailing slashes (for IAR)
W
Warwick Stone 已提交
104

M
mvandervoord 已提交
105 106
  { command: command, options: options, includes: includes }
end
W
Warwick Stone 已提交
107

M
mvandervoord 已提交
108 109 110 111 112 113 114 115 116
def link_it(exe_name, obj_list)
  linker = build_linker_fields
  cmd_str = "#{linker[:command]}#{linker[:options]}#{linker[:includes]} " +
            (obj_list.map { |obj| "#{$cfg['linker']['object_files']['path']}#{obj} " }).join +
            $cfg['linker']['bin_files']['prefix'] + ' ' +
            $cfg['linker']['bin_files']['destination'] +
            exe_name + $cfg['linker']['bin_files']['extension']
  execute(cmd_str)
end
117

M
mvandervoord 已提交
118 119
def build_simulator_fields
  return nil if $cfg['simulator'].nil?
W
Warwick Stone 已提交
120

M
mvandervoord 已提交
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
  command = if $cfg['simulator']['path'].nil?
              ''
            else
              (tackit($cfg['simulator']['path']) + ' ')
            end
  pre_support = if $cfg['simulator']['pre_support'].nil?
                  ''
                else
                  squash('', $cfg['simulator']['pre_support'])
                end
  post_support = if $cfg['simulator']['post_support'].nil?
                   ''
                 else
                   squash('', $cfg['simulator']['post_support'])
                 end
W
Warwick Stone 已提交
136

M
mvandervoord 已提交
137 138
  { command: command, pre_support: pre_support, post_support: post_support }
end
139

M
mvandervoord 已提交
140 141 142 143
def execute(command_string, verbose = true, raise_on_fail = true)
  report command_string
  output = `#{command_string}`.chomp
  report(output) if verbose && !output.nil? && !output.empty?
W
Warwick Stone 已提交
144

M
mvandervoord 已提交
145 146
  if !$?.nil? && !$?.exitstatus.zero? && raise_on_fail
    raise "Command failed. (Returned #{$?.exitstatus})"
W
Warwick Stone 已提交
147 148
  end

M
mvandervoord 已提交
149 150
  output
end
W
Warwick Stone 已提交
151

M
mvandervoord 已提交
152 153 154 155 156 157 158 159 160 161
def report_summary
  summary = UnityTestSummary.new
  summary.root = __dir__
  results_glob = "#{$cfg['compiler']['build_path']}*.test*"
  results_glob.tr!('\\', '/')
  results = Dir[results_glob]
  summary.targets = results
  summary.run
  fail_out 'FAIL: There were failures' if summary.failures > 0
end
W
Warwick Stone 已提交
162

M
mvandervoord 已提交
163 164
def run_tests(test_files)
  report 'Running system tests...'
W
Warwick Stone 已提交
165

M
mvandervoord 已提交
166 167 168 169 170
  # Tack on TEST define for compiling unit tests
  load_configuration($cfg_file)
  test_defines = ['TEST']
  $cfg['compiler']['defines']['items'] = [] if $cfg['compiler']['defines']['items'].nil?
  $cfg['compiler']['defines']['items'] << 'TEST'
W
Warwick Stone 已提交
171

M
mvandervoord 已提交
172
  include_dirs = local_include_dirs
W
Warwick Stone 已提交
173

M
mvandervoord 已提交
174 175
  # Build and execute each unit test
  test_files.each do |test|
W
Warwick Stone 已提交
176 177 178
    obj_list = []

    # Detect dependencies and build required required modules
M
mvandervoord 已提交
179 180
    extract_headers(test).each do |header|
      # Compile corresponding source file if it exists
W
Warwick Stone 已提交
181
      src_file = find_source_file(header, include_dirs)
M
mvandervoord 已提交
182 183 184 185 186 187 188 189 190 191 192 193
      obj_list << compile(src_file, test_defines) unless src_file.nil?
    end

    # Build the test runner (generate if configured to do so)
    test_base = File.basename(test, C_EXTENSION)
    runner_name = test_base + '_Runner.c'
    if $cfg['compiler']['runner_path'].nil?
      runner_path = $cfg['compiler']['build_path'] + runner_name
      test_gen = UnityTestRunnerGenerator.new($cfg_file)
      test_gen.run(test, runner_path)
    else
      runner_path = $cfg['compiler']['runner_path'] + runner_name
W
Warwick Stone 已提交
194 195
    end

M
mvandervoord 已提交
196
    obj_list << compile(runner_path, test_defines)
W
Warwick Stone 已提交
197

M
mvandervoord 已提交
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
    # Build the test module
    obj_list << compile(test, test_defines)

    # Link the test executable
    link_it(test_base, obj_list)

    # Execute unit test and generate results file
    simulator = build_simulator_fields
    executable = $cfg['linker']['bin_files']['destination'] + test_base + $cfg['linker']['bin_files']['extension']
    cmd_str = if simulator.nil?
                executable
              else
                "#{simulator[:command]} #{simulator[:pre_support]} #{executable} #{simulator[:post_support]}"
              end
    output = execute(cmd_str, true, false)
    test_results = $cfg['compiler']['build_path'] + test_base
    test_results += if output.match(/OK$/m).nil?
                      '.testfail'
                    else
                      '.testpass'
                    end
    File.open(test_results, 'w') { |f| f.print output }
W
Warwick Stone 已提交
220
  end
M
mvandervoord 已提交
221 222 223 224
end

def build_application(main)
  report 'Building application...'
W
Warwick Stone 已提交
225

M
mvandervoord 已提交
226 227 228 229 230 231 232 233 234
  obj_list = []
  load_configuration($cfg_file)
  main_path = $cfg['compiler']['source_path'] + main + C_EXTENSION

  # Detect dependencies and build required required modules
  include_dirs = get_local_include_dirs
  extract_headers(main_path).each do |header|
    src_file = find_source_file(header, include_dirs)
    obj_list << compile(src_file) unless src_file.nil?
W
Warwick Stone 已提交
235
  end
M
mvandervoord 已提交
236 237 238 239 240 241 242 243 244 245 246 247 248 249

  # Build the main source file
  main_base = File.basename(main_path, C_EXTENSION)
  obj_list << compile(main_path)

  # Create the executable
  link_it(main_base, obj_list)
end

def fail_out(msg)
  puts msg
  puts 'Not returning exit code so continuous integration can pass'
  #    exit(-1) # Only removed to pass example_3, which has failing tests on purpose.
  #               Still fail if the build fails for any other reason.
W
Warwick Stone 已提交
250
end