Skip to content

Commit efc3c2d

Browse files
committed
Merge branch 'feature/log_improvements'
2 parents bc81129 + 1eac900 commit efc3c2d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+470
-465
lines changed

Gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,4 @@ gem "thor", "~> 1.3"
2222
gem "deep_merge", "~> 1.2"
2323
gem "unicode-display_width", "~> 3.1"
2424
gem "erb", "~> 4.0"
25+
gem "parallel", "~> 1.26"

assets/project.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@
142142

143143
# Configuration Options specific to CMock. See CMock docs for details
144144
:cmock:
145-
# Core conffiguration
145+
# Core configuration
146146
:plugins: # What plugins should be used by CMock?
147147
- :ignore
148148
- :callback
@@ -166,8 +166,8 @@
166166
- __stdcall
167167
- __cdecl
168168
- __fastcall
169-
:treat_externs: :exclude # the options being :include or :exclud
170-
:treat_inlines: :exclude # the options being :include or :exclud
169+
:treat_externs: :exclude # the options being :include or :exclude
170+
:treat_inlines: :exclude # the options being :include or :exclude
171171

172172
# Type handling configuration
173173
#:unity_helper_path: '' # specify a string of where to find a unity_helper.h file to discover custom type assertions

assets/test_example_file_crash.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ void test_add_numbers_adds_numbers(void) {
2020
void test_add_numbers_will_fail(void) {
2121
// Platform-independent way of forcing a crash
2222
// NOTE: Avoid `nullptr` as it is a keyword in C23
23-
uint32_t* null_ptr = (void*) 0;
24-
uint32_t i = *null_ptr;
23+
uint32_t* a_null_pointer = (void*)0;
24+
uint32_t i = *a_null_pointer;
2525
TEST_ASSERT_EQUAL_INT(2, add_numbers(i,2));
2626
}

bin/cli.rb

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -219,10 +219,8 @@ def help(command=nil)
219219
<<-LONGDESC
220220
`ceedling new` creates a new project structure.
221221
222-
NAME is required and will be the containing directory for the new project.
223-
224-
DEST is an optional directory path for the new project (e.g. <DEST>/<name>).
225-
The default is your working directory. Nonexistent paths will be created.
222+
DEST is an optional root path for the new project (e.g. <DEST>/project.yml).
223+
The default is your working directory. Non-existent paths will be created.
226224
227225
Notes on Optional Flags:
228226
@@ -232,7 +230,7 @@ def help(command=nil)
232230
new project.
233231
LONGDESC
234232
) )
235-
def new(name, dest=nil)
233+
def new(dest=nil)
236234
require 'version' # lib/version.rb for TAG constant
237235

238236
# Get unfrozen copies so we can add / modify
@@ -241,7 +239,7 @@ def new(name, dest=nil)
241239

242240
_options[:verbosity] = options[:debug] ? VERBOSITY_DEBUG : nil
243241

244-
@handler.new_project( ENV, @app_cfg, Ceedling::Version::TAG, _options, name, _dest )
242+
@handler.new_project( ENV, @app_cfg, Ceedling::Version::TAG, _options, _dest )
245243
end
246244

247245
desc "upgrade PATH", "Upgrade vendored installation of Ceedling for a project at PATH"

bin/cli_handler.rb

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,21 @@ def app_help(env, app_cfg, options, command, &thor_help)
5353
return if !command.nil?
5454

5555
# If project configuration is available, also display Rake tasks
56-
@path_validator.standardize_paths( options[:project], *options[:mixin], )
57-
return if !@projectinator.config_available?( filepath:options[:project], env:env )
58-
59-
list_rake_tasks(
60-
env:env,
61-
app_cfg: app_cfg,
62-
filepath: options[:project],
63-
mixins: options[:mixin],
64-
# Silent Ceedling loading unless debug verbosity
65-
silent: !(verbosity == Verbosity::DEBUG)
66-
)
56+
@path_validator.standardize_paths( options[:project], *options[:mixin] )
57+
if @projectinator.config_available?( filepath:options[:project], env:env )
58+
list_rake_tasks(
59+
env:env,
60+
app_cfg: app_cfg,
61+
filepath: options[:project],
62+
mixins: options[:mixin],
63+
# Silent Ceedling loading unless debug verbosity
64+
silent: !(verbosity == Verbosity::DEBUG)
65+
)
66+
else
67+
# If no project configuration is available then note why we aren't displaying more
68+
msg = "Run help commands in a directory with a project file to list additional options"
69+
@loginator.log( msg, Verbosity::NORMAL, LogLabels::NOTICE )
70+
end
6771

6872
version = @helper.manufacture_app_version( app_cfg )
6973

@@ -79,18 +83,17 @@ def rake_help(env:, app_cfg:)
7983
end
8084

8185

82-
def new_project(env, app_cfg, ceedling_tag, options, name, dest)
86+
def new_project(env, app_cfg, ceedling_tag, options, dest)
8387
@helper.set_verbosity( options[:verbosity] )
8488

