rakefile_helper.rb 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. require 'yaml'
  2. require 'fileutils'
  3. require UNITY_ROOT + '/auto/unity_test_summary'
  4. require UNITY_ROOT + '/auto/generate_test_runner'
  5. require UNITY_ROOT + '/auto/colour_reporter'
  6. module RakefileHelpers
  7. C_EXTENSION = '.c'.freeze
  8. def load_configuration(config_file)
  9. $cfg_file = config_file
  10. $cfg = YAML.load(File.read($cfg_file))
  11. end
  12. def configure_clean
  13. CLEAN.include($cfg['compiler']['build_path'] + '*.*') unless $cfg['compiler']['build_path'].nil?
  14. end
  15. def configure_toolchain(config_file = DEFAULT_CONFIG_FILE)
  16. config_file += '.yml' unless config_file =~ /\.yml$/
  17. load_configuration(config_file)
  18. configure_clean
  19. end
  20. def unit_test_files
  21. path = $cfg['compiler']['unit_tests_path'] + 'Test*' + C_EXTENSION
  22. path.tr!('\\', '/')
  23. FileList.new(path)
  24. end
  25. def local_include_dirs
  26. include_dirs = $cfg['compiler']['includes']['items'].dup
  27. include_dirs.delete_if { |dir| dir.is_a?(Array) }
  28. include_dirs
  29. end
  30. def extract_headers(filename)
  31. includes = []
  32. lines = File.readlines(filename)
  33. lines.each do |line|
  34. m = line.match(/^\s*#include\s+\"\s*(.+\.[hH])\s*\"/)
  35. includes << m[1] unless m.nil?
  36. end
  37. includes
  38. end
  39. def find_source_file(header, paths)
  40. paths.each do |dir|
  41. src_file = dir + header.ext(C_EXTENSION)
  42. return src_file if File.exist?(src_file)
  43. end
  44. nil
  45. end
  46. def tackit(strings)
  47. result = if strings.is_a?(Array)
  48. "\"#{strings.join}\""
  49. else
  50. strings
  51. end
  52. result
  53. end
  54. def squash(prefix, items)
  55. result = ''
  56. items.each { |item| result += " #{prefix}#{tackit(item)}" }
  57. result
  58. end
  59. def build_compiler_fields
  60. command = tackit($cfg['compiler']['path'])
  61. defines = if $cfg['compiler']['defines']['items'].nil?
  62. ''
  63. else
  64. squash($cfg['compiler']['defines']['prefix'], $cfg['compiler']['defines']['items'])
  65. end
  66. options = squash('', $cfg['compiler']['options'])
  67. includes = squash($cfg['compiler']['includes']['prefix'], $cfg['compiler']['includes']['items'])
  68. includes = includes.gsub(/\\ /, ' ').gsub(/\\\"/, '"').gsub(/\\$/, '') # Remove trailing slashes (for IAR)
  69. { command: command, defines: defines, options: options, includes: includes }
  70. end
  71. def compile(file, _defines = [])
  72. compiler = build_compiler_fields
  73. cmd_str = "#{compiler[:command]}#{compiler[:defines]}#{compiler[:options]}#{compiler[:includes]} #{file} " \
  74. "#{$cfg['compiler']['object_files']['prefix']}#{$cfg['compiler']['object_files']['destination']}"
  75. obj_file = "#{File.basename(file, C_EXTENSION)}#{$cfg['compiler']['object_files']['extension']}"
  76. execute(cmd_str + obj_file)
  77. obj_file
  78. end
  79. def build_linker_fields
  80. command = tackit($cfg['linker']['path'])
  81. options = if $cfg['linker']['options'].nil?
  82. ''
  83. else
  84. squash('', $cfg['linker']['options'])
  85. end
  86. includes = if $cfg['linker']['includes'].nil? || $cfg['linker']['includes']['items'].nil?
  87. ''
  88. else
  89. squash($cfg['linker']['includes']['prefix'], $cfg['linker']['includes']['items'])
  90. end.gsub(/\\ /, ' ').gsub(/\\\"/, '"').gsub(/\\$/, '') # Remove trailing slashes (for IAR)
  91. { command: command, options: options, includes: includes }
  92. end
  93. def link_it(exe_name, obj_list)
  94. linker = build_linker_fields
  95. cmd_str = "#{linker[:command]}#{linker[:options]}#{linker[:includes]} " +
  96. (obj_list.map { |obj| "#{$cfg['linker']['object_files']['path']}#{obj} " }).join +
  97. $cfg['linker']['bin_files']['prefix'] + ' ' +
  98. $cfg['linker']['bin_files']['destination'] +
  99. exe_name + $cfg['linker']['bin_files']['extension']
  100. execute(cmd_str)
  101. end
  102. def build_simulator_fields
  103. return nil if $cfg['simulator'].nil?
  104. command = if $cfg['simulator']['path'].nil?
  105. ''
  106. else
  107. (tackit($cfg['simulator']['path']) + ' ')
  108. end
  109. pre_support = if $cfg['simulator']['pre_support'].nil?
  110. ''
  111. else
  112. squash('', $cfg['simulator']['pre_support'])
  113. end
  114. post_support = if $cfg['simulator']['post_support'].nil?
  115. ''
  116. else
  117. squash('', $cfg['simulator']['post_support'])
  118. end
  119. { command: command, pre_support: pre_support, post_support: post_support }
  120. end
  121. def execute(command_string, verbose = true, raise_on_fail = true)
  122. report command_string
  123. output = `#{command_string}`.chomp
  124. report(output) if verbose && !output.nil? && !output.empty?
  125. if !$?.exitstatus.zero? && raise_on_fail
  126. raise "Command failed. (Returned #{$?.exitstatus})"
  127. end
  128. output
  129. end
  130. def report_summary
  131. summary = UnityTestSummary.new
  132. summary.root = HERE
  133. results_glob = "#{$cfg['compiler']['build_path']}*.test*"
  134. results_glob.tr!('\\', '/')
  135. results = Dir[results_glob]
  136. summary.targets = results
  137. summary.run
  138. fail_out 'FAIL: There were failures' if summary.failures > 0
  139. end
  140. def run_tests(test_files)
  141. report 'Running system tests...'
  142. # Tack on TEST define for compiling unit tests
  143. load_configuration($cfg_file)
  144. test_defines = ['TEST']
  145. $cfg['compiler']['defines']['items'] = [] if $cfg['compiler']['defines']['items'].nil?
  146. $cfg['compiler']['defines']['items'] << 'TEST'
  147. include_dirs = local_include_dirs
  148. # Build and execute each unit test
  149. test_files.each do |test|
  150. obj_list = []
  151. # Detect dependencies and build required required modules
  152. extract_headers(test).each do |header|
  153. # Compile corresponding source file if it exists
  154. src_file = find_source_file(header, include_dirs)
  155. obj_list << compile(src_file, test_defines) unless src_file.nil?
  156. end
  157. # Build the test runner (generate if configured to do so)
  158. test_base = File.basename(test, C_EXTENSION)
  159. runner_name = test_base + '_Runner.c'
  160. if $cfg['compiler']['runner_path'].nil?
  161. runner_path = $cfg['compiler']['build_path'] + runner_name
  162. test_gen = UnityTestRunnerGenerator.new($cfg_file)
  163. test_gen.run(test, runner_path)
  164. else
  165. runner_path = $cfg['compiler']['runner_path'] + runner_name
  166. end
  167. obj_list << compile(runner_path, test_defines)
  168. # Build the test module
  169. obj_list << compile(test, test_defines)
  170. # Link the test executable
  171. link_it(test_base, obj_list)
  172. # Execute unit test and generate results file
  173. simulator = build_simulator_fields
  174. executable = $cfg['linker']['bin_files']['destination'] + test_base + $cfg['linker']['bin_files']['extension']
  175. cmd_str = if simulator.nil?
  176. executable
  177. else
  178. "#{simulator[:command]} #{simulator[:pre_support]} #{executable} #{simulator[:post_support]}"
  179. end
  180. output = execute(cmd_str, true, false)
  181. test_results = $cfg['compiler']['build_path'] + test_base
  182. test_results += if output.match(/OK$/m).nil?
  183. '.testfail'
  184. else
  185. '.testpass'
  186. end
  187. File.open(test_results, 'w') { |f| f.print output }
  188. end
  189. end
  190. def build_application(main)
  191. report 'Building application...'
  192. obj_list = []
  193. load_configuration($cfg_file)
  194. main_path = $cfg['compiler']['source_path'] + main + C_EXTENSION
  195. # Detect dependencies and build required required modules
  196. include_dirs = get_local_include_dirs
  197. extract_headers(main_path).each do |header|
  198. src_file = find_source_file(header, include_dirs)
  199. obj_list << compile(src_file) unless src_file.nil?
  200. end
  201. # Build the main source file
  202. main_base = File.basename(main_path, C_EXTENSION)
  203. obj_list << compile(main_path)
  204. # Create the executable
  205. link_it(main_base, obj_list)
  206. end
  207. def fail_out(msg)
  208. puts msg
  209. puts 'Not returning exit code so continuous integration can pass'
  210. # exit(-1) # Only removed to pass example_3, which has failing tests on purpose.
  211. # Still fail if the build fails for any other reason.
  212. end
  213. end