ceedling 0.29.0 → 0.29.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/assets/project_as_gem.yml +2 -5
  3. data/assets/project_with_guts.yml +2 -5
  4. data/assets/project_with_guts_gcov.yml +2 -5
  5. data/bin/ceedling +14 -12
  6. data/ceedling.sublime-project +26 -23
  7. data/docs/CeedlingPacket.md +17 -2
  8. data/docs/CeedlingUpgrade.md +83 -0
  9. data/lib/ceedling/build_invoker_utils.rb +1 -1
  10. data/lib/ceedling/cacheinator_helper.rb +2 -2
  11. data/lib/ceedling/dependinator.rb +2 -2
  12. data/lib/ceedling/generator_test_results.rb +13 -2
  13. data/lib/ceedling/tasks_base.rake +17 -23
  14. data/lib/ceedling/tasks_filesystem.rake +12 -3
  15. data/lib/ceedling/tasks_tests.rake +1 -1
  16. data/lib/ceedling/test_invoker.rb +1 -1
  17. data/lib/ceedling/version.rb +28 -14
  18. data/plugins/gcov/gcov.rake +3 -3
  19. data/plugins/junit_tests_report/lib/junit_tests_report.rb +4 -3
  20. data/spec/spec_system_helper.rb +42 -2
  21. data/spec/support/test_example.fail +2 -0
  22. data/spec/support/test_example.pass +2 -0
  23. data/spec/support/test_example_ignore.pass +2 -0
  24. data/spec/support/test_example_mangled.pass +1 -0
  25. data/spec/support/test_example_with_time.pass +2 -0
  26. data/spec/system/deployment_spec.rb +6 -0
  27. data/vendor/cmock/docs/CMock_Summary.md +8 -1
  28. data/vendor/cmock/lib/cmock_config.rb +11 -1
  29. data/vendor/cmock/lib/cmock_generator.rb +7 -0
  30. data/vendor/cmock/lib/cmock_generator_plugin_callback.rb +2 -2
  31. data/vendor/cmock/lib/cmock_header_parser.rb +123 -13
  32. data/vendor/cmock/src/cmock.h +1 -1
  33. data/vendor/cmock/test/system/test_compilation/config.yml +1 -0
  34. data/vendor/cmock/test/system/test_compilation/inline.h +23 -0
  35. data/vendor/cmock/test/system/test_interactions/all_plugins_coexist.yml +1 -0
  36. data/vendor/cmock/test/unit/cmock_config_test.rb +3 -0
  37. data/vendor/cmock/test/unit/cmock_config_test.yml +2 -1
  38. data/vendor/cmock/test/unit/cmock_generator_main_test.rb +3 -0
  39. data/vendor/cmock/test/unit/cmock_generator_plugin_callback_test.rb +4 -4
  40. data/vendor/cmock/test/unit/cmock_header_parser_test.rb +272 -0
  41. metadata +4 -3
  42. data/out.fail +0 -22
@@ -129,7 +129,7 @@ class TestInvoker
129
129
  @dependinator.enhance_test_build_object_dependencies( objects )
130
130
 
131
131
  # associate object files with executable
132
- @dependinator.setup_test_executable_dependencies( test, objects )
132
+ @dependinator.enhance_test_executable_dependencies( test, objects )
133
133
 
134
134
  # build test objects
135
135
  @task_invoker.invoke_test_objects( objects )
@@ -2,35 +2,49 @@
2
2
  # @private
3
3
  module Ceedling
4
4
  module Version