8589
@path_validator.standardize_paths( dest )
8690

87-
# If destination is nil, reassign it to name
88-
# Otherwise, join the destination and name into a new path
89-
dest = dest.nil? ? ('./' + name) : File.join( dest, name )
91+
# If destination is nil, assume it's the working directory
92+
dest ||= '.'
9093

9194
# Check for existing project (unless --force)
9295
if @helper.project_exists?( dest, :|, DEFAULT_PROJECT_FILENAME, 'src', 'test' )
93-
msg = "It appears a project already exists at #{dest}/. Use --force to destroy it and create a new project."
96+
msg = "It appears a project already exists at \"#{dest}/\"! Use --force to destroy it and create a new project."
9497
raise msg
9598
end unless options[:force]
9699

@@ -128,7 +131,7 @@ def new_project(env, app_cfg, ceedling_tag, options, name, dest)
128131
end
129132

130133
@loginator.log() # Blank line
131-
@loginator.log( "New project '#{name}' created at #{dest}/\n", Verbosity::NORMAL, LogLabels::TITLE )
134+
@loginator.log( "New project created at #{dest}/\n", Verbosity::NORMAL, LogLabels::TITLE )
132135
end
133136

134137

@@ -228,8 +231,10 @@ def build(env:, app_cfg:, options:{}, tasks:)
228231
CException => #{_version.cexception_tag}
229232
VERSION
230233

231-
@loginator.log( '', Verbosity::OBNOXIOUS )
232-
@loginator.log( version, Verbosity::OBNOXIOUS, LogLabels::CONSTRUCT )
234+
@loginator.lazy( Verbosity::OBNOXIOUS )
235+
@loginator.lazy( Verbosity::OBNOXIOUS, LogLabels::CONSTRUCT ) do
236+
version
237+
end
233238

