ceedling 1.1.3 → 1.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/GIT_COMMIT_SHA +1 -1
- data/README.md +1 -1
- data/assets/fake_crash_test_fixture.rb +14 -0
- data/assets/partial_all_module.h +11 -0
- data/assets/run_with_ubsan.rb +15 -0
- data/assets/same_dir_pairing_module.c +4 -0
- data/assets/same_dir_pairing_module.h +8 -0
- data/assets/same_dir_pairing_module_func.h +6 -0
- data/assets/same_dir_pairing_other.c +4 -0
- data/assets/same_dir_pairing_other.h +8 -0
- data/assets/test_example_file_ub_shift_overflow.c +22 -0
- data/assets/test_partial_all_module.c +16 -0
- data/assets/test_same_dir_pairing.c +23 -0
- data/docs/Changelog.md +32 -1
- data/docs/mkdocs/configuration/reference/index.md +8 -0
- data/docs/mkdocs/configuration/reference/partials.md +38 -0
- data/examples/wondrous_forest/src/LightSensor.c +21 -4
- data/examples/wondrous_forest/src/LightSensor.h +12 -4
- data/examples/wondrous_forest/test/TestLightSensor.c +34 -4
- data/lib/ceedling/c_extractor/c_extractor.rb +37 -11
- data/lib/ceedling/c_extractor/c_extractor_constants.rb +0 -3
- data/lib/ceedling/c_extractor/c_extractor_declarations.rb +26 -25
- data/lib/ceedling/c_extractor/c_extractor_functions.rb +5 -12
- data/lib/ceedling/config/configurator.rb +1 -0
- data/lib/ceedling/config/configurator_setup.rb +26 -0
- data/lib/ceedling/defaults.rb +8 -0
- data/lib/ceedling/file_path_utils.rb +7 -0
- data/lib/ceedling/file_wrapper.rb +8 -0
- data/lib/ceedling/generators/generator.rb +15 -0
- data/lib/ceedling/generators/generator_helper.rb +25 -6
- data/lib/ceedling/generators/generator_partials.rb +103 -3
- data/lib/ceedling/generators/generator_test_results_backtrace.rb +183 -83
- data/lib/ceedling/includes/includes.rb +66 -11
- data/lib/ceedling/objects.yml +5 -0
- data/lib/ceedling/partials/partializer.rb +18 -3
- data/lib/ceedling/partials/partializer_utils.rb +6 -1
- data/lib/ceedling/preprocess/preprocessinator_includes_handler.rb +43 -16
- data/lib/ceedling/test_invoker/test_build_executor.rb +21 -8
- data/lib/version.rb +1 -1
- data/mkdocs.yml +1 -0
- data/site-local/404.html +30 -0
- data/site-local/configuration/environment-vars.html +30 -0
- data/site-local/configuration/global-collections.html +30 -0
- data/site-local/configuration/index.html +30 -0
- data/site-local/configuration/loading.html +30 -0
- data/site-local/configuration/mixins.html +30 -0
- data/site-local/configuration/parallel-builds.html +30 -0
- data/site-local/configuration/project-file.html +30 -0
- data/site-local/configuration/reference/cexception.html +30 -0
- data/site-local/configuration/reference/cmock.html +30 -0
- data/site-local/configuration/reference/defines.html +30 -0
- data/site-local/configuration/reference/environment.html +30 -0
- data/site-local/configuration/reference/extension.html +30 -0
- data/site-local/configuration/reference/files.html +30 -0
- data/site-local/configuration/reference/flags.html +30 -0
- data/site-local/configuration/reference/index.html +36 -0
- data/site-local/configuration/reference/libraries.html +30 -0
- data/site-local/configuration/reference/mixins.html +30 -0
- data/site-local/configuration/reference/partials.html +4258 -0
- data/site-local/configuration/reference/paths.html +30 -0
- data/site-local/configuration/reference/plugins.html +30 -0
- data/site-local/configuration/reference/project.html +30 -0
- data/site-local/configuration/reference/release-build.html +31 -1
- data/site-local/configuration/reference/test-build.html +31 -1
- data/site-local/configuration/reference/test-runner.html +30 -0
- data/site-local/configuration/reference/tools.html +30 -0
- data/site-local/configuration/reference/unity.html +30 -0
- data/site-local/configuration/which-ceedling.html +30 -0
- data/site-local/development/index.html +30 -0
- data/site-local/development/plugins/configuration.html +30 -0
- data/site-local/development/plugins/index.html +30 -0
- data/site-local/development/plugins/plugin-subclass.html +30 -0
- data/site-local/development/plugins/rake-tasks.html +30 -0
- data/site-local/development/workflow.html +30 -0
- data/site-local/getting-started/command-line.html +30 -0
- data/site-local/getting-started/example-projects/cipher-quest.html +30 -0
- data/site-local/getting-started/example-projects/index.html +30 -0
- data/site-local/getting-started/example-projects/temp-sensor.html +30 -0
- data/site-local/getting-started/example-projects/wondrous-forest.html +30 -0
- data/site-local/getting-started/index.html +30 -0
- data/site-local/getting-started/installation.html +30 -0
- data/site-local/getting-started/quick-start.html +30 -0
- data/site-local/help.html +30 -0
- data/site-local/index.html +30 -0
- data/site-local/overview/build-system.html +30 -0
- data/site-local/overview/index.html +30 -0
- data/site-local/overview/test-environments.html +30 -0
- data/site-local/overview/tools-and-frameworks.html +30 -0
- data/site-local/plugins/beep.html +30 -0
- data/site-local/plugins/bullseye.html +30 -0
- data/site-local/plugins/command-hooks.html +30 -0
- data/site-local/plugins/compile-commands-json-db.html +30 -0
- data/site-local/plugins/cppcheck.html +30 -0
- data/site-local/plugins/dependencies.html +30 -0
- data/site-local/plugins/fff.html +30 -0
- data/site-local/plugins/gcov/examples.html +30 -0
- data/site-local/plugins/gcov/gcovr.html +30 -0
- data/site-local/plugins/gcov/index.html +30 -0
- data/site-local/plugins/gcov/overview.html +30 -0
- data/site-local/plugins/gcov/reportgenerator.html +30 -0
- data/site-local/plugins/gcov/reporting.html +30 -0
- data/site-local/plugins/gcov/setup.html +30 -0
- data/site-local/plugins/gcov/tool-versions.html +30 -0
- data/site-local/plugins/gcov/troubleshooting.html +30 -0
- data/site-local/plugins/index.html +30 -0
- data/site-local/plugins/module-generator.html +30 -0
- data/site-local/plugins/report-build-warnings-log.html +30 -0
- data/site-local/plugins/report-tests-gtestlike-stdout.html +30 -0
- data/site-local/plugins/report-tests-ide-stdout.html +30 -0
- data/site-local/plugins/report-tests-log-factory.html +30 -0
- data/site-local/plugins/report-tests-pretty-stdout.html +30 -0
- data/site-local/plugins/report-tests-raw-output-log.html +30 -0
- data/site-local/plugins/report-tests-teamcity-stdout.html +30 -0
- data/site-local/plugins/valgrind.html +30 -0
- data/site-local/project/release-history.html +30 -0
- data/site-local/project/upgrade.html +30 -0
- data/site-local/reference/build-directives.html +30 -0
- data/site-local/reference/command-line.html +30 -0
- data/site-local/reference/environment-vars.html +30 -0
- data/site-local/reference/gcov-plugin.html +30 -0
- data/site-local/reference/global-collections.html +30 -0
- data/site-local/reference/index.html +30 -0
- data/site-local/reference/partials-macros.html +30 -0
- data/site-local/reference/project-configuration.html +30 -0
- data/site-local/sitemap.xml.gz +0 -0
- data/site-local/snapshot/examples/cipher_quest/index.html +30 -0
- data/site-local/snapshot/examples/temp_sensor/index.html +30 -0
- data/site-local/snapshot/examples/wondrous_forest/index.html +30 -0
- data/site-local/testing-guide/build-directives.html +30 -0
- data/site-local/testing-guide/conventions.html +30 -0
- data/site-local/testing-guide/frameworks.html +30 -0
- data/site-local/testing-guide/index.html +30 -0
- data/site-local/testing-guide/partials/configuration.html +30 -0
- data/site-local/testing-guide/partials/conventions.html +30 -0
- data/site-local/testing-guide/partials/directives.html +30 -0
- data/site-local/testing-guide/partials/example.html +30 -0
- data/site-local/testing-guide/partials/index.html +30 -0
- data/site-local/testing-guide/partials/overview.html +30 -0
- data/site-local/testing-guide/partials/variables.html +30 -0
- data/site-local/testing-guide/test-cases.html +30 -0
- data/site-local/testing-guide/test-sample.html +30 -0
- data/site-local/testing-guide/test-suite-anatomy.html +30 -0
- data/spec/support/system/spec_system_helper.rb +25 -0
- data/spec/system/deployment_as_gem_spec.rb +12 -0
- data/spec/system/deployment_as_vendor_spec.rb +17 -0
- data/spec/system/encoding_stress_spec.rb +4 -4
- data/spec/system/example_wondrous_forest_spec.rb +2 -2
- data/spec/system/support/common_test_cases.rb +137 -0
- data/spec/units/c_extractor/c_extractor_declarations_spec.rb +27 -4
- data/spec/units/c_extractor/c_extractor_functions_spec.rb +1 -11
- data/spec/units/c_extractor/c_extractor_integration_spec.rb +18 -11
- data/spec/units/c_extractor/c_extractor_spec.rb +57 -1
- data/spec/units/config/configurator_setup_spec.rb +61 -0
- data/spec/units/file_path_utils_spec.rb +17 -0
- data/spec/units/generators/generator_helper_spec.rb +34 -7
- data/spec/units/generators/generator_partials_spec.rb +223 -12
- data/spec/units/generators/generator_test_results_backtrace_spec.rb +200 -37
- data/spec/units/includes/include_spec.rb +30 -18
- data/spec/units/includes/includes_spec.rb +100 -10
- data/spec/units/partials/partializer_spec.rb +113 -1
- data/spec/units/partials/partializer_utils_spec.rb +11 -0
- data/spec/units/preprocess/preprocessinator_includes_handler_spec.rb +96 -7
- data/spec/units/system_wrapper_spec.rb +72 -0
- metadata +19 -2
|
@@ -15,13 +15,9 @@ class CExtractorFunctions
|
|
|
15
15
|
|
|
16
16
|
constructor :c_extractor_code_text
|
|
17
17
|
|
|
18
|
-
attr_writer :max_line_length
|
|
19
|
-
|
|
20
18
|
def setup()
|
|
21
19
|
# Aliases
|
|
22
20
|
@code_text = @c_extractor_code_text
|
|
23
|
-
|
|
24
|
-
@max_line_length = DEFAULT_MAX_LINE_LENGTH
|
|
25
21
|
end
|
|
26
22
|
|
|
27
23
|
def try_extract_function_declaration(scanner)
|
|
@@ -124,7 +120,10 @@ class CExtractorFunctions
|
|
|
124
120
|
# - Before '{' for definitions)
|
|
125
121
|
#
|
|
126
122
|
# Safety:
|
|
127
|
-
#
|
|
123
|
+
# A signature with no terminating ')'/'{' runs the scan to the end of
|
|
124
|
+
# whatever buffer the caller (CExtractor#extract_next_feature) has grown so
|
|
125
|
+
# far; that caller is the one place enforcing an overall length ceiling,
|
|
126
|
+
# since only it knows how much more of the file remains to try growing into.
|
|
128
127
|
def extract_function_signature(scanner, type)
|
|
129
128
|
start_pos = scanner.pos
|
|
130
129
|
# Tracks paren depth while accumulating candidate end-positions — not suitable for collect_balanced()
|
|
@@ -133,16 +132,10 @@ class CExtractorFunctions
|
|
|
133
132
|
string_char = nil
|
|
134
133
|
signature_candidates = [] # Track positions where paren_depth returns to 0
|
|
135
134
|
found_opening_paren = false # Track if we've seen an opening paren
|
|
136
|
-
|
|
135
|
+
|
|
137
136
|
until scanner.eos?
|
|
138
137
|
char = scanner.peek(1)
|
|
139
138
|
|
|
140
|
-
# Safety check
|
|
141
|
-
if (scanner.pos - start_pos) > @max_line_length
|
|
142
|
-
scanner.pos = start_pos
|
|
143
|
-
return nil
|
|
144
|
-
end
|
|
145
|
-
|
|
146
139
|
# Handle string literals
|
|
147
140
|
if in_string
|
|
148
141
|
if char == '\\'
|
|
@@ -756,6 +756,7 @@ class Configurator
|
|
|
756
756
|
blotter &= @configurator_setup.validate_test_preprocessor( config )
|
|
757
757
|
blotter &= @configurator_setup.validate_backtrace( config )
|
|
758
758
|
blotter &= @configurator_setup.validate_threads( config )
|
|
759
|
+
blotter &= @configurator_setup.validate_partials( config )
|
|
759
760
|
blotter &= @configurator_setup.validate_plugins( config )
|
|
760
761
|
|
|
761
762
|
# Informational notices
|
|
@@ -744,6 +744,32 @@ class ConfiguratorSetup
|
|
|
744
744
|
return valid
|
|
745
745
|
end
|
|
746
746
|
|
|
747
|
+
# `:max_extraction_length` is a multiplier of 1000 characters, not a raw character count
|
|
748
|
+
# (project-file ergonomics -- `5000` reads more clearly than `5000000`). The minimum here
|
|
749
|
+
# keeps that ceiling comfortably above any ordinary single C declaration or function
|
|
750
|
+
# signature, catching a value so small it would defeat the setting's own purpose (e.g.
|
|
751
|
+
# someone setting `1000` expecting "1000 characters" rather than "1000x characters").
|
|
752
|
+
def validate_partials(config)
|
|
753
|
+
valid = true
|
|
754
|
+
|
|
755
|
+
max_extraction_length = config[:partials][:max_extraction_length]
|
|
756
|
+
|
|
757
|
+
walk = @reportinator.generate_config_walk( [:partials, :max_extraction_length] )
|
|
758
|
+
|
|
759
|
+
case max_extraction_length
|
|
760
|
+
when Integer
|
|
761
|
+
if max_extraction_length < 10
|
|
762
|
+
@loginator.log( "#{walk} must be at least 10 (10,000 characters)", Verbosity::ERRORS )
|
|
763
|
+
valid = false
|
|
764
|
+
end
|
|
765
|
+
else
|
|
766
|
+
@loginator.log( "#{walk} is not an integer", Verbosity::ERRORS )
|
|
767
|
+
valid = false
|
|
768
|
+
end
|
|
769
|
+
|
|
770
|
+
return valid
|
|
771
|
+
end
|
|
772
|
+
|
|
747
773
|
def validate_plugins(config)
|
|
748
774
|
missing_plugins =
|
|
749
775
|
Set.new( config[:plugins][:enabled] ) -
|
data/lib/ceedling/defaults.rb
CHANGED
|
@@ -295,6 +295,14 @@ DEFAULT_CEEDLING_PROJECT_CONFIG = {
|
|
|
295
295
|
:preprocess_force_fallback => false,
|
|
296
296
|
},
|
|
297
297
|
|
|
298
|
+
:partials => {
|
|
299
|
+
# A single C construct (declaration, definition, directive, etc.) is extracted into a
|
|
300
|
+
# growing buffer; this bounds how large that buffer may grow before extraction fails
|
|
301
|
+
# outright rather than silently dropping an oversized construct and everything after
|
|
302
|
+
# it in the file. Value is a multiplier of 1000 characters -- 5000 means 5,000,000.
|
|
303
|
+
:max_extraction_length => 5000,
|
|
304
|
+
},
|
|
305
|
+
|
|
298
306
|
# Unlike other top-level entries, :environment is an array (of hashes) to preserve order
|
|
299
307
|
:environment => [],
|
|
300
308
|
|
|
@@ -303,6 +303,13 @@ class FilePathUtils
|
|
|
303
303
|
return PARTIAL_FILENAME_PREFIX + _module + '_interface' + EXTENSION_CORE_HEADER
|
|
304
304
|
end
|
|
305
305
|
|
|
306
|
+
# A module's typedefs and aggregate (struct/enum/union) definitions are generated into this
|
|
307
|
+
# standalone header, shared by both the implementation and interface headers below, so a
|
|
308
|
+
# module tested and mocked in the same file never has its types defined more than once.
|
|
309
|
+
def form_partial_types_header_filename(_module)
|
|
310
|
+
return PARTIAL_FILENAME_PREFIX + _module + '_types' + EXTENSION_CORE_HEADER
|
|
311
|
+
end
|
|
312
|
+
|
|
306
313
|
def form_mock_partial_interface_header_filename(_module)
|
|
307
314
|
return @configurator.cmock_mock_prefix + PARTIAL_FILENAME_PREFIX + _module + '_interface' + EXTENSION_CORE_HEADER
|
|
308
315
|
end
|
|
@@ -9,6 +9,7 @@ require 'rubygems'
|
|
|
9
9
|
require 'rake' # for FileList
|
|
10
10
|
require 'fileutils'
|
|
11
11
|
require 'pathname'
|
|
12
|
+
require 'tmpdir'
|
|
12
13
|
require 'ceedling/constants'
|
|
13
14
|
|
|
14
15
|
|
|
@@ -132,4 +133,11 @@ class FileWrapper
|
|
|
132
133
|
return FileUtils.mkdir_p(folder)
|
|
133
134
|
end
|
|
134
135
|
|
|
136
|
+
# Creates a uniquely-named, empty directory nested inside `parent` and returns its path.
|
|
137
|
+
# `parent` must already exist. Collision-free even across concurrent callers targeting
|
|
138
|
+
# the same `parent` -- Dir.mktmpdir retries internally on name clash.
|
|
139
|
+
def mkdir_tmp(prefix, parent)
|
|
140
|
+
return Dir.mktmpdir(prefix, parent)
|
|
141
|
+
end
|
|
142
|
+
|
|
135
143
|
end
|
|
@@ -35,6 +35,21 @@ class Generator
|
|
|
35
35
|
@backtrace = @generator_test_results_backtrace
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
+
# A module's typedefs and aggregate definitions are identical regardless of whether a test
|
|
39
|
+
# file tests it, mocks it, or both, so they're generated once into their own header here
|
|
40
|
+
# rather than duplicated by generate_partial_implementation and generate_partial_interface.
|
|
41
|
+
# Returns the bare generated filename (for the caller to add to each header's own includes
|
|
42
|
+
# list) or nil when the module has no typedefs or aggregate definitions to share.
|
|
43
|
+
def generate_partial_types(name:, c_module:, output_path:)
|
|
44
|
+
arg_hash = {
|
|
45
|
+
:name => name,
|
|
46
|
+
:c_module => c_module,
|
|
47
|
+
:output_path => output_path
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return @generator_partials.generate_types( **arg_hash )
|
|
51
|
+
end
|
|
52
|
+
|
|
38
53
|
def generate_partial_interface(test:, partial:, function_declarations:, includes:, c_module:, input_filepath:, output_path:)
|
|
39
54
|
msg = @reportinator.generate_module_progress(
|
|
40
55
|
operation: "Generating Partial mockable interface for",
|
|
@@ -18,21 +18,40 @@ class GeneratorHelper
|
|
|
18
18
|
|
|
19
19
|
crash = false
|
|
20
20
|
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
# Any signal death is unconditionally a crash, whatever text made it out beforehand.
|
|
22
|
+
# Broadened from a SIGSEGV(11)-only check: a sanitizer's halt_on_error (UBSan, ASan)
|
|
23
|
+
# raises SIGABRT, not SIGSEGV, and there's no reason to trust a process that a
|
|
24
|
+
# signal actually killed regardless of which one. Applies to Unix-like systems
|
|
25
|
+
# including MSYS on Windows -- native (non-MSYS) Windows Ruby never reports
|
|
26
|
+
# `signaled?` true at all, so this check is inert there rather than wrong; the
|
|
27
|
+
# stats-footer and stderr checks below already carry Windows crash detection.
|
|
28
|
+
if shell_result[:status] && shell_result[:status].signaled?
|
|
29
|
+
@loginator.lazy( Verbosity::DEBUG, LogLabels::CRASH ) do
|
|
30
|
+
"#{runner} process terminated by signal #{shell_result[:status].termsig}"
|
|
26
31
|
end
|
|
27
32
|
crash = true
|
|
28
33
|
end
|
|
29
34
|
|
|
30
35
|
# No test results found in test executable output
|
|
31
|
-
|
|
36
|
+
stats_match = shell_result[:output].match( PATTERNS::TEST_STDOUT_STATISTICS )
|
|
37
|
+
if stats_match.nil?
|
|
32
38
|
# No debug logging here because we log this condition in the error log handling below
|
|
33
39
|
crash = true
|
|
34
40
|
end
|
|
35
41
|
|
|
42
|
+
# A clean summary (0 failures) is not trustworthy if the real process still exited
|
|
43
|
+
# nonzero afterward -- e.g. LeakSanitizer detecting a leak during teardown, after
|
|
44
|
+
# every test case already printed and Unity's own summary already looked clean.
|
|
45
|
+
# A nonzero exit consistent with Unity's own reported failures (its exit code
|
|
46
|
+
# convention is one per failed test case) is an ordinary failing run, not a crash.
|
|
47
|
+
if stats_match && shell_result[:status] && !shell_result[:status].success? &&
|
|
48
|
+
!shell_result[:status].signaled? && stats_match[2].to_i == 0
|
|
49
|
+
@loginator.lazy( Verbosity::DEBUG, LogLabels::CRASH ) do
|
|
50
|
+
"#{runner} reported a clean summary but exited with real status #{shell_result[:status].exitstatus}"
|
|
51
|
+
end
|
|
52
|
+
crash = true
|
|
53
|
+
end
|
|
54
|
+
|
|
36
55
|
# Scan STDERR line by line for a segfault variant that is not attributed to the test file.
|
|
37
56
|
# A line starting with test_filename is a Unity-reported test-case result, not an OS crash.
|
|
38
57
|
# Checking each line individually avoids false negatives when attributed and bare segfault
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
require 'ceedling/file_wrapper'
|
|
9
9
|
require 'ceedling/partials/partials'
|
|
10
10
|
require 'ceedling/c_extractor/c_extractor_types'
|
|
11
|
+
require 'ceedling/c_extractor/c_extractor_constants'
|
|
11
12
|
|
|
12
13
|
class GeneratorPartials
|
|
13
14
|
|
|
@@ -55,8 +56,94 @@ class GeneratorPartials
|
|
|
55
56
|
return filepath
|
|
56
57
|
end
|
|
57
58
|
|
|
59
|
+
# A module's typedefs and aggregate (struct/enum/union) definitions are the same
|
|
60
|
+
# regardless of which functions a given test file chooses to test versus mock, so they're
|
|
61
|
+
# generated once here into their own header rather than by generate_header itself. Both
|
|
62
|
+
# the implementation and interface headers then simply #include this file, which means a
|
|
63
|
+
# module tested and mocked in the same test file -- each side producing its own generated
|
|
64
|
+
# header -- never ends up with two separate C definitions of the same type in one
|
|
65
|
+
# translation unit. Nothing is written and nil is returned when a module has no such
|
|
66
|
+
# content, since there's then nothing to share.
|
|
67
|
+
#
|
|
68
|
+
# @param name [String] Partial module name (used to form the filename and include guard)
|
|
69
|
+
# @param c_module [CExtractorTypes::CModule] Merged module with type_definitions/aggregate_definitions
|
|
70
|
+
# @param output_path [String] Directory shared with the implementation and interface headers
|
|
71
|
+
# @return [String, nil] The bare filename (for use as a sibling #include), or nil if nothing was generated
|
|
72
|
+
def generate_types(name:, c_module:, output_path:)
|
|
73
|
+
return nil if c_module.type_definitions.empty? && c_module.aggregate_definitions.empty?
|
|
74
|
+
|
|
75
|
+
header = @file_path_utils.form_partial_types_header_filename(name)
|
|
76
|
+
filepath = File.join(output_path, header)
|
|
77
|
+
|
|
78
|
+
# Binary mode: see generate_implementation above.
|
|
79
|
+
@file_wrapper.open(filepath, 'wb') do |file|
|
|
80
|
+
guard = FileWrapper.generate_include_guard(header)
|
|
81
|
+
file << "#ifndef #{guard}\n"
|
|
82
|
+
file << "#define #{guard}\n\n"
|
|
83
|
+
|
|
84
|
+
anything_emitted = false
|
|
85
|
+
pending_macros = []
|
|
86
|
+
|
|
87
|
+
c_module.element_sequence.each do |item|
|
|
88
|
+
next unless item.is_a?(CExtractorTypes::CStatement)
|
|
89
|
+
|
|
90
|
+
if c_module.macro_definitions.include?(item)
|
|
91
|
+
pending_macros << item
|
|
92
|
+
next
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
next unless type_defining?(item, c_module)
|
|
96
|
+
|
|
97
|
+
# A typedef/struct/enum moved here may depend on a macro (e.g. an array-size
|
|
98
|
+
# constant) that preceded it in the original source -- this header is #included
|
|
99
|
+
# before any macro generate_header later emits inline into the non-shared
|
|
100
|
+
# header, so that macro would otherwise be undefined here. Carrying it forward
|
|
101
|
+
# is safe: an identical #define may legally repeat, so generate_header's own
|
|
102
|
+
# unmodified inline copy causes no redefinition conflict.
|
|
103
|
+
pending_macros.each { |macro| file << macro.text << "\n" }
|
|
104
|
+
pending_macros.clear
|
|
105
|
+
|
|
106
|
+
file << item.text << "\n"
|
|
107
|
+
anything_emitted = true
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
file << "\n" if anything_emitted
|
|
111
|
+
file << "#endif // #{guard}\n\n"
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
return header
|
|
115
|
+
end
|
|
116
|
+
|
|
58
117
|
private
|
|
59
118
|
|
|
119
|
+
# A typedef or a non-typedef struct/enum/union tag definition establishes a type; C treats
|
|
120
|
+
# a second definition of the same type in one translation unit as a redefinition error even
|
|
121
|
+
# when the two definitions are textually identical. A macro statement carries no such
|
|
122
|
+
# restriction -- an identical #define may legally repeat -- so only membership in
|
|
123
|
+
# type_definitions or aggregate_definitions marks a CStatement as type-defining content
|
|
124
|
+
# that belongs in the shared types header instead of being emitted inline.
|
|
125
|
+
def type_defining?(item, c_module)
|
|
126
|
+
c_module.type_definitions.include?(item) || c_module.aggregate_definitions.include?(item)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# A `CVariableDeclaration`'s `decorators` mixes storage-class specifiers (`static`,
|
|
130
|
+
# `extern`) with base-type qualifiers (`const`, `volatile`, `restrict`) in their
|
|
131
|
+
# original source order, since both kinds of keyword lead a declaration the same
|
|
132
|
+
# way. An `extern` declaration and the variable's own definition both need the
|
|
133
|
+
# qualifiers reasserted -- omitting them produces a type that disagrees with any
|
|
134
|
+
# other declaration of the same variable elsewhere in the same translation unit --
|
|
135
|
+
# but neither may carry a storage-class specifier: `static` would contradict the
|
|
136
|
+
# external linkage the Partial is establishing, and `extern` is supplied literally
|
|
137
|
+
# by the caller. Filtering to TYPE_QUALIFIER_KEYWORDS keeps only what belongs on
|
|
138
|
+
# both forms.
|
|
139
|
+
#
|
|
140
|
+
# @param decl [CExtractorTypes::CVariableDeclaration]
|
|
141
|
+
# @return [String] e.g. "volatile " / "const volatile " / "" (trailing space when non-empty)
|
|
142
|
+
def qualifier_prefix(decl)
|
|
143
|
+
quals = decl.decorators.select { |kw| CExtractorConstants::TYPE_QUALIFIER_KEYWORDS.include?(kw) }
|
|
144
|
+
quals.empty? ? '' : "#{quals.join(' ')} "
|
|
145
|
+
end
|
|
146
|
+
|
|
60
147
|
# Emit a partial header file.
|
|
61
148
|
#
|
|
62
149
|
# Iterates c_module.element_sequence to emit non-function items (macros, typedefs,
|
|
@@ -101,13 +188,21 @@ class GeneratorPartials
|
|
|
101
188
|
c_module.element_sequence.each do |item|
|
|
102
189
|
case item
|
|
103
190
|
when CExtractorTypes::CStatement
|
|
191
|
+
# Typedefs and aggregate definitions live in the shared types header generated by
|
|
192
|
+
# generate_types instead of here, so that content defines a type exactly once no
|
|
193
|
+
# matter how many of a module's generated headers end up in the same test file.
|
|
194
|
+
next if type_defining?(item, c_module)
|
|
104
195
|
io << item.text << "\n"
|
|
105
196
|
last_was_func = false
|
|
106
197
|
anything_emitted = true
|
|
107
198
|
when CExtractorTypes::CVariableDeclaration
|
|
108
199
|
next unless include_variables
|
|
109
|
-
# If there is no array involved, array_suffix collapses to an empty string
|
|
110
|
-
|
|
200
|
+
# If there is no array involved, array_suffix collapses to an empty string.
|
|
201
|
+
# A leading const/volatile/restrict on the original declaration (see
|
|
202
|
+
# qualifier_prefix) is part of the variable's type and belongs here too,
|
|
203
|
+
# so this extern agrees with the definition and any other declaration of
|
|
204
|
+
# the same variable elsewhere in the translation unit.
|
|
205
|
+
io << "extern #{qualifier_prefix(item)}#{item.type} #{item.name}#{item.array_suffix};\n"
|
|
111
206
|
last_was_func = false
|
|
112
207
|
anything_emitted = true
|
|
113
208
|
when CExtractorTypes::CFunctionDefinition, CExtractorTypes::CFunctionDeclaration
|
|
@@ -170,7 +265,12 @@ class GeneratorPartials
|
|
|
170
265
|
c_module.element_sequence.each do |item|
|
|
171
266
|
case item
|
|
172
267
|
when CExtractorTypes::CVariableDeclaration
|
|
173
|
-
|
|
268
|
+
# `item.text` is the declaration with its leading decorator run removed (see
|
|
269
|
+
# CExtractorDeclarations#extract_decorators), so a const/volatile/restrict
|
|
270
|
+
# qualifier on the base type is reapplied here via qualifier_prefix -- this
|
|
271
|
+
# definition's type must match the extern declaration emitted for the same
|
|
272
|
+
# variable in generate_header.
|
|
273
|
+
io << "#{qualifier_prefix(item)}#{item.text}\n"
|
|
174
274
|
last_was_func = false
|
|
175
275
|
anything_emitted = true
|
|
176
276
|
when CExtractorTypes::CFunctionDefinition
|