5
- # Check for local or global version of vendor directory in order to look up versions
6
- {
7
- "CEXCEPTION" => File.join("vendor","c_exception","lib","CException.h"),
8
- "CMOCK" => File.join("vendor","cmock","src","cmock.h"),
9
- "UNITY" => File.join("vendor","unity","src","unity.h"),
5
+ { "UNITY" => File.join("unity","src","unity.h"),
6
+ "CMOCK" => File.join("cmock","src","cmock.h"),
7
+ "CEXCEPTION" => File.join("c_exception","lib","CException.h")
10
8
  }.each_pair do |name, path|
11
- filename = if (File.exist?(File.join("..","..",path)))
12
- File.join("..","..",path)
13
- elsif (File.exist?(File.join(File.dirname(__FILE__),"..","..",path)))
14
- File.join(File.dirname(__FILE__),"..","..",path)
9
+ # Check for local or global version of vendor directory in order to look up versions
10
+ path1 = File.expand_path( File.join("..","..","vendor",path) )
11
+ path2 = File.expand_path( File.join(File.dirname(__FILE__),"..","..","vendor",path) )
12
+ filename = if (File.exists?(path1))
13
+ path1
14
+ elsif (File.exists?(path2))
15
+ path2
16
+ elsif exists?(CEEDLING_VENDOR)
17
+ path3 = File.expand_path( File.join(CEEDLING_VENDOR,path) )
18
+ if (File.exists?(path3))
19
+ path3
20
+ else
21
+ basepath = File.join( CEEDLING_VENDOR, path.split(/\\\//)[0], 'release')
22
+ begin
23
+ [ @ceedling[:file_wrapper].read( File.join(base_path, 'release', 'version.info') ).strip,
24
+ @ceedling[:file_wrapper].read( File.join(base_path, 'release', 'build.info') ).strip ].join('.')
25
+ rescue
26
+ "unknown"
27
+ end
28
+ end
15
29
  else
16
- eval "#{name} = 'unknown'"
30
+ module_eval("#{name} = 'unknown'")
17
31
  continue
18
32
  end
19
33
 
20
34
  # Actually look up the versions
21
35
  a = [0,0,0]
22
- File.readlines(filename) do |line|
36
+ File.readlines(filename).each do |line|
23
37
  ["VERSION_MAJOR", "VERSION_MINOR", "VERSION_BUILD"].each_with_index do |field, i|
24
38
  m = line.match(/#{name}_#{field}\s+(\d+)/)
25
39
  a[i] = m[1] unless (m.nil?)
26
40
  end
27
41
  end
28
42
 
29
- # Make a constant from each, so that we can use it elsewhere
30
- eval "#{name} = '#{a.join(".")}'"
43
+ # splat it to return the final value
44
+ eval("#{name} = '#{a.join(".")}'")
31
45
  end
32
46
 
33
- GEM = "0.29.0"
47
+ GEM = "0.29.1"
34
48
  CEEDLING = GEM
35
49
  end
36
50
  end
@@ -156,7 +156,7 @@ end
156
156
  namespace UTILS_SYM do
157
157
  def gcov_args_builder(opts)
158
158
  args = ""
159
- args += "-r \"#{opts[:gcov_report_root] || '.'}\" "
159
+ args += "-r \"#{opts[:gcov_report_root] || '.'}\" "
160
160
  args += "-f \"#{opts[:gcov_report_include]}\" " unless opts[:gcov_report_include].nil?
161
161
  args += "-e \"#{opts[:gcov_report_exclude] || GCOV_FILTER_EXCLUDE}\" "
162
162
  [ :gcov_fail_under_line, :gcov_fail_under_branch, :gcov_html_medium_threshold, :gcov_html_high_threshold].each do |opt|
@@ -165,7 +165,7 @@ namespace UTILS_SYM do
165
165
  return args
166
166
  end
167
167
 
168
- desc 'Create gcov code coverage html report (must run ceedling gcov first)'
168
+ desc 'Create gcov code coverage html/xml report (must run ceedling gcov first).'
169
169
  task GCOV_SYM do
170
170
 
171
171
  if !File.directory? GCOV_ARTIFACTS_PATH
@@ -211,7 +211,7 @@ namespace UTILS_SYM do
211
211
 
212
212
  if xml_enabled
213
213
  puts "Creating an xml report of gcov results in #{GCOV_ARTIFACTS_FILE_XML}..."
214
- command = @ceedling[:tool_executor].build_command_line(TOOLS_GCOV_POST_REPORT_XML, [], filter)
214
+ command = @ceedling[:tool_executor].build_command_line(TOOLS_GCOV_POST_REPORT_XML, [], args)
215
215
  @ceedling[:tool_executor].exec(command[:line], command[:options])
216
216
  end
217
217
 
@@ -109,11 +109,12 @@ class JunitTestsReport < Plugin
109
109
 
110
110
  def write_test( test, stream )
111
111
  test[:test].gsub!('"', '&quot;')
112
+ pp test
112
113
  case test[:result]
113
114
  when :success
114
- stream.puts(' <testcase name="%<test>s" />' % test)
115
+ stream.puts(' <testcase name="%<test>s" time="%<unity_test_time>f"/>' % test)
115
116
  when :failed
116
- stream.puts(' <testcase name="%<test>s">' % test)
117
+ stream.puts(' <testcase name="%<test>s" time="%<unity_test_time>f">' % test)
117
118
  if test[:message].empty?
118
119
  stream.puts(' <failure />')
119
120
  else
@@ -121,7 +122,7 @@ class JunitTestsReport < Plugin
121
122
  end
122
123
  stream.puts(' </testcase>')
123
124
  when :ignored
124
- stream.puts(' <testcase name="%<test>s">' % test)
125
+ stream.puts(' <testcase name="%<test>s" time="%<unity_test_time>f">' % test)
125
126
  stream.puts(' <skipped />')
126
127
  stream.puts(' </testcase>')
127
128
  end
@@ -38,6 +38,24 @@ def _add_path_in_section(project_file_path, path, section)
38
38
  end
39
39
  end
40
40
 
41
+ def _add_define_in_section(project_file_path, define, section)
42
+ project_file_contents = File.readlines(project_file_path)
43
+ defines_index = project_file_contents.index(":defines:\n")
44
+
45
+ if defines_index.nil?
46
+ # Something wrong with project.yml file, no defines?
47
+ return
48
+ end
49
+
50
+ section_index = defines_index + project_file_contents[defines_index..-1].index(" :#{section}:\n")
51
+
52
+ project_file_contents.insert(section_index + 1, " - #{define}\n")
53
+
54
+ File.open(project_file_path, "w+") do |f|
55
+ f.puts(project_file_contents)
56
+ end
57
+ end
58
+
41
59
  def add_source_path(path)
42
60
  _add_path_in_section("project.yml", path, "source")
43
61
  end
@@ -46,6 +64,10 @@ def add_test_path(path)
46
64
  _add_path_in_section("project.yml", path, "test")
47
65
  end
48
66
 
67
+ def add_test_define(define)
68
+ _add_define_in_section("project.yml", define, "test")
69
+ end
70
+
49
71
  def add_module_generator_section(project_file_path, mod_gen)
50
72
  project_file_contents = File.readlines(project_file_path)
51
73
  module_gen_index = project_file_contents.index(":module_generator:\n")
@@ -198,7 +220,7 @@ module CeedlingTestCases
198
220
 
199
221
  def can_upgrade_projects
200
222
  @c.with_context do
201
- output = `bundle exec ruby -S ceedling upgrade --local --docs #{@proj_name} 2>&1`
223
+ output = `bundle exec ruby -S ceedling upgrade #{@proj_name} 2>&1`
202
224
  expect($?.exitstatus).to match(0)
203
225
  expect(output).to match(/upgraded!/i)
204
226
  Dir.chdir @proj_name do
@@ -206,7 +228,6 @@ module CeedlingTestCases
206
228
  expect(File.exists?("src")).to eq true
207
229
  expect(File.exists?("test")).to eq true
208
230
  all_docs = Dir["vendor/ceedling/docs/*.pdf"].length + Dir["vendor/ceedling/docs/*.md"].length
209
- expect(all_docs).to be >= 4
210
231
  end
211
232
  end
212
233
  end
@@ -296,6 +317,24 @@ module CeedlingTestCases
296
317
  end
297
318
  end
298
319
 
320
+ def can_test_projects_with_unity_exec_time
321
+ @c.with_context do
322
+ Dir.chdir @proj_name do
323
+ FileUtils.cp test_asset_path("example_file.h"), 'src/'
324
+ FileUtils.cp test_asset_path("example_file.c"), 'src/'
325
+ FileUtils.cp test_asset_path("test_example_file_success.c"), 'test/'
326
+ add_test_define("UNITY_INCLUDE_EXEC_TIME")
327
+
328
+ output = `bundle exec ruby -S ceedling 2>&1`
329
+ expect($?.exitstatus).to match(0) # Since a test either pass or are ignored, we return success here
330
+ expect(output).to match(/TESTED:\s+\d/)
331
+ expect(output).to match(/PASSED:\s+\d/)
332
+ expect(output).to match(/FAILED:\s+\d/)
333
+ expect(output).to match(/IGNORED:\s+\d/)
334
+ end
335
+ end
336
+ end
337
+
299
338
  def can_test_projects_with_fail
300
339
  @c.with_context do
301
340
  Dir.chdir @proj_name do
@@ -312,6 +351,7 @@ module CeedlingTestCases
312
351
  end
313
352
  end
314
353
  end
354
+
315
355
  def can_test_projects_with_fail_alias
316
356
  @c.with_context do
317
357
  Dir.chdir @proj_name do
@@ -7,9 +7,11 @@
7
7
  - :test: test_one
8
8
  :line: 257
9
9
  :message: ''
10
+ :unity_test_time: 0
10
11
  - :test: test_two
11
12
  :line: 269
12
13
  :message: ''
14
+ :unity_test_time: 0
13
15
  :ignores: []
14
16
  :counts:
15
17
  :total: 2
@@ -6,9 +6,11 @@
6
6
  - :test: test_one
7
7
  :line: 257
8
8
  :message: ''
9
+ :unity_test_time: 0
9
10
  - :test: test_two
10
11
  :line: 269
11
12
  :message: ''
13
+ :unity_test_time: 0
12
14
  :failures: []
13
15
  :ignores: []
14
16
  :counts:
@@ -8,9 +8,11 @@
8
8
  - :test: test_one
9
9
  :line: 257
10
10
  :message: ''
11
+ :unity_test_time: 0
11
12
  - :test: test_two
12
13
  :line: 269
13
14
  :message: ''
15
+ :unity_test_time: 0
14
16
  :counts:
15
17
  :total: 2
16
18
  :passed: 0
@@ -6,6 +6,7 @@
6
6
  - :test: test_one
7
7
  :line: 257
8
8
  :message: ''
9
+ :unity_test_time: 0
9
10
  -
10
11
  :failures: []
11
12
  :ignores: []
@@ -6,9 +6,11 @@
6
6
  - :test: test_one
7
7
  :line: 257
8
8
  :message: ''
9
+ :unity_test_time: 0
9
10
  - :test: test_two
10
11
  :line: 269
11
12
  :message: ''
13
+ :unity_test_time: 0
12
14
  :failures: []
13
15
  :ignores: []
14
16
  :counts:
@@ -30,6 +30,7 @@ describe "Ceedling" do
30
30
  it { can_test_projects_with_success }
31
31
  it { can_test_projects_with_success_test_alias }
32
32
  it { can_test_projects_with_success_default }
33
+ it { can_test_projects_with_unity_exec_time }
33
34
  it { can_test_projects_with_fail }
34
35
  it { can_test_projects_with_fail_alias }
35
36
  it { can_test_projects_with_fail_default }
@@ -76,6 +77,7 @@ describe "Ceedling" do
76
77
  it { can_test_projects_with_success }
77
78
  it { can_test_projects_with_success_test_alias }
78
79
  it { can_test_projects_with_success_default }
80
+ it { can_test_projects_with_unity_exec_time }
79
81
  it { can_test_projects_with_fail }
80
82
  it { can_test_projects_with_fail_alias }
81
83
  it { can_test_projects_with_fail_default }
@@ -103,6 +105,7 @@ describe "Ceedling" do
103
105
  it { can_test_projects_with_success }
104
106
  it { can_test_projects_with_success_test_alias }
105
107
  it { can_test_projects_with_success_default }
108
+ it { can_test_projects_with_unity_exec_time }
106
109
  it { can_test_projects_with_fail }
107
110
  it { can_test_projects_with_fail_alias }
108
111
  it { can_test_projects_with_fail_default }
@@ -117,11 +120,13 @@ describe "Ceedling" do
117
120
 
118
121
  it { can_upgrade_projects }
119
122
  it { contains_a_vendor_directory }
123
+ it { does_not_contain_documentation }
120
124
  it { can_fetch_non_project_help }
121
125
  it { can_fetch_project_help }
122
126
  it { can_test_projects_with_success }
123
127
  it { can_test_projects_with_success_test_alias }
124
128
  it { can_test_projects_with_success_default }
129
+ it { can_test_projects_with_unity_exec_time }
125
130
  it { can_test_projects_with_fail }
126
131
  it { can_test_projects_with_fail_alias }
127
132
  it { can_test_projects_with_fail_default }
@@ -149,6 +154,7 @@ describe "Ceedling" do
149
154
  it { can_test_projects_with_success }
150
155
  it { can_test_projects_with_success_test_alias }
151
156
  it { can_test_projects_with_success_default }
157
+ it { can_test_projects_with_unity_exec_time }
152
158
  it { can_test_projects_with_fail }
153
159
  it { can_test_projects_with_compile_error }
154
160
  it { can_use_the_module_plugin }
@@ -518,6 +518,14 @@ from the defaults. We've tried to specify what the defaults are below.
518
518
  * `:include` will mock externed functions
519
519
  * `:exclude` will ignore externed functions (default).
520
520
 
521
+ * `:treat_inlines`:
522
+ This specifies how you want CMock to handle functions that have been
523
+ marked as inline in the header file. Should it mock them?
524
+
525
+ * `:include` will mock inlined functions
526
+ * `:exclude` will ignore inlined functions (default).
527
+
528
+
521
529
  * `:unity_helper_path`:
522
530
  If you have created a header with your own extensions to unity to
523
531
  handle your own types, you can set this argument to that path. CMock
@@ -686,4 +694,3 @@ you might tool CMock into your build process. You may also want to consider
686
694
  using [Ceedling](https://throwtheswitch.org/ceedling). Please note that
687
695
  these examples are meant to show how the build process works. They have
688
696
  failing tests ON PURPOSE to show what that would look like. Don't be alarmed. ;)
689
-
@@ -29,6 +29,7 @@ class CMockConfig
29
29
  :when_ptr => :compare_data, #the options being :compare_ptr, :compare_data, or :smart
30
30
  :verbosity => 2, #the options being 0 errors only, 1 warnings and errors, 2 normal info, 3 verbose
31
31
  :treat_externs => :exclude, #the options being :include or :exclude
32
+ :treat_inlines => :exclude, #the options being :include or :exclude
32
33
  :callback_include_count => true,
33
34
  :callback_after_arg_check => false,
34
35
  :includes => nil,
@@ -64,7 +65,16 @@ class CMockConfig
64
65
  end
65
66
  options[:unity_helper_path] ||= options[:unity_helper]
66
67
  options[:unity_helper_path] = [options[:unity_helper_path]] if options[:unity_helper_path].is_a? String
67
- options[:includes_c_post_header] = ((options[:includes_c_post_header] || []) + (options[:unity_helper_path] || [])).uniq
68
+
69
+ if options[:unity_helper_path]
70
+ require 'pathname'
71
+ includes1 = options[:includes_c_post_header] || []
72
+ includes2 = options[:unity_helper_path].map do |path|
73
+ Pathname(path).relative_path_from(Pathname(options[:mock_path])).to_s
74
+ end
75
+ options[:includes_c_post_header] = (includes1 + includes2).uniq
76
+ end
77
+
68
78
  options[:plugins].compact!
69
79
  options[:plugins].map! {|p| p.to_sym}
70
80
  @options = options
@@ -16,6 +16,7 @@ class CMockGenerator
16
16
  @prefix = @config.mock_prefix
17
17
  @suffix = @config.mock_suffix
18
18
  @weak = @config.weak
19
+ @include_inline = @config.treat_inlines
19
20
  @ordered = @config.enforce_strict_ordering
20
21
  @framework = @config.framework.to_s
21
22
  @fail_on_unexpected_calls = @config.fail_on_unexpected_calls
@@ -61,6 +62,12 @@ class CMockGenerator
61
62
  end
62
63
 
63
64
  def create_mock_header_file(parsed_stuff)
65
+ if @include_inline == :include
66
+ @file_writer.create_file(@module_name + ".h", @subdir) do |file, filename|
67
+ file << parsed_stuff[:normalized_source]
68
+ end
69
+ end
70
+
64
71
  @file_writer.create_file(@mock_name + ".h", @subdir) do |file, filename|
65
72
  create_mock_header_header(file, filename)
66
73
  create_mock_header_service_call_declarations(file)
@@ -60,9 +60,9 @@ class CMockGeneratorPluginCallback
60
60
  " UNITY_CLR_DETAILS();\n" \
61
61
  " return;\n }\n"
62
62
  else
63
- " #{function[:return][:type]} ret = #{generate_call(function)};\n" \
63
+ " #{function[:return][:type]} cmock_cb_ret = #{generate_call(function)};\n" \
64
64
  " UNITY_CLR_DETAILS();\n" \
65
- " return ret;\n }\n"
65
+ " return cmock_cb_ret;\n }\n"
66
66
  end
67
67
  end
68
68
 
@@ -6,7 +6,7 @@
6
6
 
7
7
  class CMockHeaderParser
8
8
 
9
- attr_accessor :funcs, :c_attr_noconst, :c_attributes, :treat_as_void, :treat_externs
9
+ attr_accessor :funcs, :c_attr_noconst, :c_attributes, :treat_as_void, :treat_externs, :treat_inlines
10
10
 
11
11
  def initialize(cfg)
12
12
  @funcs = []
@@ -24,13 +24,16 @@ class CMockHeaderParser
24
24
  @local_as_void = @treat_as_void
25
25
  @verbosity = cfg.verbosity
26
26
  @treat_externs = cfg.treat_externs
27
+ @treat_inlines = cfg.treat_inlines
27
28
  @c_strippables += ['extern'] if (@treat_externs == :include) #we'll need to remove the attribute if we're allowing externs
29
+ @c_strippables += ['inline'] if (@treat_inlines == :include) #we'll need to remove the attribute if we're allowing inlines
28
30
  end
29
31
 
30
32
  def parse(name, source)
31
33
  @module_name = name.gsub(/\W/,'')
32
34
  @typedefs = []
33
35
  @funcs = []
36
+ @normalized_source = nil
34
37
  function_names = []
35
38
 
36
39
  parse_functions( import_source(source) ).map do |decl|
@@ -41,14 +44,113 @@ class CMockHeaderParser
41
44
  end
42
45
  end
43
46
 
47
+ @normalized_source = if (@treat_inlines == :include)
48
+ transform_inline_functions(source)
49
+ else
50
+ ''
51
+ end
52
+
44
53
  { :includes => nil,
45
54
  :functions => @funcs,
46
- :typedefs => @typedefs
55
+ :typedefs => @typedefs,
56
+ :normalized_source => @normalized_source
47
57
  }
48
58
  end
49
59
 
50
60
  private if $ThisIsOnlyATest.nil? ################
51
61
 
62
+ def remove_nested_pairs_of_braces(source)
63
+ # remove nested pairs of braces because no function declarations will be inside of them (leave outer pair for function definition detection)
64
+ if (RUBY_VERSION.split('.')[0].to_i > 1)
65
+ #we assign a string first because (no joke) if Ruby 1.9.3 sees this line as a regex, it will crash.
66
+ r = "\\{([^\\{\\}]*|\\g<0>)*\\}"
67
+ source.gsub!(/#{r}/m, '{ }')
68
+ else
69
+ while source.gsub!(/\{[^\{\}]*\{[^\{\}]*\}[^\{\}]*\}/m, '{ }')
70
+ end
71
+ end
72
+
73
+ return source
74
+ end
75
+
76
+ # Return the number of pairs of braces/square brackets in the function provided by the user
77
+ # +source+:: String containing the function to be processed
78
+ def count_number_of_pairs_of_braces_in_function(source)
79
+ is_function_start_found = false
80
+ curr_level = 0
81
+ total_pairs = 0
82
+
83
+ source.each_char do |c|
84
+ if ("{" == c)
85
+ curr_level += 1
86
+ total_pairs +=1
87
+ is_function_start_found = true
88
+ elsif ("}" == c)
89
+ curr_level -=1
90
+ end
91
+
92
+ break if is_function_start_found && curr_level == 0 # We reached the end of the inline function body
93
+ end
94
+
95
+ if 0 != curr_level
96
+ total_pairs = 0 # Something is fishy about this source, not enough closing braces?
97
+ end
98
+
99
+ return total_pairs
100
+ end
101
+
102
+ # Transform inline functions to regular functions in the source by the user
103
+ # +source+:: String containing the source to be processed
104
+ def transform_inline_functions(source)
105
+ # Format to look for inline functions.
106
+ # This is a combination of "static" and "inline" keywords ("static inline", "inline static", "inline", "static")
107
+ # There are several possibilities:
108
+ # - sometimes they appear together, sometimes individually,
109
+ # - The keywords can appear before or after the return type (this is a compiler warning but people do weird stuff),
110
+ # so we check for word boundaries when searching for them
111
+ # - We first remove "static inline" combinations and boil down to single inline or static statements
112
+ inline_function_regex_formats = [
113
+ /(static\s+inline|inline\s+static)\s*/, # Last part (\s*) is just to remove whitespaces (only to prettify the output)
114
+ /(\bstatic\b|\binline\b)\s*/, # Last part (\s*) is just to remove whitespaces (only to prettify the output)
115
+ ]
116
+ square_bracket_pair_regex_format = /\{[^\{\}]*\}/ # Regex to match one whole block enclosed by two square brackets
117
+
118
+ # let's clean up the encoding in case they've done anything weird with the characters we might find
119
+ source = source.force_encoding("ISO-8859-1").encode("utf-8", :replace => nil)
120
+
121
+ # - Just looking for static|inline in the gsub is a bit too aggressive (functions that are named like this, ...), so we try to be a bit smarter
122
+ # Instead, look for "static inline" and parse it:
123
+ # - Everything before the match should just be copied, we don't want
124
+ # to touch anything but the inline functions.
125
+ # - Remove the implementation of the inline function (this is enclosed
126
+ # in square brackets) and replace it with ";" to complete the
127
+ # transformation to normal/non-inline function.
128
+ # To ensure proper removal of the function body, we count the number of square-bracket pairs
129
+ # and remove the pairs one-by-one.
130
+ # - Copy everything after the inline function implementation and start the parsing of the next inline function
131
+
132
+ inline_function_regex_formats.each do |format|
133
+ loop do
134
+ inline_function_match = source.match(/#{format}/) # Search for inline function declaration
135
+ break if nil == inline_function_match # No inline functions so nothing to do
136
+
137
+ total_pairs_to_remove = count_number_of_pairs_of_braces_in_function(inline_function_match.post_match)
138
+
139
+ break if 0 == total_pairs_to_remove # Bad source?
140
+
141
+ inline_function_stripped = inline_function_match.post_match
142
+
143
+ total_pairs_to_remove.times do
144
+ inline_function_stripped.sub!(/\s*#{square_bracket_pair_regex_format}/, ";") # Remove inline implementation (+ some whitespace because it's prettier)
145
+ end
146
+
147
+ source = inline_function_match.pre_match + inline_function_stripped # Make new source with the inline function removed and move on to the next
148
+ end
149
+ end
150
+
151
+ return source
152
+ end
153
+
52
154
  def import_source(source)
53
155
 
54
156
  # let's clean up the encoding in case they've done anything weird with the characters we might find
@@ -100,16 +202,15 @@ class CMockHeaderParser
100
202
  "#{functype} #{$2.strip}(#{$3});"
101
203
  end
102
204
 
103
- # remove nested pairs of braces because no function declarations will be inside of them (leave outer pair for function definition detection)
104
- if (RUBY_VERSION.split('.')[0].to_i > 1)
105
- #we assign a string first because (no joke) if Ruby 1.9.3 sees this line as a regex, it will crash.
106
- r = "\\{([^\\{\\}]*|\\g<0>)*\\}"
107
- source.gsub!(/#{r}/m, '{ }')
108
- else
109
- while source.gsub!(/\{[^\{\}]*\{[^\{\}]*\}[^\{\}]*\}/m, '{ }')
110
- end
205
+ source = remove_nested_pairs_of_braces(source)
206
+
207
+ if (@treat_inlines == :include)
208
+ # Functions having "{ }" at this point are/were inline functions,
209
+ # User wants them in so 'disguise' them as normal functions with the ";"
210
+ source.gsub!("{ }", ";")
111
211
  end
112
212
 
213
+
113
214
  # remove function definitions by stripping off the arguments right now
114
215
  source.gsub!(/\([^\)]*\)\s*\{[^\}]*\}/m, ";")
115
216
 
@@ -124,11 +225,20 @@ class CMockHeaderParser
124
225
  src_lines = source.split(/\s*;\s*/).uniq
125
226
  src_lines.delete_if {|line| line.strip.length == 0} # remove blank lines
126
227
  src_lines.delete_if {|line| !(line =~ /[\w\s\*]+\(+\s*\*[\*\s]*[\w\s]+(?:\[[\w\s]*\]\s*)+\)+\s*\((?:[\w\s\*]*,?)*\s*\)/).nil?} #remove function pointer arrays
127
- if (@treat_externs == :include)
128
- src_lines.delete_if {|line| !(line =~ /(?:^|\s+)(?:inline)\s+/).nil?} # remove inline functions
228
+
229
+ unless (@treat_externs == :include)
230
+ src_lines.delete_if {|line| !(line =~ /(?:^|\s+)(?:extern)\s+/).nil?} # remove extern functions
231
+ end
232
+
233
+ if (@treat_inlines == :include)
234
+ src_lines.each {
235
+ |src_line|
236
+ src_line.gsub!(/^inline/, "") # Remove "inline" so that they are 'normal' functions
237
+ }
129
238
  else
130
- src_lines.delete_if {|line| !(line =~ /(?:^|\s+)(?:extern|inline)\s+/).nil?} # remove inline and extern functions
239
+ src_lines.delete_if {|line| !(line =~ /(?:^|\s+)(?:inline)\s+/).nil?} # remove inline functions
131
240
  end
241
+
132
242
  src_lines.delete_if {|line| line.empty? } #drop empty lines
133
243
  end
134
244