234239
@helper.load_ceedling(
235240
config: config,

bin/cli_helper.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ def which_ceedling?(env:, config:{}, app_cfg:)
108108

109109
# Environment variable
110110
if !env['WHICH_CEEDLING'].nil?
111-
@loginator.log( " > Set which Ceedling using environment variable WHICH_CEEDLING", Verbosity::OBNOXIOUS )
111+
@loginator.log( " > Set which Ceedling using environment variable WHICH_CEEDLING", Verbosity::OBNOXIOUS )
112112
which_ceedling = env['WHICH_CEEDLING'].strip()
113113
which_ceedling = :gem if (which_ceedling.casecmp( 'gem' ) == 0)
114114
end
@@ -118,7 +118,7 @@ def which_ceedling?(env:, config:{}, app_cfg:)
118118
value, _ = @config_walkinator.fetch_value( :project, :which_ceedling, hash:config )
119119
if !value.nil?
120120
which_ceedling = value.strip()
121-
@loginator.log( " > Set which Ceedling from config :project ↳ :which_ceedling => #{which_ceedling}", Verbosity::OBNOXIOUS )
121+
@loginator.lazy( Verbosity::OBNOXIOUS ) { " > Set which Ceedling from config :project ↳ :which_ceedling => #{which_ceedling}" }
122122
which_ceedling = :gem if (which_ceedling.casecmp( 'gem' ) == 0)
123123
end
124124
end
@@ -139,7 +139,7 @@ def which_ceedling?(env:, config:{}, app_cfg:)
139139

140140
# If we're launching from the gem, return :gem and initial Rakefile path
141141
if which_ceedling == :gem
142-
@loginator.log( " > Launching Ceedling from #{app_cfg[:ceedling_root_path]}/", Verbosity::OBNOXIOUS )
142+
@loginator.lazy( Verbosity::OBNOXIOUS ) { " > Launching Ceedling from #{app_cfg[:ceedling_root_path]}/" }
143143
return which_ceedling, app_cfg[:ceedling_rakefile_filepath]
144144
end
145145

@@ -161,7 +161,7 @@ def which_ceedling?(env:, config:{}, app_cfg:)
161161
# Update variable to full application start path
162162
ceedling_path = app_cfg[:ceedling_rakefile_filepath]
163163

164-
@loginator.log( " > Launching Ceedling from #{app_cfg[:ceedling_root_path]}/", Verbosity::OBNOXIOUS )
164+
@loginator.lazy( Verbosity::OBNOXIOUS ) { " > Launching Ceedling from #{app_cfg[:ceedling_root_path]}/" }
165165

166166
return :path, ceedling_path
167167
end

bin/mixinator.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,14 @@ def mixin(builtins:, config:, mixins:)
107107
_mixin = @yaml_wrapper.load( filepath )
108108

109109
# Log what filepath we used for this mixin
110-
@loginator.log( " + Merging #{'(empty) ' if _mixin.nil?}#{source} mixin using #{filepath}", Verbosity::OBNOXIOUS )
110+
@loginator.lazy( Verbosity::OBNOXIOUS ) { " + Merging #{'(empty) ' if _mixin.nil?}#{source} mixin using #{filepath}" }
111111

112112
# Reference mixin from built-in hash-based mixins
113113
else
114114
_mixin = builtins[filepath.to_sym()]
115115

116116
# Log built-in mixin we used
117-
@loginator.log( " + Merging built-in mixin '#{filepath}' from #{source}", Verbosity::OBNOXIOUS )
117+
@loginator.lazy( Verbosity::OBNOXIOUS ) { " + Merging built-in mixin '#{filepath}' from #{source}" }
118118
end
119119

120120
# Hnadle an empty mixin (it's unlikely but logically coherent and a good safety check)

bin/projectinator.rb

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,7 @@ def load(filepath:nil, env:{}, silent:false)
5151
config = load_and_log( _filepath, "from working directory", silent )
5252
return _filepath, config
5353

54-
# If no user provided filepath and the default filepath does not exist,
55-
# we have a big problem
54+
# If no user-provided filepath and the default filepath does not exist, we have a big problem
5655
else
5756
raise "No project filepath provided and default #{DEFAULT_PROJECT_FILEPATH} not found"
5857
end
@@ -221,11 +220,11 @@ def load_and_log(filepath, method, silent)
221220

222221
# Log what the heck we loaded
223222
if !silent
224-
msg = "Loaded #{'(empty) ' if config.empty?}project configuration #{method}.\n"
225-
msg += " > Using: #{filepath}\n"
226-
msg += " > Working directory: #{Dir.pwd()}"
227-
228-
@loginator.log( msg, Verbosity::NORMAL, LogLabels::CONSTRUCT )
223+
@loginator.lazy( Verbosity::NORMAL, LogLabels::CONSTRUCT ) do
224+
"Loaded #{'(empty) ' if config.empty?}project configuration #{method}.\n" +
225+
" > Using: #{filepath}\n" +
226+
" > Working directory: #{Dir.pwd()}"
227+
end
229228
end
230229

231230
return config

ceedling.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ Ceedling projects are created with a YAML configuration file. A variety of conve
4545
s.add_dependency "diy", "~> 1.1"
4646
s.add_dependency "constructor", "~> 2"
4747
s.add_dependency "unicode-display_width", "~> 3.1"
48+
s.add_dependency "parallel", "~> 1.26"
4849

4950
# Files needed from submodules
5051
s.files = []

lib/ceedling/batchinator.rb

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# =========================================================================
2+
# Ceedling - Test-Centered Build System for C
3+
# ThrowTheSwitch.org
4+
# Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
5+
# SPDX-License-Identifier: MIT
6+
# =========================================================================
7+
8+
require 'benchmark'
9+
require 'parallel'
10+
11+
class Batchinator
12+
13+
constructor :configurator, :loginator, :reportinator
14+
15+
def setup
16+
@queue = Queue.new
17+
end
18+
19+
# Neaten up a build step with progress message and some scope encapsulation
20+
def build_step(msg, heading: true, &block)
21+
if heading
22+
msg = @reportinator.generate_heading( @loginator.decorate( msg, LogLabels::RUN ) )
23+
else # Progress message
24+
msg = "\n" + @reportinator.generate_progress( @loginator.decorate( msg, LogLabels::RUN ) )
25+
end
26+
27+
@loginator.log( msg )
28+
29+
yield # Execute build step block
30+
end
31+
32+
# Parallelize work to be done:
33+
# - Enqueue things (thread-safe)
34+
# - Spin up a number of worker threads within constraints of project file config and amount of work
35+
# - Each worker thread consumes one item from queue and runs the block against its details
36+
# - When the queue is empty, the worker threads wind down
37+
def exec(workload:, things:, &job_block)
38+
39+
batch_results = []
40+
sum_elapsed = 0.0
41+
42+
all_elapsed = Benchmark.realtime do
43+
# Determine number of worker threads to run
44+
workers = 1
45+
case workload
46+
when :compile
47+
workers = @configurator.project_compile_threads
48+
when :test
49+
workers = @configurator.project_test_threads
50+
else
51+
raise NameError.new("Unrecognized batch workload type: #{workload}")
52+
end
53+
54+
# Perform the actual parallelized work and collect the results and timing
55+
batch_results = Parallel.map(things, in_threads: workers) do |key, value|
56+
this_results = ''
57+
this_elapsed = Benchmark.realtime { this_results = job_block.call(key, value) }
58+
[this_results, this_elapsed]
59+
end
60+
61+
# Separate the elapsed time and results
62+
if batch_results.size > 0
63+
batch_results, batch_elapsed = batch_results.transpose
64+
sum_elapsed = batch_elapsed.sum()
65+
end
66+
end
67+
68+
# Report the timing if requested
69+
@loginator.lazy(Verbosity::OBNOXIOUS) do
70+
"\nBatch Elapsed: (All: %.3fsec Sum: %.3fsec)\n" % [all_elapsed, sum_elapsed]
71+
end
72+
73+
batch_results
74+
end
75+
end
76+

0 commit comments

Comments
 (0)