From a7594f86c57da9210e8f1633a3c83e9bb1b8eb15 Mon Sep 17 00:00:00 2001 From: Mark VanderVoord Date: Thu, 12 Mar 2026 17:24:06 -0400 Subject: [PATCH 1/2] bump to latest unity --- vendor/unity | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/unity b/vendor/unity index eb79bce1b..54fb72f50 160000 --- a/vendor/unity +++ b/vendor/unity @@ -1 +1 @@ -Subproject commit eb79bce1b5b242ff580ccc99d8d4660625e8ffa9 +Subproject commit 54fb72f5080f5bd30e94180fefba8b1fb54fcc5c From f26db88e0acbef2d6e52d461a8589fda74ab5e11 Mon Sep 17 00:00:00 2001 From: MrChocolateMoose Date: Fri, 13 Mar 2026 14:51:12 -0400 Subject: [PATCH 2/2] fix: strip null bytes from GCC output and handle empty YAML files Three bugs addressed that were previously worked around via a runtime monkey patch (RUBYOPT=-r ceedling_fix.rb): 1. SystemWrapper#shell_capture3: Strip null bytes from stdout/stderr captured via Open3.capture3. GCC -H and -E can inject null bytes that cause ArgumentError ('string contains null byte') in downstream file path operations and YAML parsing. 2. YamlWrapper#load: Return [] instead of false for empty YAML files. YAML.load('') returns false in Ruby; callers check .nil? || .empty? which raises NoMethodError on false. 3. PreprocessinatorExtractor#extract_file_as_array_from_expansion: Strip null bytes from GCC -E preprocessed output before line-by-line processing. Stale or freshly-generated expansion files may contain embedded nulls that break encoding operations. --- lib/ceedling/preprocessinator_extractor.rb | 5 +++++ lib/ceedling/system_wrapper.rb | 8 +++++++- lib/ceedling/yaml_wrapper.rb | 8 ++++++-- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/lib/ceedling/preprocessinator_extractor.rb b/lib/ceedling/preprocessinator_extractor.rb index c95ee9048..647f66f94 100644 --- a/lib/ceedling/preprocessinator_extractor.rb +++ b/lib/ceedling/preprocessinator_extractor.rb @@ -8,6 +8,7 @@ require 'ceedling/constants' require 'ceedling/encodinator' require 'ceedling/parsing_parcels' +require 'stringio' class PreprocessinatorExtractor @@ -80,6 +81,10 @@ class PreprocessinatorExtractor # `input` must have the interface of IO -- StringIO for testing or File in typical use def extract_file_as_array_from_expansion(input, filepath) + # Strip null bytes that GCC -E may embed in preprocessed output. + # Null bytes cause encoding errors in downstream line processing. + content = input.read.to_s.gsub("\0", "") + input = StringIO.new(content) ## ## Iterate through all lines and alternate between extract and ignore modes. diff --git a/lib/ceedling/system_wrapper.rb b/lib/ceedling/system_wrapper.rb index cc8e61230..aa52a2404 100644 --- a/lib/ceedling/system_wrapper.rb +++ b/lib/ceedling/system_wrapper.rb @@ -75,6 +75,12 @@ def shell_capture3(command:, boom:false) stdout, stderr, status = Open3.capture3(command) + # Strip null bytes that some tools (e.g. GCC -H / -E) may inject into output. + # Null bytes cause ArgumentError ("string contains null byte") in downstream + # processing such as file path operations and YAML parsing. + stdout = stdout.to_s.gsub("\0", "") + stderr = stderr.to_s.gsub("\0", "") + # If boom, then capture the actual exit code. # Otherwise, leave it as zero as though execution succeeded. exit_code = status.exitstatus.freeze if boom and !status.nil? @@ -87,7 +93,7 @@ def shell_capture3(command:, boom:false) output: (stdout + stderr).to_s.freeze, # Individual streams for detailed logging - stdout: stdout.freeze, #TODO PROBABLY DROP THESE TOO + stdout: stdout.freeze, stderr: stderr.freeze, # Relay full Process::Status diff --git a/lib/ceedling/yaml_wrapper.rb b/lib/ceedling/yaml_wrapper.rb index ad434ca65..7358416a0 100644 --- a/lib/ceedling/yaml_wrapper.rb +++ b/lib/ceedling/yaml_wrapper.rb @@ -14,10 +14,14 @@ class YamlWrapper def load(filepath) source = ERB.new(File.read(filepath)).result begin - return YAML.load(source, aliases: true) + result = YAML.load(source, aliases: true) rescue ArgumentError - return YAML.load(source) + result = YAML.load(source) end + # YAML.load("") returns false in Ruby, not nil or []. + # Callers typically check `result.nil? || result.empty?` which raises + # NoMethodError on false. Return [] for empty/false results instead. + result == false ? [] : result end def load_string(source)