diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 0b724accc..680cf5cb6 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -1,9 +1,15 @@
+# =========================================================================
+# Ceedling - Test-Centered Build System for C
+# ThrowTheSwitch.org
+# Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+# SPDX-License-Identifier: MIT
+# =========================================================================
+
---
-# Continuous Integration Workflow: Test case suite run + validation build check
+# Continuous Integration Workflow
name: CI
-# Controls when the action will run.
-# Triggers the workflow on push or pull request events but only for the master branch
+# Triggers the workflow on push or pull request events for master & test branches
on:
push:
branches:
@@ -13,40 +19,144 @@ on:
branches: [ master ]
workflow_dispatch:
+
+# Needed by softprops/action-gh-release
+permissions:
+ # Allow built gem file push to Github release
+ contents: write
+
+
jobs:
- # Job: Unit test suite
- unit-tests:
- name: "Unit Tests"
+ # Job: Linux unit test suite
+ unit-tests-linux:
+ name: "Linux Test Suite"
runs-on: ubuntu-latest
strategy:
+ fail-fast: false
matrix:
- ruby: ['2.7', '3.0', '3.1']
+ ruby: ['3.0', '3.1', '3.2']
steps:
- # Install Binutils, Multilib, etc
- - name: Install C dev Tools
+ # Use a cache for our tools to speed up testing
+ - uses: actions/cache@v4
+ with:
+ path: vendor/bundle
+ key: bundle-use-ruby-${{ matrix.os }}-${{ matrix.ruby-version }}-${{ hashFiles('**/Gemfile.lock') }}
+ restore-keys: |
+ bundle-use-ruby-${{ matrix.os }}-${{ matrix.ruby-version }}-
+
+ # Checks out repository under $GITHUB_WORKSPACE
+ - name: Checkout Latest Repo
+ uses: actions/checkout@v4
+ with:
+ submodules: recursive
+
+ # Setup Ruby Testing Tools to do tests on multiple ruby version
+ - name: Setup Ruby Testing Tools
+ uses: ruby/setup-ruby@v1
+ with:
+ ruby-version: ${{ matrix.ruby }}
+
+ # Install Ruby Testing Tools (Bundler version should match the one in Gemfile.lock)
+ - name: Install Ruby Testing Tools
+ run: |
+ gem install rspec
+ gem install rubocop -v 0.57.2
+ gem install bundler -v "$(grep -A 1 "BUNDLED WITH" Gemfile.lock | tail -n 1)"
+ bundle update
+ bundle install
+
+ # Install gdb for backtrace feature testing
+ - name: Install gdb for Backtrace Feature Testing
run: |
sudo apt-get update -qq
- sudo apt-get install --assume-yes --quiet gcc-multilib
- sudo apt-get install -qq gcc-avr binutils-avr avr-libc gdb
+ sudo apt-get install --assume-yes --quiet gdb
- # Install GCovr
- - name: Install GCovr
+ # Install GCovr for Gcov plugin
+ - name: Install GCovr for Gcov Plugin Tests
run: |
sudo pip install gcovr
+ # Install ReportGenerator for Gcov plugin
+ # Fix PATH before tool installation
+ # https://stackoverflow.com/questions/59010890/github-action-how-to-restart-the-session
+ - name: Install ReportGenerator for Gcov Plugin Tests
+ run: |
+ mkdir --parents $HOME/.dotnet/tools
+ echo "$HOME/.dotnet/tools" >> $GITHUB_PATH
+ dotnet tool install --global dotnet-reportgenerator-globaltool
+
+ # Run Tests
+ - name: Run All Self Tests
+ run: |
+ rake ci
+
+ # Build & Install Gem
+ - name: Build and Install Gem
+ run: |
+ gem build ceedling.gemspec
+ gem install --local ceedling-*.gem
+
+ # Run temp_sensor
+ - name: Run Tests on temp_sensor Project
+ run: |
+ cd examples/temp_sensor
+ ceedling test:all
+ cd ../..
+
+ # Run FFF Plugin Tests
+ - name: Run Tests on FFF Plugin
+ run: |
+ cd plugins/fff
+ rake
+ cd ../..
+
+ # Run Module Generator Plugin Tests
+ - name: Run Tests on Module Generator Plugin
+ run: |
+ cd plugins/module_generator
+ rake
+ cd ../..
+
+ # Run Dependencies Plugin Tests
+ - name: Run Tests on Dependencies Plugin
+ run: |
+ cd plugins/dependencies
+ rake
+ cd ../..
+
+ # Job: Windows unit test suite
+ unit-tests-windows:
+ name: "Windows Test Suite"
+ runs-on: windows-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ ruby: ['3.0', '3.1', '3.2']
+ steps:
+ # Use a cache for our tools to speed up testing
+ - uses: actions/cache@v4
+ with:
+ path: vendor/bundle
+ key: bundle-use-ruby-${{ matrix.os }}-${{ matrix.ruby-version }}-${{ hashFiles('**/Gemfile.lock') }}
+ restore-keys: |
+ bundle-use-ruby-${{ matrix.os }}-${{ matrix.ruby-version }}-
+
# Checks out repository under $GITHUB_WORKSPACE
- name: Checkout Latest Repo
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
with:
submodules: recursive
# Setup Ruby Testing Tools to do tests on multiple ruby version
- - name: Setup Ruby Testing Tools
+ - name: Set Up Ruby Testing Tools
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
- # Install Ruby Testing Tools (Bundler version should match the one in Gemfile.lock)
+
+ # Install Ruby Testing Tools
+ # Bundler version should match the one in Gemfile.lock
- name: Install Ruby Testing Tools
+ shell: bash
run: |
gem install rspec
gem install rubocop -v 0.57.2
@@ -54,28 +164,124 @@ jobs:
bundle update
bundle install
+ # Install GCovr for Gcov plugin test
+ - name: Install GCovr for Gcov Plugin Tests
+ run: |
+ pip install gcovr
+
+ # Install ReportGenerator for Gcov plugin test
+ - name: Install ReportGenerator for Gcov Plugin Tests
+ run: |
+ dotnet tool install --global dotnet-reportgenerator-globaltool
+
# Run Tests
- name: Run All Self Tests
run: |
- bundle exec rake ci
+ rake ci
# Build & Install Gem
- - name: Build and Install Gem
+ - name: Build and Install Gem
run: |
gem build ceedling.gemspec
gem install --local ceedling-*.gem
- # Run Blinky
- # Disabled because it's set up for avr-gcc
- #- name: Run Tests On Blinky Project
- # run: |
- # cd examples/blinky
- # ceedling module:create[someNewModule] module:destroy[someNewModule] test:all
- # cd ../..
-
- # Run Temp Sensor
- - name: Run Tests On Temp Sensor Project
+ # Run temp_sensor example project
+ - name: Run Tests on temp_sensor Project
run: |
cd examples/temp_sensor
- ceedling module:create[someNewModule] module:destroy[someNewModule] test:all
+ ceedling test:all
+ cd ../..
+
+ # Run FFF Plugin Tests
+ - name: Run Tests on FFF Plugin
+ run: |
+ cd plugins/fff
+ rake
+ cd ../..
+
+ # Run Module Generator Plugin Tests
+ - name: Run Tests on Module Generator Plugin
+ run: |
+ cd plugins/module_generator
+ rake
+ cd ../..
+
+ # Run Dependencies Plugin Tests
+ - name: Run Tests on Dependencies Plugin
+ run: |
+ cd plugins/dependencies
+ rake
cd ../..
+
+ # Job: Automatic Minor Release
+ auto-release:
+ name: "Automatic Minor Release"
+ needs:
+ - unit-tests-linux
+ - unit-tests-windows
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ ruby: [3.2]
+
+ steps:
+ # Checks out repository under $GITHUB_WORKSPACE
+ - name: Checkout Latest Repo
+ uses: actions/checkout@v4
+ with:
+ submodules: recursive
+
+ # Set Up Ruby Tools
+ - name: Set Up Ruby Tools
+ uses: ruby/setup-ruby@v1
+ with:
+ ruby-version: ${{ matrix.ruby }}
+
+ # Generate the Version + Hash Name
+ - name: Version
+ id: versions
+ shell: bash
+ run: |
+ echo "short_ver=$(ruby ./lib/ceedling/version.rb)" >> $GITHUB_ENV
+ echo "full_ver=$(ruby ./lib/ceedling/version.rb)-$(git rev-parse --short HEAD)" >> $GITHUB_ENV
+
+ # Build Gem
+ - name: Build Gem
+ run: |
+ gem build ceedling.gemspec
+
+ # Create Unofficial Release
+ - name: Create Pre-Release
+ uses: actions/create-release@v1
+ id: create_release
+ with:
+ draft: false
+ prerelease: true
+ release_name: ${{ env.full_ver }}
+ tag_name: ${{ env.full_ver }}
+ body: "Automatic pre-release for ${{ env.full_ver }}"
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
+
+ # Post Gem to Unofficial Release
+ - name: Upload Pre-Release Gem
+ uses: actions/upload-release-asset@v1
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
+ with:
+ upload_url: ${{ steps.create_release.outputs.upload_url }}
+ asset_path: ./ceedling-${{ env.short_ver }}.gem
+ asset_name: ceedling-${{ env.full_ver }}.gem
+ asset_content_type: test/x-gemfile
+
+ # - name: Upload Pre-Release Gem
+ # uses: softprops/action-gh-release@v2
+ # with:
+ # # repo_token: "${{ secrets.GITHUB_TOKEN }}"
+ # body: |
+ # [Release Notes](${{ github.workspace }}/docs/ReleaseNotes.md)
+ # name: ${{ env.full_ver }}
+ # prerelease: true
+ # files: |
+ # *.gem
+
diff --git a/.gitignore b/.gitignore
index 7224d1d97..256cceb26 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,10 +7,13 @@ out.fail
tags
*.taghl
-examples/blinky/build/
-examples/blinky/vendor/
examples/temp_sensor/vendor/
examples/temp_sensor/build/
+plugins/fff/examples/fff_example/build/
+plugins/module_generator/example/build/
+plugins/dependencies/example/boss/build/
+plugins/dependencies/example/boss/third_party/
+plugins/dependencies/example/supervisor/build/
ceedling.sublime-project
ceedling.sublime-workspace
diff --git a/.gitmodules b/.gitmodules
index 128aadf4d..e00535031 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -10,6 +10,3 @@
path = vendor/cmock
url = https://github.com/ThrowTheSwitch/CMock.git
branch = master
-[submodule "plugins/fake_function_framework"]
- path = plugins/fake_function_framework
- url = https://github.com/ElectronVector/fake_function_framework.git
diff --git a/.rubocop.yml b/.rubocop.yml
index 2d00ac807..59b714774 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -1,3 +1,10 @@
+# =========================================================================
+# Ceedling - Test-Centered Build System for C
+# ThrowTheSwitch.org
+# Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+# SPDX-License-Identifier: MIT
+# =========================================================================
+
# This is the configuration used to check the rubocop source code.
#inherit_from: .rubocop_todo.yml
diff --git a/Gemfile b/Gemfile
index a00f5e7f5..887f94857 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,6 +1,13 @@
+# =========================================================================
+# Ceedling - Test-Centered Build System for C
+# ThrowTheSwitch.org
+# Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+# SPDX-License-Identifier: MIT
+# =========================================================================
+
source "http://rubygems.org/"
-gem "bundler"
+gem "bundler", "~> 2.5"
gem "rake"
gem "rspec", "~> 3.8"
gem "require_all"
@@ -9,6 +16,7 @@ gem "diy"
gem "rr"
gem "thor"
gem "deep_merge"
+gem "unicode-display_width"
#these will be used if present, but ignored otherwise
#gem "curses"
diff --git a/Gemfile.lock b/Gemfile.lock
index 647a9fece..5cb2894c2 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -3,33 +3,37 @@ GEM
specs:
constructor (2.0.0)
deep_merge (1.2.2)
- diff-lcs (1.3)
+ diff-lcs (1.5.1)
diy (1.1.2)
constructor (>= 1.0.0)
- rake (13.0.6)
- require_all (1.3.3)
- rr (1.1.2)
- rspec (3.8.0)
- rspec-core (~> 3.8.0)
- rspec-expectations (~> 3.8.0)
- rspec-mocks (~> 3.8.0)
- rspec-core (3.8.0)
- rspec-support (~> 3.8.0)
- rspec-expectations (3.8.2)
+ rake (13.2.1)
+ require_all (3.0.0)
+ rr (3.1.0)
+ rspec (3.13.0)
+ rspec-core (~> 3.13.0)
+ rspec-expectations (~> 3.13.0)
+ rspec-mocks (~> 3.13.0)
+ rspec-core (3.13.0)
+ rspec-support (~> 3.13.0)
+ rspec-expectations (3.13.0)
diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.8.0)
- rspec-mocks (3.8.0)
+ rspec-support (~> 3.13.0)
+ rspec-mocks (3.13.1)
diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.8.0)
- rspec-support (3.8.0)
- thor (0.20.3)
+ rspec-support (~> 3.13.0)
+ rspec-support (3.13.1)
+ thor (1.3.1)
+ unicode-display_width (2.5.0)
PLATFORMS
ruby
+ x64-mingw-ucrt
x64-mingw32
+ x86_64-darwin-22
+ x86_64-linux
DEPENDENCIES
- bundler
+ bundler (~> 2.5)
constructor
deep_merge
diy
@@ -38,6 +42,7 @@ DEPENDENCIES
rr
rspec (~> 3.8)
thor
+ unicode-display_width
BUNDLED WITH
- 2.4.3
+ 2.5.10
diff --git a/README.md b/README.md
index e4c589970..01e8d95af 100644
--- a/README.md
+++ b/README.md
@@ -1,111 +1,617 @@
-Ceedling ![CI](https://github.com/ThrowTheSwitch/Ceedling/workflows/CI/badge.svg)
-========
-Ceedling is a build system for C projects that is something of an extension
-around Ruby’s Rake (make-ish) build system. Ceedling also makes TDD (Test-Driven Development)
-in C a breeze by integrating [CMock](https://github.com/throwtheswitch/cmock),
-[Unity](https://github.com/throwtheswitch/unity), and
-[CException](https://github.com/throwtheswitch/cexception) --
-three other awesome open-source projects you can’t live without if you're creating awesomeness
-in the C language. Ceedling is also extensible with a handy plugin mechanism.
+# Ceedling ![CI](https://github.com/ThrowTheSwitch/Ceedling/workflows/CI/badge.svg)
-Usage Documentation
-===================
+_May 8, 2024_ 🚚 **Ceedling 1.0** is a release candidate and will be
+shipping very soon. See the [Release Notes](#docs/ReleaseNotes.md) for an overview
+of a long list of improvements and fixes.
-Documentation and license info exists [in the repo in docs/](docs/CeedlingPacket.md)
+# 🌱 Ceedling is a handy-dandy build system for C projects
-Getting Started
-===============
+## Developer-friendly release _and_ test builds
-First make sure Ruby is installed on your system (if it's not already). Then, from a command prompt:
+Ceedling can build your release artifact but is especially adept at building
+unit test suites for your C projects — even in tricky embedded systems.
- > gem install ceedling
+Ceedling and its complementary pieces and parts are and always will be freely
+available and open source. **_[Ceedling Pro][ceedling-pro]_** is a growing list
+of paid products and services to help you do even more with these tools.
-(Alternate Installation for Those Planning to Be Ceedling Developers)
-======================================================================
+[ceedling-pro]: https://thingamabyte.com/ceedlingpro
- > git clone --recursive https://github.com/throwtheswitch/ceedling.git
- > cd ceedling
- > bundle install # Ensures you have all RubyGems needed
- > git submodule update --init --recursive # Updates all submodules
- > bundle exec rake # Run all Ceedling library tests
+⭐️ **Eager to just get going? Jump to
+[📚 Documentation & Learning](#-documentation--learning) and
+[🚀 Getting Started](#-getting-started).**
-If bundler isn't installed on your system or you run into problems, you might have to install it:
+Ceedling works the way developers want to work. It is flexible and entirely
+command-line driven. It drives code generation and command line tools for you.
+All generated and framework code is easy to see and understand.
- > sudo gem install bundler
+Ceedling’s features support all types of C development from low-level embedded
+to enterprise systems. No tool is perfect, but Ceedling can do a whole lot to
+help you and your team produce quality software.
-If you run into trouble running bundler and get messages like this `can't find gem
-bundler (>= 0.a) with executable bundle (Gem::GemNotFoundException)`, you may
-need to install a different version of bundler. For this please reference the
-version in the Gemfile.lock. An example based on the current Gemfile.lock is as
-followed:
+## Ceedling is a suite of tools
- > sudo gem install bundler -v 1.16.2
+Ceedling is also a suite of tools. It is the glue for bringing together three
+other awesome open-source projects you can’t live without if you‘re creating
+awesomeness in the C language.
-Creating A Project
-==================
+1. **[Unity]**, an [xUnit]-style test framework.
+1. **[CMock]**† , a code generating,
+ [function mocking & stubbing][test-doubles] kit for interaction-based testing.
+1. **[CException]**, a framework for adding simple exception handling to C projects
+ in the style of higher-order programming languages.
-Creating a project with Ceedling is easy. Simply tell ceedling the
-name of the project, and it will create a subdirectory called that
-name and fill it with a default directory structure and configuration.
+† Through a [plugin][FFF-plugin], Ceedling also supports [FFF] for
+[fake functions][test-doubles] as an alternative to CMock’s mocks and stubs.
- ceedling new YourNewProjectName
+## But, wait. There’s more.
-You can add files to your src and test directories and they will
-instantly become part of your test build. Need a different structure?
-You can start to tweak the `project.yml` file immediately with your new
-path or tool requirements.
+For simple project structures, Ceedling can build and test an entire project
+from just a few lines in its project configuration file.
-You can upgrade to the latest version of Ceedling at any time,
-automatically gaining access to the packaged Unity and CMock that
-come with it.
+Because it handles all the nitty-gritty of rebuilds and becuase of Unity and
+CMock, Ceedling makes [Test-Driven Development][TDD] in C a breeze.
- gem update ceedling
+Ceedling is also extensible with a simple plugin mechanism. It comes with a
+number of [built-in plugins][ceedling-plugins] for code coverage, test suite
+report generation, Continuous Integration features, IDE integration, release
+library builds & dependency management, and more.
-Documentation
-=============
+[Unity]: https://github.com/throwtheswitch/unity
+[xUnit]: https://en.wikipedia.org/wiki/XUnit
+[CMock]: https://github.com/throwtheswitch/cmock
+[CException]: https://github.com/throwtheswitch/cexception
+[TDD]: http://en.wikipedia.org/wiki/Test-driven_development
+[test-doubles]: https://blog.pragmatists.com/test-doubles-fakes-mocks-and-stubs-1a7491dfa3da
+[FFF]: https://github.com/meekrosoft/fff
+[FFF-plugin]: https://github.com/ElectronVector/fake_function_framework
+[ceedling-plugins]: docs/CeedlingPacket.md#ceedling-plugins
-Are you just getting started with Ceedling? Maybe you'd like your
-project to be installed with some of its handy documentation? No problem!
-You can do this when you create a new project.
+
- ceedling new --docs MyAwesomeProject
+# 🙋♀️ Need Help? Want to Help?
-Bonding Your Tools And Project
-==============================
+* Found a bug or want to suggest a feature?
+ **[Submit an issue][ceedling-issues]** at this repo.
+* Trying to understand features or solve a testing problem? Hit the
+ **[discussion forums][forums]**.
+* Paid training, customizations, and support contracts are available through
+ **[Ceedling Pro][ceedling-pro]**.
-Ceedling can deploy all of its guts into the project as well. This
-allows it to be used without having to worry about external dependencies.
-You don't have to worry about Ceedling changing for this particular
-project just because you updated your gems... no need to worry about
-changes in Unity or CMock breaking your build in the future. If you'd like
-to use Ceedling this way, tell it you want a local copy when you create
+The ThrowTheSwitch community follows a **[code of conduct](docs/CODE_OF_CONDUCT.md)**.
+
+Please familiarize yourself with our guidelines for **[contributing](docs/CONTRIBUTING.md)** to this project, be it code, reviews, documentation, or reports.
+
+Yes, work has begun on certified versions of the Ceedling suite of tools to be available through **[Ceedling Pro][ceedling-pro]**. [Reach out to ThingamaByte][thingama-contact] for more.
+
+[ceedling-issues]: https://github.com/ThrowTheSwitch/Ceedling/issues
+[forums]: https://www.throwtheswitch.org/forums
+[thingama-contact]: https://www.thingamabyte.com/contact
+
+
+
+# 🧑🍳 Sample Unit Testing Code
+
+While Ceedling can build your release artifact, its claim to fame is building and running tests suites.
+
+There’s a good chance you’re looking at Ceedling because of its test suite abilities. And, you’d probably like to see what that looks like, huh? Well, let’s cook you up some realistic examples of tested code and running Ceedling with that code.
+
+## First, we start with servings of source code to be tested…
+
+### Recipe.c
+
+```c
+#include "Recipe.h"
+#include "Kitchen.h"
+#include
+
+#define MAX_SPICE_COUNT (4)
+#define MAX_SPICE_AMOUNT_TSP (8.0f)
+
+static float spice_amount = 0;
+static uint8_t spice_count = 0;
+
+void Recipe_Reset(char* recipe, size_t size) {
+ memset(recipe, 0, size);
+ spice_amount = 0;
+ spice_count = 0;
+}
+
+// Add ingredients to a spice list string with amounts (tsp.)
+bool_t Recipe_BuildSpiceListTsp(char* list, size_t maxLen, SpiceId spice, float amount) {
+ if ((++spice_count > MAX_SPICE_COUNT) || ((spice_amount += amount) > MAX_SPICE_AMOUNT_TSP)) {
+ snprintf( list, maxLen, "Too spicy!" );
+ return FALSE;
+ }
+
+ // Kitchen_Ingredient() not shown
+ snprintf( list + strlen(list), maxLen, "%s\n", Kitchen_Ingredient( spice, amount, TEASPOON ) );
+ return TRUE;
+}
+```
+
+### Baking.c
+
+```c
+#include "Oven.h"
+#include "Time.h"
+#include "Baking.h"
+
+bool_t Baking_PreheatOven(float setTempF, duration_t timeout) {
+ float temperature = 0.0;
+ Timer* timer = Time_StartTimer( timeout );
+
+ Oven_SetTemperatureF( setTempF );
+
+ while (temperature < setTempF) {
+ Time_SleepMs( 250 );
+ if (Time_IsTimerExpired( timer )) break;
+ temperature = Oven_GetTemperatureReadingF();
+ }
+
+ return (temperature >= setTempF);
+}
+
+```
+
+## Next, a sprinkle of unit test code…
+
+Some of what Ceedling does is by naming conventions. See Ceedling’s [documentation](#-documentation--learning) for much more on this.
+
+### TestRecipe.c
+
+```c
+#include "unity.h" // Unity, unit test framework
+#include "Recipe.h" // By convention, Recipe.c is part of TestRecipe executable build
+#include "Kitchen.h" // By convention, Kitchen.c (not shown) is part of TestRecipe executable build
+
+char recipe[100];
+
+void setUp(void) {
+ // Execute reset before each test case
+ Recipe_Reset( recipe, sizeof(recipe) );
+}
+
+void test_Recipe_BuildSpiceListTsp_shouldBuildSpiceList(void) {
+ TEST_ASSERT_TRUE( Recipe_BuildSpiceListTsp( recipe, sizeof(recipe), OREGANO, 0.5 ) );
+ TEST_ASSERT_TRUE( Recipe_BuildSpiceListTsp( recipe, sizeof(recipe), ROSEMARY, 1.0 ) );
+ TEST_ASSERT_TRUE( Recipe_BuildSpiceListTsp( recipe, sizeof(recipe), THYME, 0.33 ) );
+ TEST_ASSERT_EQUAL_STRING( "1/2 tsp. Oregano\n1 tsp. Rosemary\n1/3 tsp. Thyme\n", recipe );
+}
+
+void test_Recipe_BuildSpiceListTsp_shouldFailIfTooMuchSpice(void) {
+ TEST_ASSERT_TRUE ( Recipe_BuildSpiceListTsp( recipe, sizeof(recipe), CORIANDER, 4.0 ) );
+ TEST_ASSERT_TRUE ( Recipe_BuildSpiceListTsp( recipe, sizeof(recipe), BLACK_PEPPER, 4.0 ) );
+ // Total spice = 8.0 + 0.1 tsp.
+ TEST_ASSERT_FALSE( Recipe_BuildSpiceListTsp( recipe, sizeof(recipe), BASIL, 0.1 ) );
+ TEST_ASSERT_EQUAL_STRING( "Too spicy!", recipe );
+}
+
+void test_Recipe_BuildSpiceListTsp_shouldFailIfTooManySpices(void) {
+ TEST_ASSERT_TRUE ( Recipe_BuildSpiceListTsp( recipe, sizeof(recipe), OREGANO, 1.0 ) );
+ TEST_ASSERT_TRUE ( Recipe_BuildSpiceListTsp( recipe, sizeof(recipe), CORIANDER, 1.0 ) );
+ TEST_ASSERT_TRUE ( Recipe_BuildSpiceListTsp( recipe, sizeof(recipe), BLACK_PEPPER, 1.0 ) );
+ TEST_ASSERT_TRUE ( Recipe_BuildSpiceListTsp( recipe, sizeof(recipe), THYME, 1.0 ) );
+ // Attempt to add 5th spice
+ TEST_ASSERT_FALSE( Recipe_BuildSpiceListTsp( recipe, sizeof(recipe), BASIL, 1.0 ) );
+ TEST_ASSERT_EQUAL_STRING( "Too spicy!", recipe );
+}
+```
+
+### TestBaking.c
+
+Let’s flavor our test code with a dash of mocks as well…
+
+```c
+#include "unity.h" // Unity, unit test framework
+#include "Baking.h" // By convention, Baking.c is part of TestBaking executable build
+#include "MockOven.h" // By convention, mock .h/.c code generated from Oven.h by CMock
+#include "MockTime.h" // By convention, mock .h/.c code generated from Time.h by CMock
+
+/*
+ * 🚫 This test will fail! Find the missing logic in `Baking_PreheatOven()`.
+ * (`Oven_SetTemperatureF()` returns success / failure.)
+ */
+void test_Baking_PreheatOven_shouldFailIfSettingOvenTemperatureFails(void) {
+ Timer timer; // Uninitialized struct
+
+ Time_StartTimer_ExpectAndReturn( TWENTY_MIN, &timer );
+
+ // Tell source code that setting the oven temperature did not work
+ Oven_SetTemperatureF_ExpectAndReturn( 350.0, FALSE );
+
+ TEST_ASSERT_FALSE( Baking_PreheatOven( 350.0, TWENTY_MIN ) );
+}
+
+void test_Baking_PreheatOven_shouldFailIfTimeoutExpires(void) {
+ Timer timer; // Uninitialized struct
+
+ Time_StartTimer_ExpectAndReturn( TEN_MIN, &timer );
+
+ Oven_SetTemperatureF_ExpectAndReturn( 200.0, TRUE );
+
+ // We only care that `sleep()` is called, not necessarily every call to it
+ Time_SleepMs_Ignore();
+
+ // Unrolled loop of timeout and temperature checks
+ Time_IsTimerExpired_ExpectAndReturn( &timer, FALSE );
+ Oven_GetTemperatureReadingF_ExpectAndReturn( 100.0 );
+ Time_IsTimerExpired_ExpectAndReturn( &timer, FALSE );
+ Oven_GetTemperatureReadingF_ExpectAndReturn( 105.0 );
+ Time_IsTimerExpired_ExpectAndReturn( &timer, FALSE );
+ Oven_GetTemperatureReadingF_ExpectAndReturn( 110.0 );
+ Time_IsTimerExpired_ExpectAndReturn( &timer, TRUE );
+
+ TEST_ASSERT_FALSE( Baking_PreheatOven( 200.0, TEN_MIN ) );
+}
+
+void test_Baking_PreheatOven_shouldSucceedAfterAWhile(void) {
+ Timer timer; // Uninitialized struct
+
+ Time_StartTimer_ExpectAndReturn( TEN_MIN, &timer );
+
+ Oven_SetTemperatureF_ExpectAndReturn( 400.0, TRUE );
+
+ // We only care that `sleep()` is called, not necessarily every call to it
+ Time_SleepMs_Ignore();
+
+ // Unrolled loop of timeout and temperature checks
+ Time_IsTimerExpired_ExpectAndReturn( &timer, FALSE );
+ Oven_GetTemperatureReadingF_ExpectAndReturn( 390.0 );
+ Time_IsTimerExpired_ExpectAndReturn( &timer, FALSE );
+ Oven_GetTemperatureReadingF_ExpectAndReturn( 395.0 );
+ Time_IsTimerExpired_ExpectAndReturn( &timer, FALSE );
+ Oven_GetTemperatureReadingF_ExpectAndReturn( 399.0 );
+ Time_IsTimerExpired_ExpectAndReturn( &timer, FALSE );
+ Oven_GetTemperatureReadingF_ExpectAndReturn( 401.0 );
+
+ TEST_ASSERT_TRUE( Baking_PreheatOven( 400.0, TEN_MIN ) );
+}
+```
+
+## Add a pinch of command line…
+
+See Ceedling’s [documentation](#-documentation--learning) for examples and everything you need to know about Ceedling’s configuration file options (not shown here).
+
+The super duper short version is that your project configuration file tells Ceedling where to find test and source files, what testing options you’re using, sets compilation symbols and build tool flags, enables your plugins, and configures your build tool command lines (Ceedling defaults to using the GNU compiler collection — which must be installed, if used).
+
+```shell
+ > ceedling test:all
+```
+
+## Voilà! Test results. `#ChefsKiss`
+
+The test results below are one of the last bits of logging Ceedling produces for a test suite build. Not shown here are all the steps for extracting build details, C code generation, and compilation and linking.
+
+```
+-------------------
+FAILED TEST SUMMARY
+-------------------
+[test/TestBaking.c]
+ Test: test_Baking_PreheatOven_shouldFailIfSettingOvenTemperatureFails
+ At line (7): "Function Time_SleepMs() called more times than expected."
+
+--------------------
+OVERALL TEST SUMMARY
+--------------------
+TESTED: 6
+PASSED: 5
+FAILED: 1
+IGNORED: 0
+```
+
+
+
+# 📚 Documentation & Learning
+
+A variety of options for [community-based support][TTS-help] exist.
+
+Training and support contracts are available through **_[Ceedling Pro][ceedling-pro]_**
+
+[TTS-help]: https://www.throwtheswitch.org/#help-section
+
+## Ceedling docs
+
+**[Usage help][ceedling-packet]** (a.k.a. _Ceedling Packet_), **[release notes][release-notes]**, **[breaking changes][breaking-changes]**, **[changelog][changelog]**, a variety of guides, and much more exists in **[docs/](docs/)**.
+
+## Library and courses
+
+[ThrowTheSwitch.org][TTS]:
+
+* Provides a small but useful **[library of resources and guides][library]** on testing and using the Ceedling suite of tools.
+* Discusses your **[options for running a test suite][running-options]**, particularly in the context of embedded systems.
+* Links to paid courses, **_[Dr. Surly’s School for Mad Scientists][courses]_**, that provide in-depth training on creating C unit tests and using Unity, CMock, and Ceedling to do so.
+
+## Online tutorial
+
+Matt Chernosky’s **[detailed tutorial][tutorial]** demonstrates using Ceedling to build a C project with test suite. As the tutorial is a number of years old, the content is a bit out of date. That said, it provides an excellent overview of a real project. Matt is the author of [FFF] and the [FFF plugin][FFF-plugin] for Ceedling.
+
+[ceedling-packet]: docs/CeedlingPacket.md
+[release-notes]: docs/ReleaseNotes.md
+[breaking-changes]: docs/BreakingChanges.md
+[changelog]: docs/Changelog.md
+[TTS]: https://throwtheswitch.org
+[library]: http://www.throwtheswitch.org/library
+[running-options]: http://www.throwtheswitch.org/build/which
+[courses]: http://www.throwtheswitch.org/dr-surlys-school
+[tutorial]: http://www.electronvector.com/blog/add-unit-tests-to-your-current-project-with-ceedling
+
+
+
+# 🚀 Getting Started
+
+👀 See the **_[Quick Start](docs/CeedlingPacket.md#quick-start)_** section in Ceedling’s core documentation, _Ceedling Packet_.
+
+## The basics
+
+### MadScienceLab Docker Image
+
+Fully packaged [Ceedling Docker images][docker-images] containing Ruby, Ceedling, the GCC toolchain, and more are also available. [Docker containers][docker-overview] provide self-contained, portable, well-managed alternative to local installation of tools like Ceedling.
+
+To run the _MadScienceLab_ container from your local terminal after [installing Docker][docker-install]:
+
+_Note: [Helper scripts are available][docker-image] to simplify your command line and access advanced features._
+
+```shell
+ > docker run -it --rm -v $PWD/my/project:/home/dev/project throwtheswitch/madsciencelab:latest
+```
+
+When the container launches it will drop you into a Z-shell command line that has access to all the tools and utilities available within the container.
+
+To run Ceedling from within the _MadScienceLab_ container’s shell and project working directory:
+
+```shell
+ > ceedling test:all
+```
+
+See the [Docker image documentation][docker-image] for all the details on how to use these containers.
+
+[docker-overview]: https://www.ibm.com/topics/docker
+[docker-install]: https://www.docker.com/products/docker-desktop/
+[docker-images]: https://hub.docker.com/r/throwtheswitch/madsciencelab
+
+### Local installation
+
+1. Install [Ruby]. (Only Ruby 3+ supported.)
+1. Install Ceedling. All supporting frameworks are included.
+ ```shell
+ > gem install ceedling
+ ```
+1. Begin crafting your project:
+ 1. Create an empty Ceedling project.
+ ```shell
+ > ceedling new []
+ ```
+ 1. Or, add a Ceedling project file to the root of an existing code project.
+1. Run tasks like so:
+ ```shell
+ > ceedling test:all release
+ ```
+
+### Example super-duper simple Ceedling configuration file
+
+```yaml
+:project:
+ :build_root: project/build/
+ :release_build: TRUE
+
+:paths:
+ :test:
+ - tests/**
+ :source:
+ - source/**
+ :include:
+ - inc/**
+```
+
+See _[CeedlingPacket][ceedling-packet]_ for all the details of your configuration file.
+
+Or, use Ceedling’s built-in `examples` & `example` commands to extract a sample project and reference its project file.
+
+[Ruby]: https://www.ruby-lang.org/
+
+## Using Ceedling’s command line (and related)
+
+### Command line help
+
+For an overview of all commands, it’s as easy as…
+
+```sh
+ > ceedling help
+```
+
+For a detailed explanation of a single command…
+
+```sh
+ > ceedling help
+```
+
+### Creating a project
+
+Creating a project with Ceedling is easy. Simply tell Ceedling the name of the
+project, and it will create a directory with that name and fill it with a
+default subdirectory structure and configuration file. An optional destination
+path is also possible.
+
+```shell
+ > ceedling new YourNewProjectName
+```
+
+You can add files to your `src/` and `test/` directories, and they will
+instantly become part of your test and/or release build. Need a different
+structure? You can modify the `project.yml` file with your new path or
+tooling setup.
+
+#### Installing local documentation
+
+Are you just getting started with Ceedling? Maybe you’d like your
+project to be installed with some of its handy [documentation](docs/)?
+No problem! You can do this when you create a new project…
+
+```shell
+ > ceedling new --docs MyAwesomeProject
+```
+
+#### Attaching a Ceedling version to your project
+
+Ceedling can be installed as a globally available Ruby gem. Ceedling can
+also deploy all its guts into your project instead. This allows it to
+be used without worrying about external dependencies. More importantly,
+you don’t have to worry about Ceedling changing outside of your project
+just because you updated your gems. No need to worry about changes in
+Unity or CMock breaking your build in the future.
+
+To use Ceedling this way, tell it you want a local copy when you create
your project:
- ceedling new --local YourNewProjectName
+```shell
+ > ceedling new --local YourNewProjectName
+```
+
+This will install all of Unity, CMock, CException, and Ceedling itself
+into a new folder `vendor/` inside your project `YourNewProjectName/`.
+It will create the same simple empty directory structure for you with
+`src/` and `test/` folders as the standard `new` command.
+
+### Running build & plugin tasks
+
+You can view all the build and plugin tasks available to you thanks to your
+Ceedling project file with `ceedling help`. Ceedling’s command line help
+provides a summary list from your project configuration if Ceedling is
+able to find your project file (`ceedling help help` for more on this).
+
+Running Ceedling build tasks tends to look like this…
+
+```shell
+ > ceedling test:all release
+```
+
+```shell
+ > ceedling gcov:all --verbosity=obnoxious --test-case=boot --mixin=code_cruncher_toolchain
+```
+
+### Upgrading / updating Ceedling
+
+You can upgrade to the latest version of Ceedling at any time, automatically
+gaining access to any accompanying updates to Unity, CMock, and CException.
+
+To update a locally installed gem…
+
+```shell
+ > gem update ceedling
+```
+
+Otherwise, if you are using the Docker image, you may upgrade by pulling
+a newer version of the image…
+
+```shell
+ > docker pull throwtheswitch/madsciencelab:
+```
+
+If you want to force a vendored version of Ceedling inside your project to
+upgrade to match your latest gem, no problem. Just do the following…
+
+```shell
+ > ceedling upgrade --local YourNewProjectName
+```
+
+Just like with the `new` command, an `upgrade` should be executed from
+within the root directory of your project.
+
+### Git integration
+
+Are you using Git? You might want Ceedling to create a `.gitignore`
+that ignores the build folder while retaining control of the artifacts
+folder. This will also add a `.gitkeep` file to your `test/support` folder.
+You can enable this by adding `--gitsupport` to your `new` call.
+
+```shell
+ > ceedling new --gitsupport YourNewProjectName
+```
+
+
+# 💻 Contributing to Ceedling Development
+
+## Alternate installation for development
+
+After installing Ruby…
+
+```shell
+ > git clone --recursive https://github.com/throwtheswitch/ceedling.git
+ > cd ceedling
+ > git submodule update --init --recursive
+ > bundle install
+```
+
+The Ceedling repository incorporates its supporting frameworks and some
+plugins via Git submodules. A simple clone may not pull in the latest
+and greatest.
+
+The `bundle` tool ensures you have all needed Ruby gems installed. If
+Bundler isn’t installed on your system or you run into problems, you
+might have to install it:
+
+```shell
+ > sudo gem install bundler
+```
+
+If you run into trouble running bundler and get messages like _can’t
+find gem bundler (>= 0.a) with executable bundle
+(Gem::GemNotFoundException)_, you may need to install a different
+version of Bundler. For this please reference the version in the
+Gemfile.lock.
+
+```shell
+ > sudo gem install bundler -v
+```
+
+## Running Ceedling’s self-tests
+
+Ceedling uses [RSpec] for its tests.
+
+To execute tests you may run the following from the root of your local
+Ceedling repository. This test suite build option balances test coverage
+with suite execution time.
+
+```shell
+ > rake spec
+```
+
+To run individual test files (Ceedling’s Ruby-based tests, that is) and
+perform other tasks, use the available Rake tasks. From the root of your
+local Ceedling repo, list those task like this:
+
+```shell
+ > rake -T
+```
-This will install all of Unity, CMock, and Ceedling into a new folder
-named `vendor` inside your project `YourNewProjectName`. It will still create
-the simple directory structure for you with `src` and `test` folders.
+[RSpec]: https://rspec.info
-SCORE!
+## Working in `bin/` vs. `lib/`
-If you want to force a locally installed version of Ceedling to upgrade
-to match your latest gem later, it's easy! Just issue the following command:
+Most of Ceedling’s functionality is contained in the application code residing
+in `lib/`. Ceedling’s command line handling, startup configuration, project
+file loading, and mixin handling are contained in a “bootloader” in `bin/`.
+The code in `bin/` is the source of the `ceedling` command line tool and
+launches the application from `lib/`.
- ceedling upgrade --local YourNewProjectName
+Depending on what you’re working on you may need to run Ceedling using
+a specialized approach.
-Just like the `new` command, it's called from the parent directory of your
-project.
+If you are only working in `lib/`, you can:
-Are you afraid of losing all your local changes when this happens? You can keep
-Ceedling from updating your project file by issuing `no_configs`.
+1. Run Ceedling using the `ceedling` command line utility you already have
+ installed. The code in `bin/` will run from your locally installed gem or
+ from within your Docker container and launch the Ceedling application for
+ you.
+1. Modify a project file by setting a path value for `:project` ↳ `:which_ceedling`
+ that points to the local copy of Ceedling you cloned from the Git repository.
+ See _CeedlingPacket_ for details.
- ceedling upgrade --local --no_configs TheProject
+If you are working in `bin/`, running `ceedling` at the command line will not
+call your modified code. Instead, you must execute the path to the executable
+`ceedling` in the `bin/` folder of the local Ceedling repository you are
+working on.
-Git Integration
-===============
-Are you using Git? You might want to automatically have Ceedling create a
-`gitignore` file for you by adding `--gitignore` to your `new` call.
-*HAPPY TESTING!*
diff --git a/Rakefile b/Rakefile
index bcdcdf2a9..6dfe1dc83 100644
--- a/Rakefile
+++ b/Rakefile
@@ -1,4 +1,11 @@
#!/usr/bin/env rake
+# =========================================================================
+# Ceedling - Test-Centered Build System for C
+# ThrowTheSwitch.org
+# Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+# SPDX-License-Identifier: MIT
+# =========================================================================
+
require 'bundler'
require 'rspec/core/rake_task'
@@ -7,5 +14,13 @@ RSpec::Core::RakeTask.new(:spec) do |t|
t.pattern = 'spec/**/*_spec.rb'
end
+Dir['spec/**/*_spec.rb'].each do |p|
+ base = File.basename(p,'.*').gsub('_spec','')
+ desc "rspec #{base}"
+ RSpec::Core::RakeTask.new("spec:#{base}") do |t|
+ t.pattern = p
+ end
+end
+
task :default => [:spec]
task :ci => [:spec]
diff --git a/assets/auto_link_deep_dependencies/src/a.c b/assets/auto_link_deep_dependencies/src/a.c
deleted file mode 100644
index 78b18e8ff..000000000
--- a/assets/auto_link_deep_dependencies/src/a.c
+++ /dev/null
@@ -1,7 +0,0 @@
-#include "a.h"
-#include "b.h"
-
-int function_from_a(int a)
-{
- return 2 * function_from_b(a);
-}
diff --git a/assets/auto_link_deep_dependencies/src/b.c b/assets/auto_link_deep_dependencies/src/b.c
deleted file mode 100644
index f40980081..000000000
--- a/assets/auto_link_deep_dependencies/src/b.c
+++ /dev/null
@@ -1,7 +0,0 @@
-#include "b.h"
-#include "c.h"
-
-int function_from_b(int b)
-{
- return 2 * function_from_c(b);
-}
diff --git a/assets/auto_link_deep_dependencies/src/c.c b/assets/auto_link_deep_dependencies/src/c.c
deleted file mode 100644
index 810aed80d..000000000
--- a/assets/auto_link_deep_dependencies/src/c.c
+++ /dev/null
@@ -1,8 +0,0 @@
-#include "c.h"
-#include "never_compiled.h"
-
-int function_from_c(int c)
-{
- function_never_compiled(2);
- return 2 * c;
-}
diff --git a/assets/auto_link_deep_dependencies/src/internal_inc/a.h b/assets/auto_link_deep_dependencies/src/internal_inc/a.h
deleted file mode 100644
index 8cde06168..000000000
--- a/assets/auto_link_deep_dependencies/src/internal_inc/a.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef A_H
-#define A_H
-
-int function_from_a(int a);
-
-#endif /* A_H */
\ No newline at end of file
diff --git a/assets/auto_link_deep_dependencies/src/internal_inc/b.h b/assets/auto_link_deep_dependencies/src/internal_inc/b.h
deleted file mode 100644
index 50035e04d..000000000
--- a/assets/auto_link_deep_dependencies/src/internal_inc/b.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef B_H
-#define B_H
-
-int function_from_b(int b);
-
-#endif /* B_H */
\ No newline at end of file
diff --git a/assets/auto_link_deep_dependencies/src/internal_inc/c.h b/assets/auto_link_deep_dependencies/src/internal_inc/c.h
deleted file mode 100644
index e481ee9bf..000000000
--- a/assets/auto_link_deep_dependencies/src/internal_inc/c.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef C_H
-#define C_H
-
-int function_from_c(int c);
-
-#endif /* C_H */
\ No newline at end of file
diff --git a/assets/auto_link_deep_dependencies/src/internal_inc/never_compiled.h b/assets/auto_link_deep_dependencies/src/internal_inc/never_compiled.h
deleted file mode 100644
index 85ab1e889..000000000
--- a/assets/auto_link_deep_dependencies/src/internal_inc/never_compiled.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef NEVER_COMPILED_H
-#define NEVER_COMPILED_H
-
-int function_never_compiled(int x);
-
-#endif /* NEVER_COMPILED_H */
\ No newline at end of file
diff --git a/assets/auto_link_deep_dependencies/src/never_compiled.c b/assets/auto_link_deep_dependencies/src/never_compiled.c
deleted file mode 100644
index 2e282bca0..000000000
--- a/assets/auto_link_deep_dependencies/src/never_compiled.c
+++ /dev/null
@@ -1,6 +0,0 @@
-#include "never_compiled.h"
-
-int function_never_compiled(int x)
-{
- never runed function
-}
\ No newline at end of file
diff --git a/assets/auto_link_deep_dependencies/test/test_a.c b/assets/auto_link_deep_dependencies/test/test_a.c
deleted file mode 100644
index 2fc6afbeb..000000000
--- a/assets/auto_link_deep_dependencies/test/test_a.c
+++ /dev/null
@@ -1,11 +0,0 @@
-#include "unity.h"
-#include "a.h"
-#include "mock_never_compiled.h"
-
-void setUp(void) {}
-void tearDown(void) {}
-
-void test_function_from_a_should_return_16(void) {
- function_never_compiled_ExpectAndReturn(2, 2);
- TEST_ASSERT_EQUAL(16, function_from_a(2));
-}
diff --git a/assets/auto_link_deep_dependencies/test/test_b.c b/assets/auto_link_deep_dependencies/test/test_b.c
deleted file mode 100644
index 82a36555a..000000000
--- a/assets/auto_link_deep_dependencies/test/test_b.c
+++ /dev/null
@@ -1,11 +0,0 @@
-#include "unity.h"
-#include "b.h"
-#include "mock_never_compiled.h"
-
-void setUp(void) {}
-void tearDown(void) {}
-
-void test_function_from_b_should_return_8(void) {
- function_never_compiled_ExpectAndReturn(2, 2);
- TEST_ASSERT_EQUAL(8, function_from_b(2));
-}
diff --git a/assets/auto_link_deep_dependencies/test/test_c.c b/assets/auto_link_deep_dependencies/test/test_c.c
deleted file mode 100644
index ac8b2d651..000000000
--- a/assets/auto_link_deep_dependencies/test/test_c.c
+++ /dev/null
@@ -1,11 +0,0 @@
-#include "unity.h"
-#include "c.h"
-#include "mock_never_compiled.h"
-
-void setUp(void) {}
-void tearDown(void) {}
-
-void test_function_from_c_should_return_4(void) {
- function_never_compiled_ExpectAndReturn(2, 2);
- TEST_ASSERT_EQUAL(4, function_from_c(2));
-}
diff --git a/assets/ceedling b/assets/ceedling
index 539308570..40cea2cd3 100755
--- a/assets/ceedling
+++ b/assets/ceedling
@@ -1,3 +1,9 @@
#!/bin/bash
+# =========================================================================
+# Ceedling - Test-Centered Build System for C
+# ThrowTheSwitch.org
+# Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+# SPDX-License-Identifier: MIT
+# =========================================================================
ruby vendor/ceedling/bin/ceedling $*
diff --git a/assets/default_gitignore b/assets/default_gitignore
index a9ebca2cf..492412d9f 100644
--- a/assets/default_gitignore
+++ b/assets/default_gitignore
@@ -1,5 +1,7 @@
-build/artifacts
-build/gcov
-build/logs
-build/temp
-build/test
+# Ignore the build/ directory in the root of the project.
+# Generally speaking, best practice is to omit generated files from revision control.
+/build/
+
+# But reserve the artifacts/ subdirectory for revision control.
+# Ceedling's notion of artifacts includes reports or release binaries you *may* want to revision.
+!/build/artifacts/
diff --git a/assets/example_file.c b/assets/example_file.c
index a50ae4bbe..631d5fefa 100644
--- a/assets/example_file.c
+++ b/assets/example_file.c
@@ -1,5 +1,16 @@
+/* =========================================================================
+ Ceedling - Test-Centered Build System for C
+ ThrowTheSwitch.org
+ Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+ SPDX-License-Identifier: MIT
+========================================================================= */
+
#include "example_file.h"
int add_numbers(int a, int b) {
return a + b;
}
+
+int difference_between_numbers(int a, int b) {
+ return a - b;
+}
diff --git a/assets/example_file.h b/assets/example_file.h
index dab6ee8bf..2e52d9827 100644
--- a/assets/example_file.h
+++ b/assets/example_file.h
@@ -1,6 +1,15 @@
+/* =========================================================================
+ Ceedling - Test-Centered Build System for C
+ ThrowTheSwitch.org
+ Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+ SPDX-License-Identifier: MIT
+========================================================================= */
+
#ifndef EXAMPLE_FILE_H
#define EXAMPLE_FILE_H
int add_numbers(int a, int b);
+int difference_between_numbers(int a, int b);
+
#endif /* EXAMPLE_FILE_H */
diff --git a/assets/example_file_call.c b/assets/example_file_call.c
index 9c3583798..00da661b8 100644
--- a/assets/example_file_call.c
+++ b/assets/example_file_call.c
@@ -1,3 +1,10 @@
+/* =========================================================================
+ Ceedling - Test-Centered Build System for C
+ ThrowTheSwitch.org
+ Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+ SPDX-License-Identifier: MIT
+========================================================================= */
+
#include "example_file_call.h"
#include "example_file.h"
diff --git a/assets/example_file_call.h b/assets/example_file_call.h
index a56815131..b1781f2e2 100644
--- a/assets/example_file_call.h
+++ b/assets/example_file_call.h
@@ -1,3 +1,10 @@
+/* =========================================================================
+ Ceedling - Test-Centered Build System for C
+ ThrowTheSwitch.org
+ Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+ SPDX-License-Identifier: MIT
+========================================================================= */
+
#ifndef EXAMPLE_FILE_CALL_H
#define EXAMPLE_FILE_CALL_H
diff --git a/assets/project_as_gem.yml b/assets/project_as_gem.yml
index 0442db2f0..891d9462b 100644
--- a/assets/project_as_gem.yml
+++ b/assets/project_as_gem.yml
@@ -1,84 +1,210 @@
----
-
-# Notes:
-# Sample project C code is not presently written to produce a release artifact.
-# As such, release build options are disabled.
-# This sample, therefore, only demonstrates running a collection of unit tests.
+# =========================================================================
+# Ceedling - Test-Centered Build System for C
+# ThrowTheSwitch.org
+# Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+# SPDX-License-Identifier: MIT
+# =========================================================================
+---
:project:
- :use_exceptions: FALSE
+ # how to use ceedling. If you're not sure, leave this as `gem` and `?`
+ :which_ceedling: gem
+ :ceedling_version: '?'
+
+ # optional features. If you don't need them, keep them turned off for performance
+ :use_mocks: TRUE
:use_test_preprocessor: TRUE
- :use_auxiliary_dependencies: TRUE
+ :use_backtrace: :none
+ :use_decorators: :auto #Decorate Ceedling's output text. Your options are :auto, :all, or :none
+
+ # tweak the way ceedling handles automatic tasks
:build_root: build
-# :release_build: TRUE
:test_file_prefix: test_
- :which_ceedling: gem
- :ceedling_version: '?'
:default_tasks:
- test:all
-#:test_build:
-# :use_assembly: TRUE
+ # performance options. If your tools start giving mysterious errors, consider
+ # dropping this to 1 to force single-tasking
+ :test_threads: 8
+ :compile_threads: 8
+
+ # enable release build (more details in release_build section below)
+ :release_build: FALSE
+
+# Specify where to find mixins and any that should be enabled automatically
+:mixins:
+ :enabled: []
+ :load_paths: []
+
+# further details to configure the way Ceedling handles test code
+:test_build:
+ :use_assembly: FALSE
-#:release_build:
-# :output: MyApp.out
-# :use_assembly: FALSE
+# further details to configure the way Ceedling handles release code
+:release_build:
+ :output: MyApp.out
+ :use_assembly: FALSE
+ :artifacts: []
+
+# Plugins are optional Ceedling features which can be enabled. Ceedling supports
+# a variety of plugins which may effect the way things are compiled, reported,
+# or may provide new command options. Refer to the readme in each plugin for
+# details on how to use it.
+:plugins:
+ :load_paths: []
+ :enabled:
+ #- beep # beeps when finished, so you don't waste time waiting for ceedling
+ - module_generator # handy for quickly creating source, header, and test templates
+ #- gcov # test coverage using gcov. Requires gcc, gcov, and a coverage analyzer like gcovr
+ #- bullseye # test coverage using bullseye. Requires bullseye for your platform
+ #- command_hooks # write custom actions to be called at different points during the build process
+ #- compile_commands_json_db # generate a compile_commands.json file
+ #- dependencies # automatically fetch 3rd party libraries, etc.
+ #- subprojects # managing builds and test for static libraries
+ #- fake_function_framework # use FFF instead of CMock
-:environment:
+ # Report options (You'll want to choose one stdout option, but may choose multiple stored options if desired)
+ #- report_build_warnings_log
+ #- report_tests_gtestlike_stdout
+ #- report_tests_ide_stdout
+ #- report_tests_log_factory
+ - report_tests_pretty_stdout
+ #- report_tests_raw_output_log
+ #- report_tests_teamcity_stdout
+# Specify which reports you'd like from the log factory
+:report_tests_log_factory:
+ :reports:
+ - json
+ - junit
+ - cppunit
+ - html
+
+# override the default extensions for your system and toolchain
:extension:
+ #:header: .h
+ #:source: .c
+ #:assembly: .s
+ #:dependencies: .d
+ #:object: .o
:executable: .out
+ #:testpass: .pass
+ #:testfail: .fail
+ #:subprojects: .a
+# This is where Ceedling should look for your source and test files.
+# see documentation for the many options for specifying this.
:paths:
:test:
- +:test/**
- -:test/support
:source:
- src/**
+ :include:
+ - src/** # In simple projects, this entry often duplicates :source
:support:
- test/support
:libraries: []
+# You can even specify specific files to add or remove from your test
+# and release collections. Usually it's better to use paths and let
+# Ceedling do the work for you!
+:files:
+ :test: []
+ :source: []
+
+# Compilation symbols to be injected into builds
+# See documentation for advanced options:
+# - Test name matchers for different symbols per test executable build
+# - Referencing symbols in multiple lists using advanced YAML
+# - Specifiying symbols used during test preprocessing
:defines:
- # in order to add common defines:
- # 1) remove the trailing [] from the :common: section
- # 2) add entries to the :common: section (e.g. :test: has TEST defined)
- :common: &common_defines []
:test:
- - *common_defines
- - TEST
- :test_preprocess:
- - *common_defines
- - TEST
+ - TEST # Simple list option to add symbol 'TEST' to compilation of all files in all test executables
+ :release: []
+
+ # Enable to inject name of a test as a unique compilation symbol into its respective executable build.
+ :use_test_definition: FALSE
+
+# Configure additional command line flags provided to tools used in each build step
+# :flags:
+# :release:
+# :compile: # Add '-Wall' and '--02' to compilation of all files in release target
+# - -Wall
+# - --O2
+# :test:
+# :compile:
+# '(_|-)special': # Add '-pedantic' to compilation of all files in all test executables with '_special' or '-special' in their names
+# - -pedantic
+# '*': # Add '-foo' to compilation of all files in all test executables
+# - -foo
+# Configuration Options specific to CMock. See CMock docs for details
:cmock:
- :mock_prefix: mock_
- :when_no_prototypes: :warn
- :enforce_strict_ordering: TRUE
- :plugins:
+ # Core conffiguration
+ :plugins: # What plugins should be used by CMock?
- :ignore
- :callback
- :treat_as:
+ :verbosity: 2 # the options being 0 errors only, 1 warnings and errors, 2 normal info, 3 verbose
+ :when_no_prototypes: :warn # the options being :ignore, :warn, or :erro
+
+ # File configuration
+ :mock_path: './build/mocks' # Subdirectory to store mocks when generated (default: mocks)
+ :skeleton_path: '' # Subdirectory to store stubs when generated (default: '')
+ :mock_prefix: 'mock_' # Prefix to append to filenames for mocks
+ :mock_suffix: '' # Suffix to append to filenames for mocks
+
+ # Parser configuration
+ :strippables: ['(?:__attribute__\s*\([ (]*.*?[ )]*\)+)']
+ :attributes:
+ - __ramfunc
+ - __irq
+ - __fiq
+ - register
+ - extern
+ :c_calling_conventions:
+ - __stdcall
+ - __cdecl
+ - __fastcall
+ :treat_externs: :exclude # the options being :include or :exclud
+ :treat_inlines: :exclude # the options being :include or :exclud
+
+ # Type handling configuration
+ #:unity_helper_path: '' # specify a string of where to find a unity_helper.h file to discover custom type assertions
+ :treat_as: # optionally add additional types to map custom types
uint8: HEX8
uint16: HEX16
uint32: UINT32
int8: INT8
bool: UINT8
+ #:treat_as_array: {} # hint to cmock that these types are pointers to something
+ #:treat_as_void: [] # hint to cmock that these types are actually aliases of void
+ :memcmp_if_unknown: true # allow cmock to use the memory comparison assertions for unknown types
+ :when_ptr: :compare_data # hint to cmock how to handle pointers in general, the options being :compare_ptr, :compare_data, or :smart
-# Add -gcov to the plugins list to make sure of the gcov plugin
-# You will need to have gcov and gcovr both installed to make it work.
-# For more information on these options, see docs in plugins/gcov
-:gcov:
- :reports:
- - HtmlDetailed
- :gcovr:
- :html_medium_threshold: 75
- :html_high_threshold: 90
+ # Mock generation configuration
+ :weak: '' # Symbol to use to declare weak functions
+ :enforce_strict_ordering: true # Do we want cmock to enforce ordering of all function calls?
+ :fail_on_unexpected_calls: true # Do we want cmock to fail when it encounters a function call that wasn't expected?
+ :callback_include_count: true # Do we want cmock to include the number of calls to this callback, when using callbacks?
+ :callback_after_arg_check: false # Do we want cmock to enforce an argument check first when using a callback?
+ #:includes: [] # You can add additional includes here, or specify the location with the options below
+ #:includes_h_pre_orig_header: []
+ #:includes_h_post_orig_header: []
+ #:includes_c_pre_header: []
+ #:includes_c_post_header: []
+ #:array_size_type: [] # Specify a type or types that should be used for array lengths
+ #:array_size_name: 'size|len' # Specify a name or names that CMock might automatically recognize as the length of an array
+ :exclude_setjmp_h: false # Don't use setjmp when running CMock. Note that this might result in late reporting or out-of-order failures.
-#:tools:
-# Ceedling defaults to using gcc for compiling, linking, etc.
-# As [:tools] is blank, gcc will be used (so long as it's in your system path)
-# See documentation to configure a given toolchain for use
+# Configuration options specific to Unity.
+:unity:
+ :defines:
+ - UNITY_EXCLUDE_FLOAT
+
+# You can optionally have ceedling create environment variables for you before
+# performing the rest of its tasks.
+:environment: []
# LIBRARIES
# These libraries are automatically injected into the build process. Those specified as
@@ -92,10 +218,183 @@
:test: []
:release: []
-:plugins:
- :load_paths:
- - "#{Ceedling.load_path}"
- :enabled:
- - stdout_pretty_tests_report
- - module_generator
+################################################################
+# PLUGIN CONFIGURATION
+################################################################
+
+# Add -gcov to the plugins list to make sure of the gcov plugin
+# You will need to have gcov and gcovr both installed to make it work.
+# For more information on these options, see docs in plugins/gcov
+:gcov:
+ :utilities:
+ - gcovr # Use gcovr to create the specified reports (default).
+ #- ReportGenerator # Use ReportGenerator to create the specified reports.
+ :reports: # Specify one or more reports to generate.
+ # Make an HTML summary report.
+ - HtmlBasic
+ # - HtmlDetailed
+ # - Text
+ # - Cobertura
+ # - SonarQube
+ # - JSON
+ # - HtmlInline
+ # - HtmlInlineAzure
+ # - HtmlInlineAzureDark
+ # - HtmlChart
+ # - MHtml
+ # - Badges
+ # - CsvSummary
+ # - Latex
+ # - LatexSummary
+ # - PngChart
+ # - TeamCitySummary
+ # - lcov
+ # - Xml
+ # - XmlSummary
+ :gcovr:
+ # :html_artifact_filename: TestCoverageReport.html
+ # :html_title: Test Coverage Report
+ :html_medium_threshold: 75
+ :html_high_threshold: 90
+ # :html_absolute_paths: TRUE
+ # :html_encoding: UTF-8
+
+# :module_generator:
+# :project_root: ./
+# :source_root: source/
+# :inc_root: includes/
+# :test_root: tests/
+# :naming: :snake #options: :bumpy, :camel, :caps, or :snake
+# :includes:
+# :tst: []
+# :src: []
+# :boilerplates:
+# :src: ""
+# :inc: ""
+# :tst: ""
+
+# :dependencies:
+# :libraries:
+# - :name: WolfSSL
+# :source_path: third_party/wolfssl/source
+# :build_path: third_party/wolfssl/build
+# :artifact_path: third_party/wolfssl/install
+# :fetch:
+# :method: :zip
+# :source: \\shared_drive\third_party_libs\wolfssl\wolfssl-4.2.0.zip
+# :environment:
+# - CFLAGS+=-DWOLFSSL_DTLS_ALLOW_FUTURE
+# :build:
+# - "autoreconf -i"
+# - "./configure --enable-tls13 --enable-singlethreaded"
+# - make
+# - make install
+# :artifacts:
+# :static_libraries:
+# - lib/wolfssl.a
+# :dynamic_libraries:
+# - lib/wolfssl.so
+# :includes:
+# - include/**
+
+# :subprojects:
+# :paths:
+# - :name: libprojectA
+# :source:
+# - ./subprojectA/source
+# :include:
+# - ./subprojectA/include
+# :build_root: ./subprojectA/build
+# :defines: []
+
+################################################################
+# TOOLCHAIN CONFIGURATION
+################################################################
+
+#:tools:
+# Ceedling defaults to using gcc for compiling, linking, etc.
+# As [:tools] is blank, gcc will be used (so long as it's in your system path)
+# See documentation to configure a given toolchain for use
+# :tools:
+# :test_compiler:
+# :executable:
+# :arguments: []
+# :name:
+# :optional: FALSE
+# :test_linker:
+# :executable:
+# :arguments: []
+# :name:
+# :optional: FALSE
+# :test_assembler:
+# :executable:
+# :arguments: []
+# :name:
+# :optional: FALSE
+# :test_fixture:
+# :executable:
+# :arguments: []
+# :name:
+# :optional: FALSE
+# :test_includes_preprocessor:
+# :executable:
+# :arguments: []
+# :name:
+# :optional: FALSE
+# :test_file_preprocessor:
+# :executable:
+# :arguments: []
+# :name:
+# :optional: FALSE
+# :test_file_preprocessor_directives:
+# :executable:
+# :arguments: []
+# :name:
+# :optional: FALSE
+# :test_dependencies_generator:
+# :executable:
+# :arguments: []
+# :name:
+# :optional: FALSE
+# :release_compiler:
+# :executable:
+# :arguments: []
+# :name:
+# :optional: FALSE
+# :release_linker:
+# :executable:
+# :arguments: []
+# :name:
+# :optional: FALSE
+# :release_assembler:
+# :executable:
+# :arguments: []
+# :name:
+# :optional: FALSE
+# :release_dependencies_generator:
+# :executable:
+# :arguments: []
+# :name:
+# :optional: FALSE
+# #These tools can be filled out when command_hooks plugin is enabled
+# :pre_mock_preprocess
+# :post_mock_preprocess
+# :pre_mock_generate
+# :post_mock_generate
+# :pre_runner_preprocess
+# :post_runner_preprocess
+# :pre_runner_generate
+# :post_runner_generate
+# :pre_compile_execute
+# :post_compile_execute
+# :pre_link_execute
+# :post_link_execute
+# :pre_test_fixture_execute
+# :pre_test
+# :post_test
+# :pre_release
+# :post_release
+# :pre_build
+# :post_build
+# :post_error
...
diff --git a/assets/project_with_guts.yml b/assets/project_with_guts.yml
index cb1086fb3..5f051ffc2 100644
--- a/assets/project_with_guts.yml
+++ b/assets/project_with_guts.yml
@@ -1,84 +1,210 @@
----
-
-# Notes:
-# Sample project C code is not presently written to produce a release artifact.
-# As such, release build options are disabled.
-# This sample, therefore, only demonstrates running a collection of unit tests.
+# =========================================================================
+# Ceedling - Test-Centered Build System for C
+# ThrowTheSwitch.org
+# Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+# SPDX-License-Identifier: MIT
+# =========================================================================
+---
:project:
- :use_exceptions: FALSE
+ # how to use ceedling. If you're not sure, leave this as `gem` and `?`
+ :which_ceedling: vendor/ceedling
+ :ceedling_version: '?'
+
+ # optional features. If you don't need them, keep them turned off for performance
+ :use_mocks: TRUE
:use_test_preprocessor: TRUE
- :use_auxiliary_dependencies: TRUE
+ :use_backtrace: :none
+ :use_decorators: :auto #Decorate Ceedling's output text. Your options are :auto, :all, or :none
+
+ # tweak the way ceedling handles automatic tasks
:build_root: build
-# :release_build: TRUE
:test_file_prefix: test_
- :which_ceedling: vendor/ceedling
- :ceedling_version: '?'
:default_tasks:
- test:all
-#:test_build:
-# :use_assembly: TRUE
+ # performance options. If your tools start giving mysterious errors, consider
+ # dropping this to 1 to force single-tasking
+ :test_threads: 8
+ :compile_threads: 8
+
+ # enable release build (more details in release_build section below)
+ :release_build: FALSE
+
+# Specify where to find mixins and any that should be enabled automatically
+:mixins:
+ :enabled: []
+ :load_paths: []
+
+# further details to configure the way Ceedling handles test code
+:test_build:
+ :use_assembly: FALSE
-#:release_build:
-# :output: MyApp.out
-# :use_assembly: FALSE
+# further details to configure the way Ceedling handles release code
+:release_build:
+ :output: MyApp.out
+ :use_assembly: FALSE
+ :artifacts: []
+
+# Plugins are optional Ceedling features which can be enabled. Ceedling supports
+# a variety of plugins which may effect the way things are compiled, reported,
+# or may provide new command options. Refer to the readme in each plugin for
+# details on how to use it.
+:plugins:
+ :load_paths: []
+ :enabled:
+ #- beep # beeps when finished, so you don't waste time waiting for ceedling
+ - module_generator # handy for quickly creating source, header, and test templates
+ #- gcov # test coverage using gcov. Requires gcc, gcov, and a coverage analyzer like gcovr
+ #- bullseye # test coverage using bullseye. Requires bullseye for your platform
+ #- command_hooks # write custom actions to be called at different points during the build process
+ #- compile_commands_json_db # generate a compile_commands.json file
+ #- dependencies # automatically fetch 3rd party libraries, etc.
+ #- subprojects # managing builds and test for static libraries
+ #- fake_function_framework # use FFF instead of CMock
-:environment:
+ # Report options (You'll want to choose one stdout option, but may choose multiple stored options if desired)
+ #- report_build_warnings_log
+ #- report_tests_gtestlike_stdout
+ #- report_tests_ide_stdout
+ #- report_tests_log_factory
+ - report_tests_pretty_stdout
+ #- report_tests_raw_output_log
+ #- report_tests_teamcity_stdout
+# Specify which reports you'd like from the log factory
+:report_tests_log_factory:
+ :reports:
+ - json
+ - junit
+ - cppunit
+ - html
+
+# override the default extensions for your system and toolchain
:extension:
+ #:header: .h
+ #:source: .c
+ #:assembly: .s
+ #:dependencies: .d
+ #:object: .o
:executable: .out
+ #:testpass: .pass
+ #:testfail: .fail
+ #:subprojects: .a
+# This is where Ceedling should look for your source and test files.
+# see documentation for the many options for specifying this.
:paths:
:test:
- +:test/**
- -:test/support
:source:
- src/**
+ :include:
+ - src/** # In simple projects, this entry often duplicates :source
:support:
- test/support
:libraries: []
+# You can even specify specific files to add or remove from your test
+# and release collections. Usually it's better to use paths and let
+# Ceedling do the work for you!
+:files:
+ :test: []
+ :source: []
+
+# Compilation symbols to be injected into builds
+# See documentation for advanced options:
+# - Test name matchers for different symbols per test executable build
+# - Referencing symbols in multiple lists using advanced YAML
+# - Specifiying symbols used during test preprocessing
:defines:
- # in order to add common defines:
- # 1) remove the trailing [] from the :common: section
- # 2) add entries to the :common: section (e.g. :test: has TEST defined)
- :common: &common_defines []
:test:
- - *common_defines
- - TEST
- :test_preprocess:
- - *common_defines
- - TEST
+ - TEST # Simple list option to add symbol 'TEST' to compilation of all files in all test executables
+ :release: []
+
+ # Enable to inject name of a test as a unique compilation symbol into its respective executable build.
+ :use_test_definition: FALSE
+
+# Configure additional command line flags provided to tools used in each build step
+# :flags:
+# :release:
+# :compile: # Add '-Wall' and '--02' to compilation of all files in release target
+# - -Wall
+# - --O2
+# :test:
+# :compile:
+# '(_|-)special': # Add '-pedantic' to compilation of all files in all test executables with '_special' or '-special' in their names
+# - -pedantic
+# '*': # Add '-foo' to compilation of all files in all test executables
+# - -foo
+# Configuration Options specific to CMock. See CMock docs for details
:cmock:
- :mock_prefix: mock_
- :when_no_prototypes: :warn
- :enforce_strict_ordering: TRUE
- :plugins:
+ # Core conffiguration
+ :plugins: # What plugins should be used by CMock?
- :ignore
- :callback
- :treat_as:
+ :verbosity: 2 # the options being 0 errors only, 1 warnings and errors, 2 normal info, 3 verbose
+ :when_no_prototypes: :warn # the options being :ignore, :warn, or :erro
+
+ # File configuration
+ :mock_path: './build/mocks' # Subdirectory to store mocks when generated (default: mocks)
+ :skeleton_path: '' # Subdirectory to store stubs when generated (default: '')
+ :mock_prefix: 'mock_' # Prefix to append to filenames for mocks
+ :mock_suffix: '' # Suffix to append to filenames for mocks
+
+ # Parser configuration
+ :strippables: ['(?:__attribute__\s*\([ (]*.*?[ )]*\)+)']
+ :attributes:
+ - __ramfunc
+ - __irq
+ - __fiq
+ - register
+ - extern
+ :c_calling_conventions:
+ - __stdcall
+ - __cdecl
+ - __fastcall
+ :treat_externs: :exclude # the options being :include or :exclud
+ :treat_inlines: :exclude # the options being :include or :exclud
+
+ # Type handling configuration
+ #:unity_helper_path: '' # specify a string of where to find a unity_helper.h file to discover custom type assertions
+ :treat_as: # optionally add additional types to map custom types
uint8: HEX8
uint16: HEX16
uint32: UINT32
int8: INT8
bool: UINT8
+ #:treat_as_array: {} # hint to cmock that these types are pointers to something
+ #:treat_as_void: [] # hint to cmock that these types are actually aliases of void
+ :memcmp_if_unknown: true # allow cmock to use the memory comparison assertions for unknown types
+ :when_ptr: :compare_data # hint to cmock how to handle pointers in general, the options being :compare_ptr, :compare_data, or :smart
-# Add -gcov to the plugins list to make sure of the gcov plugin
-# You will need to have gcov and gcovr both installed to make it work.
-# For more information on these options, see docs in plugins/gcov
-:gcov:
- :reports:
- - HtmlDetailed
- :gcovr:
- :html_medium_threshold: 75
- :html_high_threshold: 90
+ # Mock generation configuration
+ :weak: '' # Symbol to use to declare weak functions
+ :enforce_strict_ordering: true # Do we want cmock to enforce ordering of all function calls?
+ :fail_on_unexpected_calls: true # Do we want cmock to fail when it encounters a function call that wasn't expected?
+ :callback_include_count: true # Do we want cmock to include the number of calls to this callback, when using callbacks?
+ :callback_after_arg_check: false # Do we want cmock to enforce an argument check first when using a callback?
+ #:includes: [] # You can add additional includes here, or specify the location with the options below
+ #:includes_h_pre_orig_header: []
+ #:includes_h_post_orig_header: []
+ #:includes_c_pre_header: []
+ #:includes_c_post_header: []
+ #:array_size_type: [] # Specify a type or types that should be used for array lengths
+ #:array_size_name: 'size|len' # Specify a name or names that CMock might automatically recognize as the length of an array
+ :exclude_setjmp_h: false # Don't use setjmp when running CMock. Note that this might result in late reporting or out-of-order failures.
-#:tools:
-# Ceedling defaults to using gcc for compiling, linking, etc.
-# As [:tools] is blank, gcc will be used (so long as it's in your system path)
-# See documentation to configure a given toolchain for use
+# Configuration options specific to Unity.
+:unity:
+ :defines:
+ - UNITY_EXCLUDE_FLOAT
+
+# You can optionally have ceedling create environment variables for you before
+# performing the rest of its tasks.
+:environment: []
# LIBRARIES
# These libraries are automatically injected into the build process. Those specified as
@@ -92,11 +218,184 @@
:test: []
:release: []
-:plugins:
- :load_paths:
- - vendor/ceedling/plugins
- :enabled:
- - stdout_pretty_tests_report
- - module_generator
- - raw_output_report
+################################################################
+# PLUGIN CONFIGURATION
+################################################################
+
+# Add -gcov to the plugins list to make sure of the gcov plugin
+# You will need to have gcov and gcovr both installed to make it work.
+# For more information on these options, see docs in plugins/gcov
+:gcov:
+ :utilities:
+ - gcovr # Use gcovr to create the specified reports (default).
+ #- ReportGenerator # Use ReportGenerator to create the specified reports.
+ :reports: # Specify one or more reports to generate.
+ # Make an HTML summary report.
+ - HtmlBasic
+ # - HtmlDetailed
+ # - Text
+ # - Cobertura
+ # - SonarQube
+ # - JSON
+ # - HtmlInline
+ # - HtmlInlineAzure
+ # - HtmlInlineAzureDark
+ # - HtmlChart
+ # - MHtml
+ # - Badges
+ # - CsvSummary
+ # - Latex
+ # - LatexSummary
+ # - PngChart
+ # - TeamCitySummary
+ # - lcov
+ # - Xml
+ # - XmlSummary
+ :gcovr:
+ # :html_artifact_filename: TestCoverageReport.html
+ # :html_title: Test Coverage Report
+ :html_medium_threshold: 75
+ :html_high_threshold: 90
+ # :html_absolute_paths: TRUE
+ # :html_encoding: UTF-8
+
+# :module_generator:
+# :project_root: ./
+# :source_root: source/
+# :inc_root: includes/
+# :test_root: tests/
+# :naming: :snake #options: :bumpy, :camel, :caps, or :snake
+# :includes:
+# :tst: []
+# :src: []
+# :boilerplates:
+# :src: ""
+# :inc: ""
+# :tst: ""
+
+# :dependencies:
+# :libraries:
+# - :name: WolfSSL
+# :source_path: third_party/wolfssl/source
+# :build_path: third_party/wolfssl/build
+# :artifact_path: third_party/wolfssl/install
+# :fetch:
+# :method: :zip
+# :source: \\shared_drive\third_party_libs\wolfssl\wolfssl-4.2.0.zip
+# :environment:
+# - CFLAGS+=-DWOLFSSL_DTLS_ALLOW_FUTURE
+# :build:
+# - "autoreconf -i"
+# - "./configure --enable-tls13 --enable-singlethreaded"
+# - make
+# - make install
+# :artifacts:
+# :static_libraries:
+# - lib/wolfssl.a
+# :dynamic_libraries:
+# - lib/wolfssl.so
+# :includes:
+# - include/**
+
+# :subprojects:
+# :paths:
+# - :name: libprojectA
+# :source:
+# - ./subprojectA/source
+# :include:
+# - ./subprojectA/include
+# :build_root: ./subprojectA/build
+# :defines: []
+
+################################################################
+# TOOLCHAIN CONFIGURATION
+################################################################
+
+
+#:tools:
+# Ceedling defaults to using gcc for compiling, linking, etc.
+# As [:tools] is blank, gcc will be used (so long as it's in your system path)
+# See documentation to configure a given toolchain for use
+# :tools:
+# :test_compiler:
+# :executable:
+# :arguments: []
+# :name:
+# :optional: FALSE
+# :test_linker:
+# :executable:
+# :arguments: []
+# :name:
+# :optional: FALSE
+# :test_assembler:
+# :executable:
+# :arguments: []
+# :name:
+# :optional: FALSE
+# :test_fixture:
+# :executable:
+# :arguments: []
+# :name:
+# :optional: FALSE
+# :test_includes_preprocessor:
+# :executable:
+# :arguments: []
+# :name:
+# :optional: FALSE
+# :test_file_preprocessor:
+# :executable:
+# :arguments: []
+# :name:
+# :optional: FALSE
+# :test_file_preprocessor_directives:
+# :executable:
+# :arguments: []
+# :name:
+# :optional: FALSE
+# :test_dependencies_generator:
+# :executable:
+# :arguments: []
+# :name:
+# :optional: FALSE
+# :release_compiler:
+# :executable:
+# :arguments: []
+# :name:
+# :optional: FALSE
+# :release_linker:
+# :executable:
+# :arguments: []
+# :name:
+# :optional: FALSE
+# :release_assembler:
+# :executable:
+# :arguments: []
+# :name:
+# :optional: FALSE
+# :release_dependencies_generator:
+# :executable:
+# :arguments: []
+# :name:
+# :optional: FALSE
+# #These tools can be filled out when command_hooks plugin is enabled
+# :pre_mock_preprocess
+# :post_mock_preprocess
+# :pre_mock_generate
+# :post_mock_generate
+# :pre_runner_preprocess
+# :post_runner_preprocess
+# :pre_runner_generate
+# :post_runner_generate
+# :pre_compile_execute
+# :post_compile_execute
+# :pre_link_execute
+# :post_link_execute
+# :pre_test_fixture_execute
+# :pre_test
+# :post_test
+# :pre_release
+# :post_release
+# :pre_build
+# :post_build
+# :post_error
...
diff --git a/assets/project_with_guts_gcov.yml b/assets/project_with_guts_gcov.yml
deleted file mode 100644
index 29418a1cd..000000000
--- a/assets/project_with_guts_gcov.yml
+++ /dev/null
@@ -1,102 +0,0 @@
----
-
-# Notes:
-# Sample project C code is not presently written to produce a release artifact.
-# As such, release build options are disabled.
-# This sample, therefore, only demonstrates running a collection of unit tests.
-
-:project:
- :use_exceptions: FALSE
- :use_test_preprocessor: TRUE
- :use_auxiliary_dependencies: TRUE
- :build_root: build
-# :release_build: TRUE
- :test_file_prefix: test_
- :which_ceedling: vendor/ceedling
- :ceedling_version: '?'
- :default_tasks:
- - test:all
-
-#:test_build:
-# :use_assembly: TRUE
-
-#:release_build:
-# :output: MyApp.out
-# :use_assembly: FALSE
-
-:environment:
-
-:extension:
- :executable: .out
-
-:paths:
- :test:
- - +:test/**
- - -:test/support
- :source:
- - src/**
- :support:
- - test/support
- :libraries: []
-
-:defines:
- # in order to add common defines:
- # 1) remove the trailing [] from the :common: section
- # 2) add entries to the :common: section (e.g. :test: has TEST defined)
- :common: &common_defines []
- :test:
- - *common_defines
- - TEST
- :test_preprocess:
- - *common_defines
- - TEST
-
-:cmock:
- :mock_prefix: mock_
- :when_no_prototypes: :warn
- :enforce_strict_ordering: TRUE
- :plugins:
- - :ignore
- - :callback
- :treat_as:
- uint8: HEX8
- uint16: HEX16
- uint32: UINT32
- int8: INT8
- bool: UINT8
-
-# Add -gcov to the plugins list to make sure of the gcov plugin
-# You will need to have gcov and gcovr both installed to make it work.
-# For more information on these options, see docs in plugins/gcov
-:gcov:
- :reports:
- - HtmlDetailed
- :gcovr:
- :html_medium_threshold: 75
- :html_high_threshold: 90
-
-#:tools:
-# Ceedling defaults to using gcc for compiling, linking, etc.
-# As [:tools] is blank, gcc will be used (so long as it's in your system path)
-# See documentation to configure a given toolchain for use
-
-# LIBRARIES
-# These libraries are automatically injected into the build process. Those specified as
-# common will be used in all types of builds. Otherwise, libraries can be injected in just
-# tests or releases. These options are MERGED with the options in supplemental yaml files.
-:libraries:
- :placement: :end
- :flag: "-l${1}"
- :path_flag: "-L ${1}"
- :system: [] # for example, you might list 'm' to grab the math library
- :test: []
- :release: []
-
-:plugins:
- :load_paths:
- - vendor/ceedling/plugins
- :enabled:
- - stdout_pretty_tests_report
- - module_generator
- - gcov
-...
diff --git a/assets/test_example_file.c b/assets/test_example_file.c
index e8378aa54..7a43a7a96 100644
--- a/assets/test_example_file.c
+++ b/assets/test_example_file.c
@@ -1,3 +1,10 @@
+/* =========================================================================
+ Ceedling - Test-Centered Build System for C
+ ThrowTheSwitch.org
+ Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+ SPDX-License-Identifier: MIT
+========================================================================= */
+
#include "unity.h"
#include "example_file.h"
@@ -5,9 +12,9 @@ void setUp(void) {}
void tearDown(void) {}
void test_add_numbers_adds_numbers(void) {
- TEST_ASSERT_EQUAL(2, add_numbers(1,1));
+ TEST_ASSERT_EQUAL_INT(2, add_numbers(1,1));
}
void test_add_numbers_will_fail(void) {
- TEST_ASSERT_EQUAL(2, add_numbers(2,2));
+ TEST_ASSERT_EQUAL_INT(2, add_numbers(2,2));
}
diff --git a/assets/test_example_file_boom.c b/assets/test_example_file_boom.c
index 1365296b9..fd2b64bc4 100644
--- a/assets/test_example_file_boom.c
+++ b/assets/test_example_file_boom.c
@@ -1,3 +1,10 @@
+/* =========================================================================
+ Ceedling - Test-Centered Build System for C
+ ThrowTheSwitch.org
+ Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+ SPDX-License-Identifier: MIT
+========================================================================= */
+
#include "unity.h"
#include "example_file.h"
@@ -5,9 +12,9 @@ void setUp(void) {}
void tearDown(void) {}
void test_add_numbers_adds_numbers(void) {
- TEST_ASSERT_EQUAL(2, add_numbers(1,1) //Removed semicolon & parenthesis to make a compile error.
+ TEST_ASSERT_EQUAL_INT(2, add_numbers(1,1) //Removed semicolon & parenthesis to make a compile error.
}
void test_add_numbers_will_fail(void) {
- TEST_ASSERT_EQUAL(2, add_numbers(2,2));
+ TEST_ASSERT_EQUAL_INT(2, add_numbers(2,2));
}
diff --git a/assets/test_example_file_crash.c b/assets/test_example_file_crash.c
new file mode 100644
index 000000000..dcc86066a
--- /dev/null
+++ b/assets/test_example_file_crash.c
@@ -0,0 +1,25 @@
+/* =========================================================================
+ Ceedling - Test-Centered Build System for C
+ ThrowTheSwitch.org
+ Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+ SPDX-License-Identifier: MIT
+========================================================================= */
+
+#include
+#include "unity.h"
+#include "example_file.h"
+
+
+void setUp(void) {}
+void tearDown(void) {}
+
+void test_add_numbers_adds_numbers(void) {
+ TEST_ASSERT_EQUAL_INT(2, add_numbers(1,1));
+}
+
+void test_add_numbers_will_fail(void) {
+ // Platform-independent way of forcing a crash
+ uint32_t* nullptr = (void*) 0;
+ uint32_t i = *nullptr;
+ TEST_ASSERT_EQUAL_INT(2, add_numbers(i,2));
+}
diff --git a/assets/test_example_file_sigsegv.c b/assets/test_example_file_sigsegv.c
deleted file mode 100644
index 8ac1aef8d..000000000
--- a/assets/test_example_file_sigsegv.c
+++ /dev/null
@@ -1,16 +0,0 @@
-#include
-#include "unity.h"
-#include "example_file.h"
-
-
-void setUp(void) {}
-void tearDown(void) {}
-
-void test_add_numbers_adds_numbers(void) {
- TEST_ASSERT_EQUAL(2, add_numbers(1,1));
-}
-
-void test_add_numbers_will_fail(void) {
- raise(SIGSEGV);
- TEST_ASSERT_EQUAL(2, add_numbers(2,2));
-}
diff --git a/assets/test_example_file_success.c b/assets/test_example_file_success.c
index 4bc326492..6800dfd8d 100644
--- a/assets/test_example_file_success.c
+++ b/assets/test_example_file_success.c
@@ -1,3 +1,10 @@
+/* =========================================================================
+ Ceedling - Test-Centered Build System for C
+ ThrowTheSwitch.org
+ Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+ SPDX-License-Identifier: MIT
+========================================================================= */
+
#include "unity.h"
#include "example_file.h"
@@ -5,10 +12,10 @@ void setUp(void) {}
void tearDown(void) {}
void test_add_numbers_adds_numbers(void) {
- TEST_ASSERT_EQUAL(2, add_numbers(1,1));
+ TEST_ASSERT_EQUAL_INT(2, add_numbers(1,1));
}
void test_add_numbers_will_fail_but_is_ignored_for_now(void) {
TEST_IGNORE();
- TEST_ASSERT_EQUAL(2, add_numbers(2,2));
+ TEST_ASSERT_EQUAL_INT(2, add_numbers(2,2));
}
diff --git a/assets/test_example_file_unity_printf.c b/assets/test_example_file_unity_printf.c
index 0aee87345..b87da5434 100644
--- a/assets/test_example_file_unity_printf.c
+++ b/assets/test_example_file_unity_printf.c
@@ -1,3 +1,10 @@
+/* =========================================================================
+ Ceedling - Test-Centered Build System for C
+ ThrowTheSwitch.org
+ Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+ SPDX-License-Identifier: MIT
+========================================================================= */
+
#include "unity.h"
#include "example_file.h"
#include
@@ -7,6 +14,6 @@ void tearDown(void) {}
void test_add_numbers_adds_numbers(void) {
TEST_PRINTF("1 + 1 =%d", 1 + 1);
- TEST_ASSERT_EQUAL(2, add_numbers(1,1));
+ TEST_ASSERT_EQUAL_INT(2, add_numbers(1,1));
}
diff --git a/assets/test_example_file_verbose.c b/assets/test_example_file_verbose.c
index 9e50ea622..63d2f3819 100644
--- a/assets/test_example_file_verbose.c
+++ b/assets/test_example_file_verbose.c
@@ -1,3 +1,10 @@
+/* =========================================================================
+ Ceedling - Test-Centered Build System for C
+ ThrowTheSwitch.org
+ Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+ SPDX-License-Identifier: MIT
+========================================================================= */
+
#include "unity.h"
#include "example_file.h"
#include
@@ -7,6 +14,6 @@ void tearDown(void) {}
void test_add_numbers_adds_numbers(void) {
printf("1 + 1 = 2\n");
- TEST_ASSERT_EQUAL(2, add_numbers(1,1));
+ TEST_ASSERT_EQUAL_INT(2, add_numbers(1,1));
}
diff --git a/assets/test_example_file_with_mock.c b/assets/test_example_file_with_mock.c
index f9e270be1..c3748ccf0 100644
--- a/assets/test_example_file_with_mock.c
+++ b/assets/test_example_file_with_mock.c
@@ -1,3 +1,10 @@
+/* =========================================================================
+ Ceedling - Test-Centered Build System for C
+ ThrowTheSwitch.org
+ Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+ SPDX-License-Identifier: MIT
+========================================================================= */
+
#include "unity.h"
#include "example_file_call.h"
// mock header should have higher priority than real file
@@ -9,5 +16,5 @@ void tearDown(void) {}
void test_add_numbers_adds_numbers(void) {
add_numbers_ExpectAndReturn(1, 1, 2);
- TEST_ASSERT_EQUAL(2, call_add_numbers(1, 1));
+ TEST_ASSERT_EQUAL_INT(2, call_add_numbers(1, 1));
}
diff --git a/assets/test_example_with_parameterized_tests.c b/assets/test_example_with_parameterized_tests.c
index 3b511db62..4e0a93ecc 100644
--- a/assets/test_example_with_parameterized_tests.c
+++ b/assets/test_example_with_parameterized_tests.c
@@ -1,3 +1,10 @@
+/* =========================================================================
+ Ceedling - Test-Centered Build System for C
+ ThrowTheSwitch.org
+ Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+ SPDX-License-Identifier: MIT
+========================================================================= */
+
#include "unity.h"
#define TEST_CASE(...)
diff --git a/assets/tests_with_defines/src/adc_hardware.c b/assets/tests_with_defines/src/adc_hardware.c
index 244dc6606..e316b9fb9 100644
--- a/assets/tests_with_defines/src/adc_hardware.c
+++ b/assets/tests_with_defines/src/adc_hardware.c
@@ -1,3 +1,10 @@
+/* =========================================================================
+ Ceedling - Test-Centered Build System for C
+ ThrowTheSwitch.org
+ Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+ SPDX-License-Identifier: MIT
+========================================================================= */
+
#include "adc_hardware.h"
#include "adc_hardware_configurator.h"
@@ -5,7 +12,7 @@ void AdcHardware_Init(void)
{
#ifdef SPECIFIC_CONFIG
Adc_ResetSpec();
- #elif STANDARD_CONFIG
+ #elif defined(STANDARD_CONFIG)
Adc_Reset();
#endif
}
diff --git a/assets/tests_with_defines/src/adc_hardware.h b/assets/tests_with_defines/src/adc_hardware.h
index aee79daf7..7483ac99a 100644
--- a/assets/tests_with_defines/src/adc_hardware.h
+++ b/assets/tests_with_defines/src/adc_hardware.h
@@ -1,3 +1,10 @@
+/* =========================================================================
+ Ceedling - Test-Centered Build System for C
+ ThrowTheSwitch.org
+ Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+ SPDX-License-Identifier: MIT
+========================================================================= */
+
#ifndef _ADCHARDWARE_H
#define _ADCHARDWARE_H
diff --git a/assets/tests_with_defines/src/adc_hardware_configurator.c b/assets/tests_with_defines/src/adc_hardware_configurator.c
index 0ea263c58..bd90c6397 100644
--- a/assets/tests_with_defines/src/adc_hardware_configurator.c
+++ b/assets/tests_with_defines/src/adc_hardware_configurator.c
@@ -1,10 +1,17 @@
+/* =========================================================================
+ Ceedling - Test-Centered Build System for C
+ ThrowTheSwitch.org
+ Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+ SPDX-License-Identifier: MIT
+========================================================================= */
+
#include "adc_hardware_configurator.h"
#ifdef SPECIFIC_CONFIG
void Adc_ResetSpec(void)
{
}
-#elif STANDARD_CONFIG
+#elif defined(STANDARD_CONFIG)
void Adc_Reset(void)
{
}
diff --git a/assets/tests_with_defines/src/adc_hardware_configurator.h b/assets/tests_with_defines/src/adc_hardware_configurator.h
index 699eced8e..8a8fe8153 100644
--- a/assets/tests_with_defines/src/adc_hardware_configurator.h
+++ b/assets/tests_with_defines/src/adc_hardware_configurator.h
@@ -1,9 +1,16 @@
+/* =========================================================================
+ Ceedling - Test-Centered Build System for C
+ ThrowTheSwitch.org
+ Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+ SPDX-License-Identifier: MIT
+========================================================================= */
+
#ifndef _ADCHARDWARECONFIGURATOR_H
#define _ADCHARDWARECONFIGURATOR_H
#ifdef SPECIFIC_CONFIG
void Adc_ResetSpec(void);
-#elif STANDARD_CONFIG
+#elif defined(STANDARD_CONFIG)
void Adc_Reset(void);
#endif
diff --git a/assets/tests_with_defines/test/test_adc_hardware.c b/assets/tests_with_defines/test/test_adc_hardware.c
index fa424dac4..260098f1d 100644
--- a/assets/tests_with_defines/test/test_adc_hardware.c
+++ b/assets/tests_with_defines/test/test_adc_hardware.c
@@ -1,3 +1,10 @@
+/* =========================================================================
+ Ceedling - Test-Centered Build System for C
+ ThrowTheSwitch.org
+ Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+ SPDX-License-Identifier: MIT
+========================================================================= */
+
#include "unity.h"
#include "adc_hardware.h"
#include "mock_adc_hardware_configurator.h"
diff --git a/assets/tests_with_defines/test/test_adc_hardware_special.c b/assets/tests_with_defines/test/test_adc_hardware_special.c
index ef215d49d..fcc3e1f0c 100644
--- a/assets/tests_with_defines/test/test_adc_hardware_special.c
+++ b/assets/tests_with_defines/test/test_adc_hardware_special.c
@@ -1,3 +1,10 @@
+/* =========================================================================
+ Ceedling - Test-Centered Build System for C
+ ThrowTheSwitch.org
+ Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+ SPDX-License-Identifier: MIT
+========================================================================= */
+
#include "unity.h"
#include "adc_hardware.h"
#include "mock_adc_hardware_configurator.h"
diff --git a/assets/uncovered_example_file.c b/assets/uncovered_example_file.c
index 830847a50..dc389359e 100644
--- a/assets/uncovered_example_file.c
+++ b/assets/uncovered_example_file.c
@@ -1,3 +1,10 @@
+/* =========================================================================
+ Ceedling - Test-Centered Build System for C
+ ThrowTheSwitch.org
+ Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+ SPDX-License-Identifier: MIT
+========================================================================= */
+
// This file is to test abort on uncovered files feature
int multiply_numbers(int a, int b) {
diff --git a/bin/actions_wrapper.rb b/bin/actions_wrapper.rb
new file mode 100644
index 000000000..f5a06c464
--- /dev/null
+++ b/bin/actions_wrapper.rb
@@ -0,0 +1,42 @@
+# =========================================================================
+# Ceedling - Test-Centered Build System for C
+# ThrowTheSwitch.org
+# Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+# SPDX-License-Identifier: MIT
+# =========================================================================
+
+require 'thor'
+require 'fileutils'
+
+# Wrapper for handy Thor Actions
+class ActionsWrapper
+ include Thor::Base
+ include Thor::Actions
+
+ # Most important mixin method is Thor::Actions class method `source_root()` we call externally
+
+ def _directory(src, *args)
+ directory( src, *args )
+ end
+
+ def _copy_file(src, *args)
+ copy_file( src, *args )
+ end
+
+ def _touch_file(src)
+ FileUtils.touch(src)
+ end
+
+ def _chmod(src, mode, *args)
+ chmod( src, mode, *args )
+ end
+
+ def _empty_directory(dest, *args)
+ empty_directory( dest, *args )
+ end
+
+ def _gsub_file(path, flag, *args, &block)
+ gsub_file( path, flag, *args, &block )
+ end
+
+end
diff --git a/bin/app_cfg.rb b/bin/app_cfg.rb
new file mode 100644
index 000000000..e09c33d7e
--- /dev/null
+++ b/bin/app_cfg.rb
@@ -0,0 +1,119 @@
+# =========================================================================
+# Ceedling - Test-Centered Build System for C
+# ThrowTheSwitch.org
+# Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+# SPDX-License-Identifier: MIT
+# =========================================================================
+
+require "io/console"
+
+# Create our global application configuration option set
+# This approach bridges clean Ruby and Rake
+
+class CeedlingAppConfig
+
+ def initialize()
+ # Default installation location determined from the location of this file
+ ceedling_root_path = File.join( File.dirname( __FILE__ ), '..' )
+
+ # Create internal hash of needed values
+ @app_cfg = {
+ # Base path of any Ceedling installation
+ :ceedling_root_path => '',
+
+ # Ceedling installation base path + /lib
+ :ceedling_lib_base_path => '',
+
+ # Ceedling installation base path + /lib/ceedling
+ :ceedling_lib_path => '',
+
+ # Ceedling installation base path + /plugins
+ :ceedling_plugins_path => '',
+
+ # Ceedling installation base path + /vendor
+ :ceedling_vendor_path => '',
+
+ # Ceedling installation base path + /examples
+ :ceedling_examples_path => '',
+
+ # Ceedling lib path + lib/ceedling/rakefile.rb
+ :ceedling_rakefile_filepath => '',
+
+ # Blank initial value for completeness
+ :project_config => {},
+
+ # Default, blank value
+ :log_filepath => '',
+
+ # Only specified in project config (no command line or environment variable)
+ :default_tasks => ['test:all'],
+
+ # Default, blank test case filters
+ :include_test_case => '',
+ :exclude_test_case => '',
+
+ # Default to no duration logging for setup & build ops in Rake context
+ :stopwatch => false,
+
+ # Default to `exit(1)` upon failing test cases
+ :tests_graceful_fail => false,
+
+ # Set terminal width (in columns) to a default
+ :terminal_width => 120,
+ }
+
+ set_paths( ceedling_root_path )
+
+ # Try to query terminal width (not always available on all platforms)
+ begin
+ @app_cfg[:terminal_width] = (IO.console.winsize)[1]
+ rescue
+ # Do nothing; allow value already set to stand as default
+ end
+ end
+
+ def set_project_config(config)
+ @app_cfg[:project_config] = config
+ end
+
+ def set_log_filepath(filepath)
+ @app_cfg[:log_filepath] = filepath
+ end
+
+ def set_include_test_case(matcher)
+ @app_cfg[:include_test_case] = matcher
+ end
+
+ def set_exclude_test_case(matcher)
+ @app_cfg[:exclude_test_case] = matcher
+ end
+
+ def set_stopwatch(enable)
+ @app_cfg[:stopwatch] = enable
+ end
+
+ def set_tests_graceful_fail(enable)
+ @app_cfg[:tests_graceful_fail] = enable
+ end
+
+ def set_paths(root_path)
+ _root_path = File.expand_path( root_path )
+ lib_base_path = File.join( _root_path, 'lib' )
+ lib_path = File.join( lib_base_path, 'ceedling' )
+
+ @app_cfg[:ceedling_root_path] = _root_path
+ @app_cfg[:ceedling_lib_base_path] = lib_base_path
+ @app_cfg[:ceedling_lib_path] = lib_path
+ @app_cfg[:ceedling_vendor_path] = File.join( _root_path, 'vendor' )
+ @app_cfg[:ceedling_plugins_path] = File.join( _root_path, 'plugins' )
+ @app_cfg[:ceedling_examples_path] = File.join( _root_path, 'examples' )
+
+ @app_cfg[:ceedling_rakefile_filepath] = File.join( lib_path, 'rakefile.rb' )
+ end
+
+ # External accessor to preserve hash-like read accesses
+ def [](key)
+ return @app_cfg[key]
+ end
+
+end
diff --git a/bin/ceedling b/bin/ceedling
index 5a1d49584..0b928258b 100755
--- a/bin/ceedling
+++ b/bin/ceedling
@@ -1,364 +1,161 @@
#!/usr/bin/env ruby
+# =========================================================================
+# Ceedling - Test-Centered Build System for C
+# ThrowTheSwitch.org
+# Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+# SPDX-License-Identifier: MIT
+# =========================================================================
-#these are always used
require 'rubygems'
-require 'fileutils'
-# Check for the main project file (either the one defined in the ENV or the default)
-main_filepath = ENV['CEEDLING_MAIN_PROJECT_FILE']
-project_found = (!main_filepath.nil? && File.exist?(main_filepath))
-if (!project_found)
- main_filepath = "project.yml"
- project_found = File.exist?(main_filepath)
-end
-
-def is_windows?
- return ((RbConfig::CONFIG['host_os'] =~ /mswin|mingw/) ? true : false) if defined?(RbConfig)
- return ((Config::CONFIG['host_os'] =~ /mswin|mingw/) ? true : false)
-end
-
-def here
- File.join(File.expand_path(File.dirname(__FILE__)),"/..")
-end
-
-unless (project_found)
-#===================================== We Do Not Have A Project ================================================
-
- puts "Welcome to Ceedling!"
- require 'thor'
-
- class CeedlingTasks < Thor
- include Thor::Actions
-
- desc "new PROJECT_NAME", "create a new ceedling project"
- method_option :docs, :type => :boolean, :default => false, :desc => "Add docs in project vendor directory"
- method_option :local, :type => :boolean, :default => false, :desc => "Create a copy of Ceedling in the project vendor directory"
- method_option :gitignore, :type => :boolean, :default => false, :desc => "Create a gitignore file for ignoring ceedling generated files"
- method_option :no_configs, :type => :boolean, :default => false, :desc => "Don't install starter configuration files"
- method_option :noconfigs, :type => :boolean, :default => false
-
- #deprecated:
- method_option :no_docs, :type => :boolean, :default => false
- method_option :nodocs, :type => :boolean, :default => false
- method_option :as_gem, :type => :boolean, :default => false
- method_option :asgem, :type => :boolean, :default => false
- method_option :with_ignore, :type => :boolean, :default => false
- method_option :withignore, :type => :boolean, :default => false
- def new(name, silent = false)
- copy_assets_and_create_structure(name, silent, false, options)
- end
-
- desc "upgrade PROJECT_NAME", "upgrade ceedling for a project (not req'd if gem used)"
- def upgrade(name, silent = false)
- as_local = true
- yaml_path = File.join(name, "project.yml")
- begin
- require File.join(here,"lib","ceedling","yaml_wrapper.rb")
- as_local = (YamlWrapper.new.load(yaml_path)[:project][:which_ceedling] != 'gem')
- rescue
- raise "ERROR: Could not find valid project file '#{yaml_path}'"
- end
- found_docs = File.exist?( File.join(name, "docs", "CeedlingPacket.md") )
- copy_assets_and_create_structure(name, silent, true, {:upgrade => true, :no_configs => true, :local => as_local, :docs => found_docs})
- end
-
- no_commands do
- def copy_assets_and_create_structure(name, silent=false, force=false, options = {})
-
- puts "WARNING: --no_docs deprecated. It is now the default. Specify -docs if you want docs installed." if (options[:no_docs] || options[:nodocs])
- puts "WARNING: --as_gem deprecated. It is now the default. Specify -local if you want ceedling installed to this project." if (options[:as_gem] || options[:asgem])
- puts "WARNING: --with_ignore deprecated. It is now called -gitignore" if (options[:with_ignore] || options[:withignore])
-
- use_docs = options[:docs] || false
- use_configs = !(options[:no_configs] || options[:noconfigs] || false)
- use_gem = !(options[:local])
- use_ignore = options[:gitignore] || false
- is_upgrade = options[:upgrade] || false
-
- ceedling_path = File.join(name, 'vendor', 'ceedling')
- source_path = File.join(name, 'src')
- test_path = File.join(name, 'test')
- test_support_path = File.join(name, 'test/support')
-
- # If it's not an upgrade, make sure we have the paths we expect
- if (!is_upgrade)
- [source_path, test_path, test_support_path].each do |d|
- FileUtils.mkdir_p d
- end
- else
- prj_yaml = YamlWrapper.new.load(File.join(name, 'project.yml'))
- test_support_path = if prj_yaml.key?(:path) && \
- prj_yaml[:path].key?(:support)
- prj_yaml.key?[:path][:support]
- else
- ''
- end
- end
-
- # Genarate gitkeep in test support path
- FileUtils.touch(File.join(test_support_path, '.gitkeep')) unless \
- test_support_path.empty?
-
- # If documentation requested, create a place to dump them and do so
- doc_path = ''
- if use_docs
- doc_path = use_gem ? File.join(name, 'docs') : File.join(ceedling_path, 'docs')
- FileUtils.mkdir_p doc_path
-
- in_doc_path = lambda {|f| File.join(doc_path, f)}
-
- # Add documentation from main projects to list
- doc_files = {}
- ['docs','vendor/unity/docs','vendor/cmock/docs','vendor/cexception/docs'].each do |p|
- Dir[ File.expand_path(File.join(here, p, '*.md')) ].each do |f|
- doc_files[ File.basename(f) ] = f unless(doc_files.include? f)
- end
- end
-
- # Add documentation from plugins to list
- Dir[ File.join(here, 'plugins/**/README.md') ].each do |plugin_path|
- k = "plugin_" + plugin_path.split(/\\|\//)[-2] + ".md"
- doc_files[ k ] = File.expand_path(plugin_path)
- end
-
- # Copy all documentation
- doc_files.each_pair do |k, v|
- copy_file(v, in_doc_path.call(k), :force => force)
- end
- end
-
- # If installed locally to project, copy ceedling, unity, cmock, & supports to vendor
- unless use_gem
- FileUtils.mkdir_p ceedling_path
-
- #copy full folders from ceedling gem into project
- %w{plugins lib bin}.map do |f|
- {:src => f, :dst => File.join(ceedling_path, f)}
- end.each do |f|
- directory(f[:src], f[:dst], :force => force)
- end
+# Get the path for our current code directory
+ceedling_bin_path = File.expand_path( File.join( File.dirname( __FILE__ ), '..', 'bin' ) )
- # mark ceedling as an executable
- File.chmod(0755, File.join(ceedling_path, 'bin', 'ceedling')) unless is_windows?
+# Add load path so we can `require` files in bin/
+$LOAD_PATH.unshift( ceedling_bin_path )
- #copy necessary subcomponents from ceedling gem into project
- sub_components = [
- {:src => 'vendor/c_exception/lib/', :dst => 'vendor/c_exception/lib'},
- {:src => 'vendor/cmock/config/', :dst => 'vendor/cmock/config'},
- {:src => 'vendor/cmock/lib/', :dst => 'vendor/cmock/lib'},
- {:src => 'vendor/cmock/src/', :dst => 'vendor/cmock/src'},
- {:src => 'vendor/diy/lib', :dst => 'vendor/diy/lib'},
- {:src => 'vendor/unity/auto/', :dst => 'vendor/unity/auto'},
- {:src => 'vendor/unity/src/', :dst => 'vendor/unity/src'},
- ]
+# Pull in our startup configuration code in bin/
+require 'app_cfg'
+CEEDLING_APPCFG = CeedlingAppConfig.new()
- sub_components.each do |c|
- directory(c[:src], File.join(ceedling_path, c[:dst]), :force => force)
- end
- end
+# Add load paths for `require 'ceedling/*'` statements in bin/ code
+$LOAD_PATH.unshift( CEEDLING_APPCFG[:ceedling_lib_base_path] )
- # We're copying in a configuration file if we haven't said not to
- if (use_configs)
- dst_yaml = File.join(name, 'project.yml')
- src_yaml = if use_gem
- File.join(here, 'assets', 'project_as_gem.yml')
- else
- if is_windows?
- copy_file(File.join('assets', 'ceedling.cmd'), File.join(name, 'ceedling.cmd'), :force => force)
- else
- copy_file(File.join('assets', 'ceedling'), File.join(name, 'ceedling'), :force => force)
- File.chmod(0755, File.join(name, 'ceedling'))
- end
- File.join(here, 'assets', 'project_with_guts.yml')
- end
+require 'constructor' # Assumed installed via Ceedling gem dependencies
+require 'ceedling/constants'
- # Perform the actual clone of the config file, while updating the version
- File.open(dst_yaml,'w') do |dst|
- require File.expand_path(File.join(File.dirname(__FILE__),"..","lib","ceedling","version.rb"))
- dst << File.read(src_yaml).gsub(":ceedling_version: '?'",":ceedling_version: #{Ceedling::Version::CEEDLING}")
- puts " create #{dst_yaml}"
- end
- end
+# Centralized exception handler for:
+# 1. Bootloader (bin/)
+# 2. Application (lib/) last resort / outer exception handling
+def boom_handler(loginator, exception)
+ $stderr.puts( "\n" )
- # Copy the gitignore file if requested
- if (use_ignore)
- copy_file(File.join('assets', 'default_gitignore'), File.join(name, '.gitignore'), :force => force)
- end
+ if !loginator.nil?
+ loginator.log( exception.message, Verbosity::ERRORS, LogLabels::EXCEPTION )
+ loginator.log( "Backtrace ==>", Verbosity::DEBUG )
+ # Output to console the exception backtrace, formatted like Ruby does it
+ loginator.log( "#{exception.backtrace.first}: #{exception.message} (#{exception.class})", Verbosity::DEBUG )
+ loginator.log( exception.backtrace.drop(1).map{|s| "\t#{s}"}.join("\n"), Verbosity::DEBUG )
- unless silent
- puts "\n"
- puts "Project '#{name}' #{force ? "upgraded" : "created"}!"
- puts " - Tool documentation is located in #{doc_path}" if use_docs
- puts " - Execute 'ceedling help' from #{name} to view available test & build tasks"
- puts ''
- end
- end
- end
-
- desc "examples", "list available example projects"
- def examples()
- puts "Available sample projects:"
- FileUtils.cd(File.join(here, "examples")) do
- Dir["*"].each {|proj| puts " #{proj}"}
- end
- end
-
- desc "example PROJ_NAME [DEST]", "new specified example project (in DEST, if specified)"
- def example(proj_name, dest=nil)
- if dest.nil? then dest = proj_name end
-
- copy_assets_and_create_structure(dest, true, false, {:local=>true, :docs=>true})
-
- dest_src = File.join(dest,'src')
- dest_test = File.join(dest,'test')
- dest_project = File.join(dest,'project.yml')
-
- directory "examples/#{proj_name}/src", dest_src
- directory "examples/#{proj_name}/test", dest_test
- remove_file dest_project
- copy_file "examples/#{proj_name}/project.yml", dest_project
-
- puts "\n"
- puts "Example project '#{proj_name}' created!"
- puts " - Tool documentation is located in vendor/ceedling/docs"
- puts " - Execute 'ceedling help' to view available test & build tasks"
- puts ''
- end
-
- desc "version", "return the version of the tools installed"
- def version()
- require File.expand_path(File.join(File.dirname(__FILE__),"..","lib","ceedling","version.rb"))
- puts " Ceedling:: #{Ceedling::Version::CEEDLING}"
- puts " CMock:: #{Ceedling::Version::CMOCK}"
- puts " Unity:: #{Ceedling::Version::UNITY}"
- puts " CException:: #{Ceedling::Version::CEXCEPTION}"
- end
- end
-
- if (ARGV[0] =~ /^\-T$/)
- puts "\n(No Project Detected, Therefore Showing Options to Create Projects)"
- CeedlingTasks.tasks.each_pair do |k,v|
- puts v.usage.ljust(25,' ') + v.description
- end
- puts "\n"
+ # Something went really wrong... logging isn't even up and running yet
else
- CeedlingTasks.source_root here
- CeedlingTasks.start
- end
-
-#===================================== We Have A Project Already ================================================
-else
- require File.join(here,"lib","ceedling","yaml_wrapper.rb")
- require 'rbconfig'
-
- #determine platform
- platform = begin
- case(RbConfig::CONFIG['host_os'])
- when /mswin|mingw|cygwin/i
- :mswin
- when /darwin/
- :osx
- else
- :linux
- end
- rescue
- :linux
+ $stderr.puts( "#{exception.class} ==> #{exception.message}" )
+ $stderr.puts( "Backtrace ==>" )
+ $stderr.puts( exception.backtrace )
end
+end
- #create our default meta-runner option set
- options = {
- :pretest => nil,
- :args => [],
- :add_path => [],
- :path_connector => (platform == :mswin) ? ";" : ":",
- :graceful_fail => false,
- :which_ceedling => (Dir.exist?("vendor/ceedling") ? "vendor/ceedling" : 'gem'),
- :default_tasks => [ 'test:all' ],
- :list_tasks => false
- }
-
- #guess that we need a special script file first if it exists
- if (platform == :mswin)
- options[:pretest] = File.exist?("#{ platform }_setup.bat") ? "#{ platform }_setup.bat" : nil
- else
- options[:pretest] = File.exist?("#{ platform }_setup.sh") ? "source #{ platform }_setup.sh" : nil
- end
- #merge in project settings if they can be found here
- yaml_options = YamlWrapper.new.load(main_filepath)
- if (yaml_options[:paths])
- options[:add_path] = yaml_options[:paths][:tools] || []
+# Entry point
+begin
+ diy_vendor_path = File.join( CEEDLING_APPCFG[:ceedling_vendor_path], 'diy/lib' )
+
+ # Construct all bootloader objects
+ # 1. Add full path to $LOAD_PATH to simplify objects.yml
+ # 2. Add vendored DIY to $LOAD_PATH so we can use it
+ # 3. Require DIY (used by Ceedling application too)
+ # 4. Perform object construction + dependency injection from bin/objects.yml
+ # 5. Remove all paths added to $LOAD_PATH
+ # (Main application will restore certain paths -- possibly updated by :which_ceedling)
+ $LOAD_PATH.unshift(
+ CEEDLING_APPCFG[:ceedling_lib_path],
+ diy_vendor_path
+ )
+
+ require 'diy'
+ bin_objects_filepath = File.join( ceedling_bin_path, 'objects.yml' )
+ objects = {} # Empty initial hash to be redefined (fingers crossed)
+ objects = DIY::Context.from_yaml( File.read( bin_objects_filepath ) )
+ objects.build_everything()
+
+ # Extract objects shared between bootloader and application
+ # This prevents double instantiation and preserves object state in handoff
+ handoff_objects = {}
+ handoff = [
+ :loginator,
+ :file_wrapper,
+ :yaml_wrapper,
+ :config_walkinator,
+ :system_wrapper,
+ :verbosinator
+ ]
+ CEEDLING_HANDOFF_OBJECTS = handoff_objects
+ handoff.each {|name| handoff_objects[name] = objects[name] }
+
+ # Load Thor-based top-level CLI after:
+ # 1. CEEDLING_BIN load path set
+ # 2. `objects` hash filled with DIY output
+ # 3. CEEDLING_HANDOFF_OBJECTS global is set
+ require 'cli'
+
+ # Remove all load paths we've relied on (main application will set up load paths again)
+ $LOAD_PATH.delete( ceedling_bin_path )
+ $LOAD_PATH.delete( CEEDLING_APPCFG[:ceedling_lib_path] )
+ $LOAD_PATH.delete( diy_vendor_path )
+
+ # Keep a copy of the command line for edge case CLI hacking (Thor consumes ARGV)
+ _ARGV = ARGV.clone
+
+ # Especially on Windows, tell Thor & Rake how wide the terminal is
+ ENV['THOR_COLUMNS'] = CEEDLING_APPCFG[:terminal_width].to_s()
+ ENV['RAKE_COLUMNS'] = ENV['THOR_COLUMNS']
+
+ #
+ # NOTE: See comment block in cli.rb to understand CLI handling
+ # ------------------------------------------------------------
+ #
+
+ # Command line filtering hacks
+ # - Backwards compatibility to silently preserve Rake `-T` CLI handling
+ # - Add in common `--version` or `-v` version handling
+ # Note: This `if` logic is necessary to ensure any other argument lists of size 1 end up with Thor
+ if (ARGV.size() == 1) and (ARGV[0] == '-T' or ARGV[0] == '--version' or ARGV[0] == '-v')
+ case ARGV[0]
+ when '-T'
+ # Call Rake task listing handler w/ default handling of project file and mixins
+ objects[:cli_handler].rake_help( env:ENV, app_cfg:CEEDLING_APPCFG )
+
+ when '--version', '-v'
+ # Call Ceedling's version handler directly
+ objects[:cli_handler].version()
+ end
+
+ # Run command line args through Thor (including "naked" Rake tasks)
else
- options[:add_path] = []
- end
- options[:graceful_fail] = yaml_options[:graceful_fail] if yaml_options[:graceful_fail]
- options[:which_ceedling] = yaml_options[:project][:which_ceedling] if (yaml_options[:project] && yaml_options[:project][:which_ceedling])
- options[:default_tasks] = yaml_options[:default_tasks] if yaml_options[:default_tasks]
-
- #sort through command line options
- ARGV.each do |v|
- case(v)
- when /^(?:new|examples?|templates?)$/
- puts "\nOops. You called ceedling with argument '#{v}'.\n" +
- " This is an operation that will create a new project... \n" +
- " but it looks like you're already in a project. If you really \n" +
- " want to do this, try moving to an empty folder.\n\n"
- abort
- when /^help$/
- options[:list_tasks] = true
- when /^-T$/
- options[:list_tasks] = true
- when /^--tasks$/
- options[:list_tasks] = true
- when /^project:(\w+)/
- ENV['CEEDLING_USER_PROJECT_FILE'] = "#{$1}.yml"
- when /^--test_case=(\w+)/
- ENV['CEEDLING_INCLUDE_TEST_CASE_NAME'] = $1
- when /^--exclude_test_case=(\w+)/
- ENV['CEEDLING_EXCLUDE_TEST_CASE_NAME'] = $1
- else
- options[:args].push(v)
- end
+ CeedlingTasks::CLI.start( ARGV,
+ {
+ :app_cfg => CEEDLING_APPCFG,
+ :objects => objects,
+ }
+ )
end
- #add to the path
- if (options[:add_path] && !options[:add_path].empty?)
- path = ENV["PATH"]
- options[:add_path].each do |p|
- f = File.expand_path(File.dirname(__FILE__),p)
- path = (f + options[:path_connector] + path) unless path.include? f
- end
- ENV["PATH"] = path
+# Handle case of Thor application CLI failing to handle command line arguments.
+rescue Thor::UndefinedCommandError
+ # Marrying Thor & Rake command line handling creates a gap (see comments in CLI handling).
+ # If a user enters only Rake build tasks at the command line followed by Thor flags,
+ # our Thor configuration doesn't see those flags.
+ # We catch the exception of unrecognized Thor commands here (i.e. any "naked" Rake tasks),
+ # and try again by forcing the Thor `build` command at the beginning of the command line.
+ # This way, our Thor handling will process option flags and properly pass the Rake tasks
+ # along as well.
+
+ # Necessary nested exception handling
+ begin
+ CeedlingTasks::CLI.start( _ARGV.unshift( 'build' ),
+ {
+ :app_cfg => CEEDLING_APPCFG,
+ :objects => objects,
+ }
+ )
+ rescue StandardError => ex
+ boom_handler( objects[:loginator], ex )
+ exit(1)
end
- # Load Ceedling (either through the rakefile OR directly)
- if (File.exist?("rakefile.rb"))
- load 'rakefile.rb'
- else
- if (options[:which_ceedling] == 'gem')
- require 'ceedling'
- else
- load "#{options[:which_ceedling]}/lib/ceedling.rb"
- end
- Ceedling.load_project
- end
-
- Rake.application.standard_exception_handling do
- if options[:list_tasks]
- # Display helpful task list when requested. This required us to dig into Rake internals a bit
- Rake.application.define_singleton_method(:name=) {|n| @name = n}
- Rake.application.name = 'ceedling'
- Rake.application.options.show_tasks = :tasks
- Rake.application.options.show_task_pattern = /^(?!.*build).*$/
- Rake.application.display_tasks_and_comments()
- else
- task :default => options[:default_tasks]
-
- # Run our Tasks!
- Rake.application.collect_command_line_tasks(options[:args])
- Rake.application.top_level
- end
- end
- true
-#===================================================================================================================
+# Bootloader boom handling
+rescue StandardError => ex
+ boom_handler( objects[:loginator], ex )
+ exit(1)
end
+
diff --git a/bin/cli.rb b/bin/cli.rb
new file mode 100644
index 000000000..fb4f38632
--- /dev/null
+++ b/bin/cli.rb
@@ -0,0 +1,443 @@
+# =========================================================================
+# Ceedling - Test-Centered Build System for C
+# ThrowTheSwitch.org
+# Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+# SPDX-License-Identifier: MIT
+# =========================================================================
+
+require 'thor'
+require 'ceedling/constants' # From Ceedling application
+
+##
+## Command Line Handling
+## =====================
+##
+## OVERVIEW
+## --------
+## Ceedling's command line handling marries Thor and Rake. Thor does not call
+## Rake. Rather, a handful of command line conventions, edge case handling,
+## and Thor features are stitched together to ensure a given command line is
+## processed by Thor and/or Rake.
+##
+## Ceedling's command line is processed with these mechanisms:
+## 1. Special / edge case hacking of ARGV directly.
+## 2. Thor for all application commands and flags.
+## 3. Handing off to Rake from either (1) or (2) for task listing or running
+## build tasks.
+##
+## EDGE CASE HACKING
+## -----------------
+## Special / edge cases:
+## 1. Silent backwards compatibility support for Rake's `-T`.
+## 2. Thor does not recognize "naked" Rake build tasks as application commands
+## (`ceedling test:all` instead of `ceedling build test:all`). So, we catch
+## this exception and then provide the command line back to Thor as a `build`
+## command line. This also allows us to ensure Thor processes `build` option
+## flags following naked build tasks that would otherwise be ignored if
+## we simply passed a failing command line to Rake.
+##
+## THOR
+## ----
+## Thor is configured or overridden with these special attributes:
+## * The default task is `build`. This means that if the `build` keyword is
+## omitted but Thor otherwise recognizes the command line (a `build` flag is
+## the first item on the command line), it will process it as the `build`
+## command. The build command takes flags and tasks. Tasks are handed off to
+## Rake to process. If no `build` keyword is present and `build` flags come
+## after tasks, Thor sees the command line as unhandled commands.
+## * The PermissiveCLI code handles unrecognized command exception so as to
+## eat the Thor complaint and re-throw the exception for edge case handling.
+##
+## NOTES
+## -----
+## * Ultimately, any unrecognized command or task is processed by Rake, and
+## Rake makes the complaint.
+##
+
+
+##
+## This Class
+## ==========
+##
+## The nature of Thor more-or-less requires this class to be used as a class
+## and not as an insantiated object. This shows up in a variety of ways:
+## * The calling convention is `CeedlingTasks::CLI.start( ARGV )`
+## * Many of the methods necessary to configure the CLI class are class
+## methods in Thor and are called that way.
+##
+## The nature of Thor both requires and allows for some slightly ugly or
+## brittle code -- relying on globals, etc.
+##
+## Because of this, care has been taken that this class contains as little
+## logic as possible and is the funnel for any and all necessary global
+## references and other little oddball needs.
+##
+
+
+# Special handling to prevent Thor from barfing on unrecognized CLI arguments
+# (i.e. Rake tasks)
+module PermissiveCLI
+ def self.extended(base)
+ super
+ base.check_unknown_options!
+ end
+
+ # Redefine the Thor CLI entrypoint and exception handling
+ def start(args, config={})
+ # Copy args as Thor changes them within the call chain of dispatch()
+ _args = args.clone()
+
+ # Call Thor's handlers as it does in start()
+ config[:shell] ||= Thor::Base.shell.new
+ dispatch(nil, args, nil, config)
+
+ # Handle undefined commands at top-level and for `help `
+ rescue Thor::UndefinedCommandError => ex
+ # Handle `help` for an argument that is not an application command such as `new` or `build`
+ if _args[0].downcase() == 'help'
+
+ # Raise ftal StandardError to differentiate from UndefinedCommandError
+ msg = "Argument '#{_args[1]}' is not a recognized application command with detailed help. " +
+ "It may be a build / plugin task without detailed help or simply a goof."
+ raise( msg )
+
+ # Otherwise, eat unhandled command errors
+ else
+ # - No error message
+ # - No `exit()`
+ # - Re-raise to allow special, external CLI handling logic
+ raise ex
+ end
+ end
+end
+
+module CeedlingTasks
+
+ VERBOSITY_NORMAL = 'normal'
+ VERBOSITY_DEBUG = 'debug'
+
+ DOC_LOCAL_FLAG = "Install Ceedling plus supporting tools to vendor/"
+
+ DOC_DOCS_FLAG = "Copy all documentation to docs/ subdirectory of project"
+
+ DOC_PROJECT_FLAG = "Loads the filepath as your base project configuration"
+
+ DOC_MIXIN_FLAG = "Merges the configuration mixin by name or filepath."
+
+ LONGDOC_LOCAL_FLAG = "`--local` copies Ceedling and its dependencies to a vendor/
+ subdirectory in the root of the project. It also installs a
+ platform-appropriate executable script `ceedling` at the root of the
+ project."
+
+ LONGDOC_MIXIN_FLAG = "`--mixin` merges the specified configuration mixin. This
+ flag may be repeated for multiple mixins. A simple mixin name initiates a
+ lookup from within mixin load paths in your project file and among built-in
+ mixins. A filepath and/or filename (with extension) will instead merge the
+ specified YAML file. See documentation for complete details.
+ \x5> --mixin my_compiler --mixin my/path/mixin.yml"
+
+ class CLI < Thor
+ include Thor::Actions
+ extend PermissiveCLI
+
+ # Ensure we bail out with non-zero exit code if the command line is wrong
+ def self.exit_on_failure?() true end
+
+ # Allow `build` to be omitted in command line
+ default_command( :build )
+
+ # Intercept construction to extract configuration and injected dependencies
+ def initialize(args, config, options)
+ super(args, config, options)
+
+ @app_cfg = options[:app_cfg]
+ @handler = options[:objects][:cli_handler]
+
+ @loginator = options[:objects][:loginator]
+
+ # Set the name for labelling CLI interactions
+ CLI::package_name( @loginator.decorate( 'Ceedling application', LogLabels::TITLE ) )
+ end
+
+
+ # Override Thor help to list Rake tasks as well
+ desc "help [COMMAND]", "Describe available commands and list build operations"
+ method_option :project, :type => :string, :default => nil, :aliases => ['-p'], :desc => DOC_PROJECT_FLAG
+ method_option :mixin, :type => :string, :default => [], :repeatable => true, :aliases => ['-m'], :desc => DOC_MIXIN_FLAG
+ method_option :debug, :type => :boolean, :default => false, :hide => true
+ long_desc( CEEDLING_HANDOFF_OBJECTS[:loginator].sanitize(
+ <<-LONGDESC
+ `ceedling help` provides summary help for all available application commands
+ and build tasks.
+
+ COMMAND is optional and will produce detailed help for a specific application command --
+ not a build or plugin task, however.
+
+ `ceedling help` also lists the available build operations from loading your
+ project configuration. Optionally, a project filepath and/or mixins may be
+ provided to load a different project configuration than the default.
+
+ Notes on Optional Flags:
+
+ • #{LONGDOC_MIXIN_FLAG}
+ LONGDESC
+ ) )
+ def help(command=nil)
+ # Get unfrozen copies so we can add / modify
+ _options = options.dup()
+ _options[:project] = options[:project].dup() if !options[:project].nil?
+ _options[:mixin] = []
+ options[:mixin].each {|mixin| _options[:mixin] << mixin.dup() }
+
+ _options[:verbosity] = options[:debug] ? VERBOSITY_DEBUG : nil
+
+ # Call application help with block to execute Thor's built-in help in the help logic
+ @handler.app_help( ENV, @app_cfg, _options, command ) { |command| super(command) }
+ end
+
+
+ desc "new NAME [DEST]", "Create a new project structure at optional DEST path"
+ method_option :local, :type => :boolean, :default => false, :desc => DOC_LOCAL_FLAG
+ method_option :docs, :type => :boolean, :default => false, :desc => DOC_DOCS_FLAG
+ method_option :configs, :type => :boolean, :default => true, :desc => "Install starter project file in project root"
+ method_option :force, :type => :boolean, :default => false, :desc => "Ignore any existing project and recreate destination"
+ method_option :debug, :type => :boolean, :default => false, :hide => true
+ method_option :gitsupport, :type => :boolean, :default => false, :desc => "Create .gitignore / .gitkeep files for convenience"
+ long_desc( CEEDLING_HANDOFF_OBJECTS[:loginator].sanitize(
+ <<-LONGDESC
+ `ceedling new` creates a new project structure.
+
+ NAME is required and will be the containing directory for the new project.
+
+ DEST is an optional directory path for the new project (e.g. /).
+ The default is your working directory. Nonexistent paths will be created.
+
+ Notes on Optional Flags:
+
+ • #{LONGDOC_LOCAL_FLAG}
+
+ • `--force` completely destroys anything found in the target path for the
+ new project.
+ LONGDESC
+ ) )
+ def new(name, dest=nil)
+ # Get unfrozen copies so we can add / modify
+ _options = options.dup()
+ _dest = dest.dup() if !dest.nil?
+
+ _options[:verbosity] = options[:debug] ? VERBOSITY_DEBUG : nil
+
+ @handler.new_project( ENV, @app_cfg, _options, name, _dest )
+ end
+
+
+ desc "upgrade PATH", "Upgrade vendored installation of Ceedling for a project at PATH"
+ method_option :project, :type => :string, :default => DEFAULT_PROJECT_FILENAME, :desc => "Project filename"
+ method_option :debug, :type => :boolean, :default => false, :hide => true
+ long_desc( CEEDLING_HANDOFF_OBJECTS[:loginator].sanitize(
+ <<-LONGDESC
+ `ceedling upgrade` updates an existing project.
+
+ PATH is required and should be the root of the project to upgrade.
+
+ This command only meaningfully operates on projects wth a local vendored copy
+ of Ceedling (in /vendor/) and optional documentation (in
+ /docs/).
+
+ Running this command replaces vendored Ceedling with the version running
+ this command. If docs are found, they will be replaced.
+
+ A basic check for project existence looks for vendored ceedlng and a project
+ configuration file.
+
+ Notes on Optional Flags:
+
+ • `--project` specifies a filename (optionally with leading path) for the
+ project configuration file used in the project existence check. Otherwise,
+ the default ./#{DEFAULT_PROJECT_FILENAME} at the root of the project is
+ checked.
+ LONGDESC
+ ) )
+ def upgrade(path)
+ # Get unfrozen copies so we can add / modify
+ _options = options.dup()
+ _options[:project] = options[:project].dup()
+ _path = path.dup()
+
+ _options[:verbosity] = options[:debug] ? VERBOSITY_DEBUG : nil
+
+ @handler.upgrade_project( ENV, @app_cfg, _options, _path )
+ end
+
+
+ desc "build [TASKS...]", "Run build tasks (`build` keyword not required)"
+ method_option :project, :type => :string, :default => nil, :aliases => ['-p'], :desc => DOC_PROJECT_FLAG
+ method_option :mixin, :type => :string, :default => [], :repeatable => true, :aliases => ['-m'], :desc => DOC_MIXIN_FLAG
+ method_option :verbosity, :type => :string, :default => VERBOSITY_NORMAL, :aliases => ['-v'], :desc => "Sets logging level"
+ method_option :log, :type => :boolean, :default => false, :aliases => ['-l'], :desc => "Enable logging to default filepath in build directory"
+ method_option :logfile, :type => :string, :default => '', :desc => "Enable logging to given filepath"
+ method_option :graceful_fail, :type => :boolean, :default => nil, :desc => "Force exit code of 0 for unit test failures"
+ method_option :test_case, :type => :string, :default => '', :desc => "Filter for individual unit test names"
+ method_option :exclude_test_case, :type => :string, :default => '', :desc => "Prevent matched unit test names from running"
+ # Include for consistency with other commands (override --verbosity)
+ method_option :debug, :type => :boolean, :default => false, :hide => true
+ long_desc( CEEDLING_HANDOFF_OBJECTS[:loginator].sanitize(
+ <<-LONGDESC
+ `ceedling build` executes build tasks created from your project configuration.
+
+ NOTE: `build` is not required to run tasks. The following are equivalent:
+ \x5 > ceedling test:all
+ \x5 > ceedling build test:all
+
+ TASKS are zero or more build operations created from your project configuration.
+ If no tasks are provided, built-in default tasks or your :project ↳
+ :default_tasks will be executed.
+
+ Notes on Optional Flags:
+
+ • #{LONGDOC_MIXIN_FLAG}
+
+ • `--test-case` and its inverse `--exclude-test-case` set test case name
+ matchers to run only a subset of the unit test suite. See docs for full details.
+ LONGDESC
+ ) )
+ def build(*tasks)
+ # Get unfrozen copies so we can add / modify
+ _options = options.dup()
+ _options[:project] = options[:project].dup() if !options[:project].nil?
+ _options[:mixin] = []
+ options[:mixin].each {|mixin| _options[:mixin] << mixin.dup() }
+ _options[:verbosity] = VERBOSITY_DEBUG if options[:debug]
+
+ @handler.build( env:ENV, app_cfg:@app_cfg, options:_options, tasks:tasks )
+ end
+
+
+ desc "dumpconfig FILEPATH [SECTIONS...]", "Process project configuration and write final config to a YAML file"
+ method_option :project, :type => :string, :default => nil, :aliases => ['-p'], :desc => DOC_PROJECT_FLAG
+ method_option :mixin, :type => :string, :default => [], :repeatable => true, :aliases => ['-m'], :desc => DOC_MIXIN_FLAG
+ method_option :app, :type => :boolean, :default => true, :desc => "Runs Ceedling application and its config manipulations"
+ method_option :debug, :type => :boolean, :default => false, :hide => true
+ long_desc( CEEDLING_HANDOFF_OBJECTS[:loginator].sanitize(
+ <<-LONGDESC
+ `ceedling dumpconfig` loads your project configuration, including all manipulations & merges,
+ and writes the final config to a YAML file.
+
+ FILEPATH is a required path to a destination YAML file. A nonexistent path will be created.
+
+ SECTIONS is an optional config “path” that extracts a portion of a configuration. The
+ top-level YAML container will be the path’s last element.
+ The following example will produce config.yml containing ':test_compiler: {...}'.
+ \x5> ceedling dumpconfig my/path/config.yml tools test_compiler
+
+ Notes on Optional Flags:
+
+ • #{LONGDOC_MIXIN_FLAG}
+
+ • `--app` loads various settings, merges defaults, loads plugin config changes, and validates
+ the configuration. Disabling it dumps project config after any mixins but before any
+ application manipulations.
+ LONGDESC
+ ) )
+ def dumpconfig(filepath, *sections)
+ # Get unfrozen copies so we can add / modify
+ _options = options.dup()
+ _options[:project] = options[:project].dup() if !options[:project].nil?
+ _options[:mixin] = []
+ options[:mixin].each {|mixin| _options[:mixin] << mixin.dup() }
+ _filepath = filepath.dup()
+
+ _options[:verbosity] = options[:debug] ? VERBOSITY_DEBUG : nil
+
+ @handler.dumpconfig( ENV, @app_cfg, _options, _filepath, sections )
+ end
+
+
+ desc "environment", "List all configured environment variable names with values."
+ method_option :project, :type => :string, :default => nil, :aliases => ['-p'], :desc => DOC_PROJECT_FLAG
+ method_option :mixin, :type => :string, :default => [], :repeatable => true, :aliases => ['-m'], :desc => DOC_MIXIN_FLAG
+ method_option :debug, :type => :boolean, :default => false, :hide => true
+ long_desc( CEEDLING_HANDOFF_OBJECTS[:loginator].sanitize(
+ <<-LONGDESC
+ `ceedling environment` displays all environment variables that have been set for project use.
+
+ Notes on Optional Flags:
+
+ * #{LONGDOC_MIXIN_FLAG}
+ LONGDESC
+ ) )
+ def environment()
+ # Get unfrozen copies so we can add / modify
+ _options = options.dup()
+ _options[:project] = options[:project].dup() if !options[:project].nil?
+ _options[:mixin] = []
+ options[:mixin].each {|mixin| _options[:mixin] << mixin.dup() }
+
+ _options[:verbosity] = options[:debug] ? VERBOSITY_DEBUG : nil
+
+ @handler.environment( ENV, @app_cfg, _options )
+ end
+
+ desc "examples", "List available example projects"
+ method_option :debug, :type => :boolean, :default => false, :hide => true
+ long_desc( CEEDLING_HANDOFF_OBJECTS[:loginator].sanitize(
+ <<-LONGDESC
+ `ceedling examples` lists the names of the example projects that come packaged with Ceedling.
+
+ The output of this list is most useful when used by the `ceedling example` (no ‘s’) command
+ to extract an example project to your filesystem.
+ LONGDESC
+ ) )
+ def examples()
+ # Get unfrozen copies so we can add / modify
+ _options = options.dup()
+
+ _options[:verbosity] = options[:debug] ? VERBOSITY_DEBUG : nil
+
+ @handler.list_examples( ENV, @app_cfg, _options )
+ end
+
+
+ desc "example NAME [DEST]", "Create named example project in optional DEST path"
+ method_option :local, :type => :boolean, :default => false, :desc => DOC_LOCAL_FLAG
+ method_option :docs, :type => :boolean, :default => false, :desc => DOC_DOCS_FLAG
+ method_option :debug, :type => :boolean, :default => false, :hide => true
+ long_desc( CEEDLING_HANDOFF_OBJECTS[:loginator].sanitize(
+ <<-LONGDESC
+ `ceedling example` extracts the named example project from within Ceedling to
+ your filesystem.
+
+ NAME is required to specify the example to extract. A list of example projects
+ is available with the `examples` command. NAME will be the containing directory
+ for the extracted project.
+
+ DEST is an optional containing directory path (ex: /). The default
+ is your working directory. A nonexistent path will be created.
+
+ Notes on Optional Flags:
+
+ • #{LONGDOC_LOCAL_FLAG}
+
+ NOTE: `example` is destructive. If the destination path is a previoulsy created
+ example project, `ceedling example` will overwrite the contents.
+ LONGDESC
+ ) )
+ def example(name, dest=nil)
+ # Get unfrozen copies so we can add / modify
+ _options = options.dup()
+ _dest = dest.dup() if !dest.nil?
+
+ _options[:verbosity] = options[:debug] ? VERBOSITY_DEBUG : nil
+
+ @handler.create_example( ENV, @app_cfg, _options, name, _dest )
+ end
+
+
+ desc "version", "Display version details of app components (also `--version` or `-v`)"
+ # No long_desc() needed
+ def version()
+ @handler.version()
+ end
+
+ end
+end
diff --git a/bin/cli_handler.rb b/bin/cli_handler.rb
new file mode 100644
index 000000000..236ad036a
--- /dev/null
+++ b/bin/cli_handler.rb
@@ -0,0 +1,398 @@
+# =========================================================================
+# Ceedling - Test-Centered Build System for C
+# ThrowTheSwitch.org
+# Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+# SPDX-License-Identifier: MIT
+# =========================================================================
+
+require 'mixins' # Built-in Mixins
+require 'ceedling/constants' # From Ceedling application
+
+class CliHandler
+
+ constructor :configinator, :projectinator, :cli_helper, :path_validator, :actions_wrapper, :loginator
+
+ # Override to prevent exception handling from walking & stringifying the object variables.
+ # Object variables are lengthy and produce a flood of output.
+ def inspect
+ return this.class.name
+ end
+
+ def setup()
+ # Aliases
+ @helper = @cli_helper
+ @actions = @actions_wrapper
+ end
+
+
+ # Thor application help + Rake help (if available)
+ def app_help(env, app_cfg, options, command, &thor_help)
+ verbosity = @helper.set_verbosity( options[:verbosity] )
+
+ # If help requested for a command, show it and skip listing build tasks
+ if !command.nil?
+ # Block handler
+ thor_help.call( command ) if block_given?
+ return
+ end
+
+ # Display Thor-generated help listing
+ thor_help.call( command ) if block_given?
+
+ # If it was help for a specific command, we're done
+ return if !command.nil?
+
+ # If project configuration is available, also display Rake tasks
+ @path_validator.standardize_paths( options[:project], *options[:mixin], )
+ return if !@projectinator.config_available?( filepath:options[:project], env:env )
+
+ list_rake_tasks(
+ env:env,
+ app_cfg: app_cfg,
+ filepath: options[:project],
+ mixins: options[:mixin],
+ # Silent Ceedling loading unless debug verbosity
+ silent: !(verbosity == Verbosity::DEBUG)
+ )
+ end
+
+
+ # Public to be used by `-T` ARGV hack handling
+ def rake_help(env:, app_cfg:)
+ @helper.set_verbosity() # Default to normal
+
+ list_rake_tasks( env:env, app_cfg:app_cfg )
+ end
+
+
+ def new_project(env, app_cfg, options, name, dest)
+ @helper.set_verbosity( options[:verbosity] )
+
+ @path_validator.standardize_paths( dest )
+
+ # If destination is nil, reassign it to name
+ # Otherwise, join the destination and name into a new path
+ dest = dest.nil? ? ('./' + name) : File.join( dest, name )
+
+ # Check for existing project (unless --force)
+ if @helper.project_exists?( dest, :|, DEFAULT_PROJECT_FILENAME, 'src', 'test' )
+ msg = "It appears a project already exists at #{dest}/. Use --force to destroy it and create a new project."
+ raise msg
+ end unless options[:force]
+
+ # Update app_cfg paths (ignore return values)
+ @helper.which_ceedling?( env:env, app_cfg:app_cfg )
+
+ # Thor Actions for project tasks use paths in relation to this path
+ ActionsWrapper.source_root( app_cfg[:ceedling_root_path] )
+
+ # Blow away any existing directories and contents if --force
+ @actions.remove_dir( dest ) if options[:force]
+
+ # Create blank directory structure
+ ['.', 'src', 'test', 'test/support'].each do |path|
+ @actions._empty_directory( File.join( dest, path) )
+ end
+
+ # Vendor the tools and install command line helper scripts
+ @helper.vendor_tools( app_cfg[:ceedling_root_path], dest ) if options[:local]
+
+ # Copy in documentation
+ @helper.copy_docs( app_cfg[:ceedling_root_path], dest ) if options[:docs]
+
+ # Copy / set up project file
+ @helper.create_project_file( dest, options[:local] ) if options[:configs]
+
+ # Copy Git Ignore file
+ if options[:gitsupport]
+ @actions._copy_file(
+ File.join( 'assets', 'default_gitignore' ),
+ File.join( dest, '.gitignore' ),
+ :force => true
+ )
+ @actions._touch_file( File.join( dest, 'test/support', '.gitkeep') )
+ end
+
+ @loginator.log() # Blank line
+ @loginator.log( "New project '#{name}' created at #{dest}/\n", Verbosity::NORMAL, LogLabels::TITLE )
+ end
+
+
+ def upgrade_project(env, app_cfg, options, path)
+ @helper.set_verbosity( options[:verbosity] )
+
+ @path_validator.standardize_paths( path, options[:project] )
+
+ # Check for existing project
+ if !@helper.project_exists?( path, :&, options[:project], 'vendor/ceedling/lib/ceedling/version.rb' )
+ msg = "Could not find an existing project at #{path}/."
+ raise msg
+ end
+
+ which, _ = @helper.which_ceedling?( env:env, app_cfg:app_cfg )
+ if (which == :gem)
+ msg = "Project configuration specifies the Ceedling gem, not vendored Ceedling"
+ @loginator.log( msg, Verbosity::NORMAL, LogLabels::NOTICE )
+ end
+
+ # Thor Actions for project tasks use paths in relation to this path
+ ActionsWrapper.source_root( app_cfg[:ceedling_root_path] )
+
+ # Recreate vendored tools
+ vendor_path = File.join( path, 'vendor', 'ceedling' )
+ @actions.remove_dir( vendor_path )
+ @helper.vendor_tools( app_cfg[:ceedling_root_path], path )
+
+ # Recreate documentation if we find docs/ subdirectory
+ docs_path = File.join( path, 'docs' )
+ founds_docs = @helper.project_exists?( path, :&, File.join( 'docs', 'CeedlingPacket.md' ) )
+ if founds_docs
+ @actions.remove_dir( docs_path )
+ @helper.copy_docs( app_cfg[:ceedling_root_path], path )
+ end
+
+ @loginator.log() # Blank line
+ @loginator.log( "Upgraded project at #{path}/\n", Verbosity::NORMAL, LogLabels::TITLE )
+ end
+
+
+ def build(env:, app_cfg:, options:{}, tasks:)
+ @helper.set_verbosity( options[:verbosity] )
+
+ @path_validator.standardize_paths( options[:project], options[:logfile], *options[:mixin] )
+
+ _, config = @configinator.loadinate( builtin_mixins:BUILTIN_MIXINS, filepath:options[:project], mixins:options[:mixin], env:env )
+
+ default_tasks = @configinator.default_tasks( config:config, default_tasks:app_cfg[:default_tasks] )
+
+ @helper.process_testcase_filters(
+ config: config,
+ include: options[:test_case],
+ exclude: options[:exclude_test_case],
+ tasks: tasks,
+ default_tasks: default_tasks
+ )
+
+ log_filepath = @helper.process_logging( options[:log], options[:logfile] )
+
+ # Save references
+ app_cfg.set_project_config( config )
+ app_cfg.set_log_filepath( log_filepath )
+ app_cfg.set_include_test_case( options[:test_case] )
+ app_cfg.set_exclude_test_case( options[:exclude_test_case] )
+
+ # Set graceful_exit from command line & configuration options
+ app_cfg.set_tests_graceful_fail(
+ @helper.process_graceful_fail(
+ config: config,
+ cmdline_graceful_fail: options[:graceful_fail],
+ tasks: tasks,
+ default_tasks: default_tasks
+ )
+ )
+
+ # Enable setup / operations duration logging in Rake context
+ app_cfg.set_stopwatch( @helper.process_stopwatch( tasks:tasks, default_tasks:default_tasks ) )
+
+ _, path = @helper.which_ceedling?( env:env, config:config, app_cfg:app_cfg )
+
+ @helper.load_ceedling(
+ config: config,
+ rakefile_path: path,
+ default_tasks: default_tasks
+ )
+
+ # Hand Rake tasks off to be executed
+ @helper.run_rake_tasks( tasks )
+ end
+
+
+ def dumpconfig(env, app_cfg, options, filepath, sections)
+ @helper.set_verbosity( options[:verbosity] )
+
+ @path_validator.standardize_paths( filepath, options[:project], *options[:mixin] )
+
+ _, config = @configinator.loadinate( builtin_mixins:BUILTIN_MIXINS, filepath:options[:project], mixins:options[:mixin], env:env )
+
+ # Exception handling to ensure we dump the configuration regardless of config validation errors
+ begin
+ # If enabled, process the configuration through Ceedling automatic settings, defaults, plugins, etc.
+ if options[:app]
+ default_tasks = @configinator.default_tasks( config:config, default_tasks:app_cfg[:default_tasks] )
+
+ # Save references
+ app_cfg.set_project_config( config )
+
+ _, path = @helper.which_ceedling?( env:env, config:config, app_cfg:app_cfg )
+
+ config = @helper.load_ceedling(
+ config: config,
+ rakefile_path: path,
+ default_tasks: default_tasks
+ )
+ else
+ @loginator.log( " > Skipped loading Ceedling application", Verbosity::OBNOXIOUS )
+ end
+ ensure
+ @helper.dump_yaml( config, filepath, sections )
+
+ @loginator.log() # Blank line
+ @loginator.log( "Dumped project configuration to #{filepath}\n", Verbosity::NORMAL, LogLabels::TITLE )
+ end
+ end
+
+
+ def environment(env, app_cfg, options)
+ @helper.set_verbosity( options[:verbosity] )
+
+ @path_validator.standardize_paths( options[:project], *options[:mixin] )
+
+ _, config = @configinator.loadinate( builtin_mixins:BUILTIN_MIXINS, filepath:options[:project], mixins:options[:mixin], env:env )
+
+ # Save references
+ app_cfg.set_project_config( config )
+
+ _, path = @helper.which_ceedling?( env:env, config:config, app_cfg:app_cfg )
+
+ config = @helper.load_ceedling(
+ config: config,
+ rakefile_path: path
+ )
+
+ env_list = []
+
+ # Process external environment -- filter for Ceedling variables
+ env.each do |var, value|
+ next if !(var =~ /ceedling/i)
+ name = var.to_s
+ env_list << "#{name}: \"#{value}\""
+ end
+
+ # Process environment created by configuration
+ config[:environment].each do |env|
+ env.each_key do |key|
+ name = key.to_s
+ env_list << "#{name}: \"#{env[key]}\""
+ end
+ end
+
+ output = "Environment variables:\n"
+
+ env_list.sort.each do |line|
+ output << " • #{line}\n"
+ end
+
+ @loginator.log() # Blank line
+ @loginator.log( output + "\n", Verbosity::NORMAL, LogLabels::TITLE )
+ end
+
+
+ def list_examples(env, app_cfg, options)
+ @helper.set_verbosity( options[:verbosity] )
+
+ # Process which_ceedling for app_cfg modifications but ignore return values
+ @helper.which_ceedling?( env:env, app_cfg:app_cfg )
+
+ examples = @helper.lookup_example_projects( app_cfg[:ceedling_examples_path] )
+
+ raise( "No examples projects found") if examples.empty?
+
+ output = "Available example projects:\n"
+
+ examples.each {|example| output << " • #{example}\n" }
+
+ @loginator.log() # Blank line
+ @loginator.log( output + "\n", Verbosity::NORMAL, LogLabels::TITLE )
+ end
+
+
+ def create_example(env, app_cfg, options, name, dest)
+ @helper.set_verbosity( options[:verbosity] )
+
+ @path_validator.standardize_paths( dest )
+
+ # Process which_ceedling for app_cfg modifications but ignore return values
+ @helper.which_ceedling?( env:env, app_cfg:app_cfg )
+
+ examples = @helper.lookup_example_projects( app_cfg[:ceedling_examples_path] )
+
+ if !examples.include?( name )
+ raise( "No example project '#{name}' could be found" )
+ end
+
+ # If destination is nil, reassign it to name
+ # Otherwise, join the destination and name into a new path
+ dest = dest.nil? ? ('./' + name) : File.join( dest, name )
+
+ dest_src = File.join( dest, 'src' )
+ dest_test = File.join( dest, 'test' )
+ dest_mixin = File.join( dest, 'mixin' )
+ dest_project = File.join( dest, DEFAULT_PROJECT_FILENAME )
+ dest_readme = File.join( dest, 'README.md' )
+
+ # Thor Actions for project tasks use paths in relation to this path
+ ActionsWrapper.source_root( app_cfg[:ceedling_root_path] )
+
+ @actions._directory( "examples/#{name}/src", dest_src, :force => true )
+ @actions._directory( "examples/#{name}/test", dest_test, :force => true )
+ @actions._directory( "examples/#{name}/mixin", dest_mixin, :force => true )
+ @actions._copy_file( "examples/#{name}/#{DEFAULT_PROJECT_FILENAME}", dest_project, :force => true )
+ @actions._copy_file( "examples/#{name}/README.md", dest_readme, :force => true )
+
+ # Vendor the tools and install command line helper scripts
+ @helper.vendor_tools( app_cfg[:ceedling_root_path], dest ) if options[:local]
+
+ # Copy in documentation
+ @helper.copy_docs( app_cfg[:ceedling_root_path], dest ) if options[:docs]
+
+ @loginator.log() # Blank line
+ @loginator.log( "Example project '#{name}' created at #{dest}/\n", Verbosity::NORMAL, LogLabels::TITLE )
+ end
+
+
+ def version()
+ require 'ceedling/version'
+ version = <<~VERSION
+ Welcome to Ceedling!
+
+ Ceedling => #{Ceedling::Version::CEEDLING}
+ CMock => #{Ceedling::Version::CMOCK}
+ Unity => #{Ceedling::Version::UNITY}
+ CException => #{Ceedling::Version::CEXCEPTION}
+ VERSION
+ @loginator.log( version, Verbosity::NORMAL, LogLabels::TITLE )
+ end
+
+
+ ### Private ###
+
+ private
+
+ def list_rake_tasks(env:, app_cfg:, filepath:nil, mixins:[], silent:false)
+ _, config =
+ @configinator.loadinate(
+ builtin_mixins:BUILTIN_MIXINS,
+ filepath: filepath,
+ mixins: mixins,
+ env: env,
+ silent: silent
+ )
+
+ # Save reference to loaded configuration
+ app_cfg.set_project_config( config )
+
+ _, path = @helper.which_ceedling?( env:env, config:config, app_cfg:app_cfg )
+
+ @helper.load_ceedling(
+ config: config,
+ rakefile_path: path,
+ default_tasks: app_cfg[:default_tasks]
+ )
+
+ msg = "Ceedling build & plugin tasks:\n(Parameterized tasks tend to need enclosing quotes or escape sequences in most shells)"
+ @loginator.log( msg, Verbosity::NORMAL, LogLabels::TITLE )
+
+ @helper.print_rake_tasks()
+ end
+
+end
diff --git a/bin/cli_helper.rb b/bin/cli_helper.rb
new file mode 100644
index 000000000..bb432349b
--- /dev/null
+++ b/bin/cli_helper.rb
@@ -0,0 +1,440 @@
+# =========================================================================
+# Ceedling - Test-Centered Build System for C
+# ThrowTheSwitch.org
+# Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+# SPDX-License-Identifier: MIT
+# =========================================================================
+
+require 'app_cfg'
+require 'ceedling/constants' # From Ceedling application
+
+class CliHelper
+
+ constructor :file_wrapper, :actions_wrapper, :config_walkinator, :path_validator, :loginator, :system_wrapper
+
+ def setup
+ # Aliases
+ @actions = @actions_wrapper
+ end
+
+
+ def project_exists?( path, op, *components )
+ exists = []
+
+ components.each do |f|
+ _path = File.join( path, f )
+ exists << (@file_wrapper.exist?( _path ) or @file_wrapper.directory?( _path ))
+ end
+
+ return exists.reduce(op)
+ end
+
+
+ def create_project_file(dest, local)
+ project_filepath = File.join( dest, DEFAULT_PROJECT_FILENAME )
+ source_filepath = ''
+
+ if local
+ source_filepath = File.join( 'assets', 'project_with_guts.yml' )
+ else
+ source_filepath = File.join( 'assets', 'project_as_gem.yml' )
+ end
+
+ # Clone the project file and update internal version
+ require 'ceedling/version'
+ @actions._copy_file( source_filepath, project_filepath, :force => true)
+ @actions._gsub_file( project_filepath, /:ceedling_version:\s+'\?'/, ":ceedling_version: #{Ceedling::Version::CEEDLING}" )
+ end
+
+
+ # Returns two value: (1) symbol :gem or :path and (2) path for Ceedling installation
+ def which_ceedling?(env:, config:{}, app_cfg:)
+ # Determine which Ceedling we're running (in priority)
+ # 1. If there's an environment variable set, validate it, and return :gem or a path
+ # 2. If :project ↳ :which_ceedling exists in the config, validate it, and return :gem or a path
+ # 3. If there's a vendor/ceedling/ path in our working directory, return it as a path
+ # 4. If nothing is set, default to :gem and return it
+ # 5. Update app_cfg paths if not the gem
+
+ # Nil for prioritized case checking logic blocks that follow
+ which_ceedling = nil
+
+ # Environment variable
+ if !env['WHICH_CEEDLING'].nil?
+ @loginator.log( " > Set which Ceedling using environment variable WHICH_CEEDLING", Verbosity::OBNOXIOUS )
+ which_ceedling = env['WHICH_CEEDLING'].strip()
+ which_ceedling = :gem if (which_ceedling.casecmp( 'gem' ) == 0)
+ end
+
+ # Configuration file
+ if which_ceedling.nil?
+ walked = @config_walkinator.fetch_value( config, :project, :which_ceedling )
+ if !walked[:value].nil?
+ which_ceedling = walked[:value].strip()
+ @loginator.log( " > Set which Ceedling from config :project ↳ :which_ceedling => #{which_ceedling}", Verbosity::OBNOXIOUS )
+ which_ceedling = :gem if (which_ceedling.casecmp( 'gem' ) == 0)
+ end
+ end
+
+ # Working directory
+ if which_ceedling.nil?
+ if @file_wrapper.directory?( 'vendor/ceedling' )
+ which_ceedling = 'vendor/ceedling'
+ @loginator.log( " > Set which Ceedling to be vendored installation", Verbosity::OBNOXIOUS )
+ end
+ end
+
+ # Default to gem
+ if which_ceedling.nil?
+ which_ceedling = :gem
+ @loginator.log( " > Defaulting to running Ceedling from Gem", Verbosity::OBNOXIOUS )
+ end
+
+ # If we're launching from the gem, return :gem and initial Rakefile path
+ if which_ceedling == :gem
+ return which_ceedling, app_cfg[:ceedling_rakefile_filepath]
+ end
+
+ # Otherwise, handle which_ceedling as a base path
+ ceedling_path = which_ceedling.dup()
+ @path_validator.standardize_paths( ceedling_path )
+ if !@file_wrapper.directory?( ceedling_path )
+ raise "Configured Ceedling launch path #{ceedling_path}/ does not exist"
+ end
+
+ # Update Ceedling installation paths
+ app_cfg.set_paths( ceedling_path )
+
+ # Check updated Ceedling paths
+ if !@file_wrapper.exist?( app_cfg[:ceedling_rakefile_filepath] )
+ raise "Configured Ceedling launch path #{ceedling_path}/ contains no Ceedling installation"
+ end
+
+ # Update variable to full application start path
+ ceedling_path = app_cfg[:ceedling_rakefile_filepath]
+
+ @loginator.log( " > Launching Ceedling from #{ceedling_path}/", Verbosity::OBNOXIOUS )
+
+ return :path, ceedling_path
+ end
+
+
+ def load_ceedling(config:, rakefile_path:, default_tasks:[])
+ # Set default tasks
+ Rake::Task.define_task(:default => default_tasks) if !default_tasks.empty?
+
+ # Load Ceedling application from Rakefile path
+ require( rakefile_path )
+
+ # Loading the Rakefile manipulates the config hash, return it as a convenience
+ return config
+ end
+
+
+ def process_testcase_filters(config:, include:, exclude:, tasks:, default_tasks:)
+ # Do nothing if no test case filters
+ return if (include.nil? || include.empty?) && (exclude.nil? || exclude.empty?)
+
+ # TODO: When we can programmatically check if a task is a test task,
+ # raise an exception if --graceful-fail is set without test operations
+
+ # Add test runner configuration setting necessary to use test case filters
+ walked = @config_walkinator.fetch_value( config, :test_runner )
+ if walked[:value].nil?
+ # If no :test_runner section, create the whole thing
+ config[:test_runner] = {:cmdline_args => true}
+ else
+ # If a :test_runner section, just set :cmdlne_args
+ walked[:value][:cmdline_args] = true
+ end
+ end
+
+
+ def process_graceful_fail(config:, cmdline_graceful_fail:, tasks:, default_tasks:)
+ # TODO: When we can programmatically check if a task is a test task,
+ # raise an exception if --graceful-fail is set without test operations
+
+ # Precedence
+ # 1. Command line option
+ # 2. Configuration entry
+
+ # If command line option was set, use it
+ return cmdline_graceful_fail if !cmdline_graceful_fail.nil?
+
+ # If configuration contains :graceful_fail, use it
+ walked = @config_walkinator.fetch_value( config, :test_build, :graceful_fail )
+ return walked[:value] if !walked[:value].nil?
+
+ return false
+ end
+
+
+ def process_logging(enabled, filepath)
+ # No log file if neither enabled nor a specific filename/filepath
+ return '' if !enabled && (filepath.nil? || filepath.empty?())
+
+ # Default logfile name (to be placed in default location) if enabled but no filename/filepath
+ return DEFAULT_CEEDLING_LOGFILE if enabled && filepath.empty?()
+
+ # Otherwise, a filename/filepath was provided that implicitly enables logging
+ dir = File.dirname( filepath )
+
+ # Ensure logging directory path exists
+ if not dir.empty?
+ @file_wrapper.mkdir( dir )
+ end
+
+ # Return filename/filepath
+ return filepath
+ end
+
+
+ def process_stopwatch(tasks:, default_tasks:)
+ _tasks = tasks.empty?() ? default_tasks.dup() : tasks.dup()
+
+ # Namespace-less (clobber, clean, etc.), files:, and paths: tasks should not have stopwatch logging
+ # 1. Filter out tasks lacking a namespace
+ # 2. Look for any tasks other than paths: or files:
+ _tasks.select! {|t| t.include?( ':') }
+ _tasks.reject! {|t| t =~ /(^files:|^paths:)/}
+
+ return !_tasks.empty?
+ end
+
+
+ def print_rake_tasks()
+ # (This required digging into Rake internals a bit.)
+ Rake.application.define_singleton_method(:name=) {|n| @name = n}
+ Rake.application.name = 'ceedling'
+ Rake.application.options.show_tasks = :tasks
+ Rake.application.options.show_task_pattern = /^(?!.*build).*$/
+ Rake.application.display_tasks_and_comments()
+ end
+
+
+ def run_rake_tasks(tasks)
+ Rake.application.collect_command_line_tasks( tasks )
+ Rake.application.top_level()
+ end
+
+
+ # Set global consts for verbosity and debug
+ def set_verbosity(verbosity=nil)
+ verbosity = if verbosity.nil?
+ Verbosity::NORMAL
+ elsif verbosity.to_i.to_s == verbosity
+ verbosity.to_i
+ elsif VERBOSITY_OPTIONS.include? verbosity.to_sym
+ VERBOSITY_OPTIONS[verbosity.to_sym]
+ else
+ raise "Unkown Verbosity '#{verbosity}' specified"
+ end
+
+ # If we already set verbosity, there's nothing more to do here
+ return if Object.const_defined?('PROJECT_VERBOSITY')
+
+ # Create global constant PROJECT_VERBOSITY
+ Object.module_eval("PROJECT_VERBOSITY = verbosity")
+ PROJECT_VERBOSITY.freeze()
+
+ # Create global constant PROJECT_DEBUG
+ debug = (verbosity == Verbosity::DEBUG)
+ Object.module_eval("PROJECT_DEBUG = debug")
+ PROJECT_DEBUG.freeze()
+
+ return verbosity
+ end
+
+
+ def dump_yaml(config, filepath, sections)
+ # Default to dumping entire configuration
+ _config = config
+
+ # If sections were provided, process them
+ if !sections.empty?
+ # Symbolify section names
+ _sections = sections.map {|section| section.to_sym}
+
+ # Try to extract subconfig from section path
+ walked = @config_walkinator.fetch_value( config, *_sections )
+
+ # If we fail to find the section path, blow up
+ if walked[:value].nil?
+ # Reformat list of symbols to list of :s
+ _sections.map! {|section| ":#{section.to_s}"}
+ msg = "Cound not find configuration section #{_sections.join(' ↳ ')}"
+ raise(msg)
+ end
+
+ # Update _config to subconfig with final sections path element as container
+ _config = { _sections.last => walked[:value] }
+ end
+
+ File.open( filepath, 'w' ) {|out| YAML.dump( _config, out )}
+ end
+
+
+ def lookup_example_projects(examples_path)
+ examples = []
+
+ # Examples directory listing glob
+ glob = File.join( examples_path, '*' )
+
+ @file_wrapper.directory_listing(glob).each do |path|
+ # Skip anything that's not a directory
+ next if !@file_wrapper.directory?( path )
+
+ # Split the directory path into elements, indexing the last one
+ project = (path.split( File::SEPARATOR ))[-1]
+
+ examples << project
+ end
+
+ return examples
+ end
+
+
+ def copy_docs(ceedling_root, dest)
+ docs_path = File.join( dest, 'docs' )
+
+ # Hash that will hold documentation copy paths
+ # - Key: (modified) destination documentation path
+ # - Value: source path
+ doc_files = {}
+
+ # Add docs to list from Ceedling (docs/) and supporting projects (docs/)
+ { # Source path => docs/ destination path
+ 'docs' => '.',
+ 'vendor/unity/docs' => 'unity',
+ 'vendor/cmock/docs' => 'cmock',
+ 'vendor/c_exception/docs' => 'c_exception'
+ }.each do |src, dest|
+ # Form glob to collect all markdown files
+ glob = File.join( ceedling_root, src, '*.md' )
+ # Look up markdown files
+ listing = @file_wrapper.directory_listing( glob ) # Already case-insensitive
+ # For each markdown filepath, add to hash
+ listing.each do |filepath|
+ # Reassign destination
+ _dest = File.join( dest, File.basename(filepath) )
+ doc_files[ _dest ] = filepath
+ end
+ end
+
+ # Add docs to list from Ceedling plugins (docs/plugins)
+ glob = File.join( ceedling_root, 'plugins/**/README.md' )
+ listing = @file_wrapper.directory_listing( glob ) # Already case-insensitive
+ listing.each do |path|
+ # 'README.md' => '.md' where name extracted from containing path
+ rename = path.split(/\\|\//)[-2] + '.md'
+ # For each Ceedling plugin readme, add to hash
+ dest = File.join( 'plugins', rename )
+ doc_files[ dest ] = path
+ end
+
+ # Add licenses from Ceedling (docs/) and supporting projects (docs/)
+ { # Destination path => Source path
+ '.' => '.', # Ceedling
+ 'unity' => 'vendor/unity',
+ 'cmock' => 'vendor/cmock',
+ 'c_exception' => 'vendor/c_exception',
+ }.each do |dest, src|
+ glob = File.join( ceedling_root, src, 'license.txt' )
+ # Look up licenses (use glob as capitalization can be inconsistent)
+ listing = @file_wrapper.directory_listing( glob ) # Already case-insensitive
+ # Safety check on nil references since we explicitly reference first element
+ next if listing.empty?
+ filepath = listing.first
+ # Reassign dest
+ dest = File.join( dest, File.basename( filepath ) )
+ doc_files[ dest ] = filepath
+ end
+
+ # Copy all documentation
+ doc_files.each_pair do |dest, src|
+ @actions._copy_file(src, File.join( docs_path, dest ), :force => true)
+ end
+ end
+
+
+ def vendor_tools(ceedling_root, dest)
+ vendor_path = File.join( dest, 'vendor', 'ceedling' )
+
+ # Copy folders from current Ceedling into project
+ %w{plugins lib bin}.each do |folder|
+ @actions._directory(
+ folder,
+ File.join( vendor_path, folder ),
+ :force => true
+ )
+ end
+
+ # Mark ceedling as an executable
+ @actions._chmod( File.join( vendor_path, 'bin', 'ceedling' ), 0755 ) unless @system_wrapper.windows?
+
+ # Assembly necessary subcomponent dirs
+ components = [
+ 'vendor/c_exception/lib/',
+ 'vendor/cmock/config/',
+ 'vendor/cmock/lib/',
+ 'vendor/cmock/src/',
+ 'vendor/diy/lib/',
+ 'vendor/unity/auto/',
+ 'vendor/unity/src/',
+ ]
+
+ # Copy necessary subcomponent dirs into project
+ components.each do |path|
+ _src = path
+ _dest = File.join( vendor_path, path )
+ @actions._directory( _src, _dest, :force => true )
+ end
+
+ # Add licenses from Ceedling and supporting projects
+ license_files = {}
+ [ # Source paths
+ '.', # Ceedling
+ 'vendor/unity',
+ 'vendor/cmock',
+ 'vendor/c_exception',
+ 'vendor/diy'
+ ].each do |src|
+ # Look up licenses using a Glob as capitalization can be inconsistent
+ glob = File.join( ceedling_root, src, 'license.txt' )
+ listing = @file_wrapper.directory_listing( glob ) # Already case-insensitive
+
+ # Safety check on nil references since we explicitly reference first element
+ next if listing.empty?
+
+ # Add license copying to hash
+ license = listing.first
+ filepath = File.join( vendor_path, src, File.basename( license ) )
+ license_files[ filepath ] = license
+ end
+
+ # Copy license files into place
+ license_files.each_pair do |dest, src|
+ @actions._copy_file( src, dest, :force => true)
+ end
+
+ # Create executable helper scripts in project root
+ if @system_wrapper.windows?
+ # Windows command prompt launch script
+ @actions._copy_file(
+ File.join( 'assets', 'ceedling.cmd'),
+ File.join( dest, 'ceedling.cmd'),
+ :force => true
+ )
+ else
+ # Unix shell launch script
+ launch = File.join( dest, 'ceedling')
+ @actions._copy_file(
+ File.join( 'assets', 'ceedling'),
+ launch,
+ :force => true
+ )
+ @actions._chmod( launch, 0755 )
+ end
+ end
+
+end
diff --git a/bin/configinator.rb b/bin/configinator.rb
new file mode 100644
index 000000000..b99aa86f5
--- /dev/null
+++ b/bin/configinator.rb
@@ -0,0 +1,111 @@
+# =========================================================================
+# Ceedling - Test-Centered Build System for C
+# ThrowTheSwitch.org
+# Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+# SPDX-License-Identifier: MIT
+# =========================================================================
+
+require 'deep_merge'
+
+class Configinator
+
+ constructor :config_walkinator, :projectinator, :mixinator
+
+ def loadinate(builtin_mixins:, filepath:nil, mixins:[], env:{}, silent:false)
+ # Aliases for clarity
+ cmdline_filepath = filepath
+ cmdline_mixins = mixins || []
+
+ # Load raw config from command line, environment variable, or default filepath
+ project_filepath, config = @projectinator.load( filepath:cmdline_filepath, env:env, silent:silent )
+
+ # Extract cfg_enabled_mixins mixins list plus load paths list from config
+ cfg_enabled_mixins, cfg_load_paths = @projectinator.extract_mixins( config: config )
+
+ # Get our YAML file extension
+ yaml_ext = @projectinator.lookup_yaml_extension( config:config )
+
+ # Remove any silly redundancies
+ cfg_enabled_mixins.uniq!
+ # Use absolute path to ensure proper deduplication
+ cfg_load_paths.uniq! { |path| File.expand_path(path) }
+ cmdline_mixins.uniq!
+
+ # Validate :cfg_load_paths from :mixins section of project configuration
+ @projectinator.validate_mixin_load_paths( cfg_load_paths )
+
+ # Validate enabled mixins from :mixins section of project configuration
+ if not @projectinator.validate_mixins(
+ mixins: cfg_enabled_mixins,
+ load_paths: cfg_load_paths,
+ builtins: builtin_mixins,
+ source: 'Config :mixins ↳ :enabled =>',
+ yaml_extension: yaml_ext
+ )
+ raise 'Project configuration file section :mixins failed validation'
+ end
+
+ # Validate command line mixins
+ if not @projectinator.validate_mixins(
+ mixins: cmdline_mixins,
+ load_paths: cfg_load_paths,
+ builtins: builtin_mixins,
+ source: 'Mixin',
+ yaml_extension: yaml_ext
+ )
+ raise 'Command line failed validation'
+ end
+
+ # Find mixins in project file among load paths or built-in mixins
+ # Return ordered list of filepaths or built-in mixin names
+ config_mixins = @projectinator.lookup_mixins(
+ mixins: cfg_enabled_mixins,
+ load_paths: cfg_load_paths,
+ builtins: builtin_mixins,
+ yaml_extension: yaml_ext
+ )
+
+ # Find mixins from command line among load paths or built-in mixins
+ # Return ordered list of filepaths or built-in mixin names
+ cmdline_mixins = @projectinator.lookup_mixins(
+ mixins: cmdline_mixins,
+ load_paths: cfg_load_paths,
+ builtins: builtin_mixins,
+ yaml_extension: yaml_ext
+ )
+
+ # Fetch CEEDLING_MIXIN_# environment variables
+ # Sort into ordered list of hash tuples [{env variable => filepath}...]
+ env_mixins = @mixinator.fetch_env_filepaths( env )
+ @mixinator.validate_env_filepaths( env_mixins )
+
+ # Eliminate duplicate mixins and return list of mixins in merge order
+ # [{source => filepath}...]
+ mixins_assembled = @mixinator.assemble_mixins(
+ config: config_mixins,
+ env: env_mixins,
+ cmdline: cmdline_mixins
+ )
+
+ # Merge mixins
+ @mixinator.merge( builtins:builtin_mixins, config:config, mixins:mixins_assembled )
+
+ return project_filepath, config
+ end
+
+ def default_tasks(config:, default_tasks:)
+ # 1. If :default_tasks set in config, use it
+ # 2. Otherwise use the function argument (most likely a default set in the first moments of startup)
+ walked = @config_walkinator.fetch_value( config, :project, :default_tasks )
+ if walked[:value]
+ # Update method parameter to config value
+ default_tasks = walked[:value].dup()
+ else
+ # Set key/value in config if it's not set
+ config.deep_merge( {:project => {:default_tasks => default_tasks}} )
+ end
+
+ return default_tasks
+ end
+
+end
\ No newline at end of file
diff --git a/bin/mixinator.rb b/bin/mixinator.rb
new file mode 100644
index 000000000..c8e39ce4d
--- /dev/null
+++ b/bin/mixinator.rb
@@ -0,0 +1,131 @@
+# =========================================================================
+# Ceedling - Test-Centered Build System for C
+# ThrowTheSwitch.org
+# Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+# SPDX-License-Identifier: MIT
+# =========================================================================
+
+require 'deep_merge'
+
+class Mixinator
+
+ constructor :path_validator, :yaml_wrapper, :loginator
+
+ def setup
+ # ...
+ end
+
+ def validate_cmdline_filepaths(paths)
+ validated = @path_validator.validate(
+ paths: paths,
+ source: 'Filepath argument',
+ )
+
+ if !validated
+ raise 'Mixins command line failed validation'
+ end
+ end
+
+ def fetch_env_filepaths(env)
+ var_names = []
+
+ env.each do |var, filepath|
+ # Explicitly ignores CEEDLING_MIXIN_0
+ var_names << var if var =~ /CEEDLING_MIXIN_[1-9]\d*/
+ end
+
+ # Extract numeric string (guranteed to exist) and convert to integer for ascending sorting
+ var_names.sort_by! {|name| name.match(/\d+$/)[0].to_i() }
+
+ _vars = []
+ # Iterate over sorted environment variable names
+ var_names.each do |name|
+ # Duplicate the filepath string to get unfrozen copy
+ # Handle any Windows path shenanigans
+ # Insert in array {env var name => filepath}
+ path = env[name].dup()
+ @path_validator.standardize_paths( path )
+ _vars << {name => path}
+ end
+
+ # Remove any duplicate filepaths by comparing the full absolute path
+ # Higher numbered environment variables removed
+ _vars.uniq! {|entry| File.expand_path( entry.values.first )}
+
+ return _vars
+ end
+
+ def validate_env_filepaths(vars)
+ validated = true
+
+ vars.each do |entry|
+ validated &= @path_validator.validate(
+ paths: [entry.values.first],
+ source: "Environment variable `#{entry.keys.first}` filepath",
+ )
+ end
+
+ if !validated
+ raise 'Mixins environment variables failed validation'
+ end
+ end
+
+ def assemble_mixins(config:, env:, cmdline:)
+ assembly = []
+
+ # Build list of hashses to facilitate deduplication
+ cmdline.each {|mixin| assembly << {'command line' => mixin}}
+ assembly += env
+ config.each {|mixin| assembly << {'project configuration' => mixin}}
+
+ # Remove duplicates inline
+ # 1. Expand filepaths to absolute paths for correct deduplication (skip expanding simple mixin names)
+ # 2. Remove duplicates
+ assembly.uniq! do |entry|
+ # If entry is filepath, expand it, otherwise leave entry untouched (it's a mixin name only)
+ mixin = entry.values.first
+ @path_validator.filepath?( mixin ) ? File.expand_path( mixin ) : mixin
+ end
+
+ # Return the compacted list (in merge order)
+ return assembly
+ end
+
+ def merge(builtins:, config:, mixins:)
+ mixins.each do |mixin|
+ source = mixin.keys.first
+ filepath = mixin.values.first
+
+ _mixin = {} # Empty initial value
+
+ # Load mixin from filepath if it is a filepath
+ if @path_validator.filepath?( filepath )
+ _mixin = @yaml_wrapper.load( filepath )
+
+ # Log what filepath we used for this mixin
+ @loginator.log( " + Merging #{'(empty) ' if _mixin.nil?}#{source} mixin using #{filepath}", Verbosity::OBNOXIOUS )
+
+ # Reference mixin from built-in hash-based mixins
+ else
+ _mixin = builtins[filepath.to_sym()]
+
+ # Log built-in mixin we used
+ @loginator.log( " + Merging built-in mixin '#{filepath}' from #{source}", Verbosity::OBNOXIOUS )
+ end
+
+ # Hnadle an empty mixin (it's unlikely but logically coherent and a good safety check)
+ _mixin = {} if _mixin.nil?
+
+ # Sanitize the mixin config by removing any :mixins section (these should not end up in merges)
+ _mixin.delete(:mixins)
+
+ # Merge this bad boy
+ config.deep_merge( _mixin )
+ end
+
+ # Validate final configuration
+ msg = "Final configuration is empty"
+ raise msg if config.empty?
+ end
+
+end
diff --git a/bin/mixins.rb b/bin/mixins.rb
new file mode 100644
index 000000000..2c95a228e
--- /dev/null
+++ b/bin/mixins.rb
@@ -0,0 +1,5 @@
+
+BUILTIN_MIXINS = {
+ # Mixin name as symbol => mixin config hash
+ # :mixin => {}
+}
diff --git a/bin/objects.yml b/bin/objects.yml
new file mode 100644
index 000000000..cf63d15aa
--- /dev/null
+++ b/bin/objects.yml
@@ -0,0 +1,80 @@
+# =========================================================================
+# Ceedling - Test-Centered Build System for C
+# ThrowTheSwitch.org
+# Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+# SPDX-License-Identifier: MIT
+# =========================================================================
+
+#
+# Loaded from lib/
+# ----------------
+#
+
+file_wrapper:
+
+yaml_wrapper:
+
+config_walkinator:
+
+system_wrapper:
+
+verbosinator:
+
+loginator:
+ compose:
+ - verbosinator
+ - file_wrapper
+ - system_wrapper
+
+#
+# Loaded from bin/
+# ----------------
+#
+
+actions_wrapper:
+
+# Separation of logic from CLI user interface
+cli_handler:
+ compose:
+ - configinator
+ - projectinator
+ - cli_helper
+ - path_validator
+ - actions_wrapper
+ - loginator
+
+cli_helper:
+ compose:
+ - file_wrapper
+ - config_walkinator
+ - path_validator
+ - actions_wrapper
+ - loginator
+ - system_wrapper
+
+path_validator:
+ compose:
+ - file_wrapper
+ - loginator
+
+mixinator:
+ compose:
+ - path_validator
+ - yaml_wrapper
+ - loginator
+
+projectinator:
+ compose:
+ - file_wrapper
+ - path_validator
+ - yaml_wrapper
+ - loginator
+ - system_wrapper
+
+configinator:
+ compose:
+ - config_walkinator
+ - projectinator
+ - mixinator
+
+
diff --git a/bin/path_validator.rb b/bin/path_validator.rb
new file mode 100644
index 000000000..91dd793f5
--- /dev/null
+++ b/bin/path_validator.rb
@@ -0,0 +1,54 @@
+# =========================================================================
+# Ceedling - Test-Centered Build System for C
+# ThrowTheSwitch.org
+# Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+# SPDX-License-Identifier: MIT
+# =========================================================================
+
+class PathValidator
+
+ constructor :file_wrapper, :loginator
+
+ def validate(paths:, source:, type: :filepath)
+ validated = true
+
+ paths.each do |path|
+ # Error out on empty paths
+ if path.empty?
+ validated = false
+ @loginator.log( "#{source} contains an empty path", Verbosity::ERRORS )
+ next
+ end
+
+ # Error out if path is not a directory / does not exist
+ if (type == :directory) and !@file_wrapper.directory?( path )
+ validated = false
+ @loginator.log( "#{source} '#{path}' does not exist as a directory in the filesystem", Verbosity::ERRORS )
+ end
+
+ # Error out if filepath does not exist
+ if (type == :filepath) and !@file_wrapper.exist?( path )
+ validated = false
+ @loginator.log( "#{source} '#{path}' does not exist in the filesystem", Verbosity::ERRORS )
+ end
+ end
+
+ return validated
+ end
+
+ # Ensure any Windows backslashes are converted to Ruby path forward slashes
+ # Santization happens inline
+ def standardize_paths( *paths )
+ paths.each do |path|
+ next if path.nil? or path.empty?
+ path.gsub!( "\\", '/' )
+ end
+ end
+
+
+ def filepath?(str)
+ # If argument includes a file extension or a path separator, it's a filepath
+ return (!File.extname( str ).empty?) || (str.include?( File::SEPARATOR ))
+ end
+
+end
\ No newline at end of file
diff --git a/bin/projectinator.rb b/bin/projectinator.rb
new file mode 100644
index 000000000..29d0cca21
--- /dev/null
+++ b/bin/projectinator.rb
@@ -0,0 +1,236 @@
+# =========================================================================
+# Ceedling - Test-Centered Build System for C
+# ThrowTheSwitch.org
+# Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+# SPDX-License-Identifier: MIT
+# =========================================================================
+
+require 'ceedling/constants' # From Ceedling application
+
+class Projectinator
+
+ PROJECT_FILEPATH_ENV_VAR = 'CEEDLING_PROJECT_FILE'
+ DEFAULT_PROJECT_FILEPATH = './' + DEFAULT_PROJECT_FILENAME
+ DEFAULT_YAML_FILE_EXTENSION = '.yml'
+
+ constructor :file_wrapper, :path_validator, :yaml_wrapper, :loginator, :system_wrapper
+
+ # Discovers project file path and loads configuration.
+ # Precendence of attempts:
+ # 1. Explcit flepath from argument
+ # 2. Environment variable
+ # 3. Default filename in working directory
+ # Returns:
+ # - Absolute path of project file found and used
+ # - Config hash loaded from project file
+ def load(filepath:nil, env:{}, silent:false)
+ # Highest priority: command line argument
+ if filepath
+ config = load_filepath( filepath, 'from command line argument', silent )
+ return File.expand_path( filepath ), config
+
+ # Next priority: environment variable
+ elsif env[PROJECT_FILEPATH_ENV_VAR]
+ filepath = env[PROJECT_FILEPATH_ENV_VAR]
+ @path_validator.standardize_paths( filepath )
+ config = load_filepath(
+ filepath,
+ "from environment variable `#{PROJECT_FILEPATH_ENV_VAR}`",
+ silent
+ )
+ return File.expand_path( filepath ), config
+
+ # Final option: default filepath
+ elsif @file_wrapper.exist?( DEFAULT_PROJECT_FILEPATH )
+ filepath = DEFAULT_PROJECT_FILEPATH
+ config = load_filepath( filepath, "at default location", silent )
+ return File.expand_path( filepath ), config
+
+ # If no user provided filepath and the default filepath does not exist,
+ # we have a big problem
+ else
+ raise "No project filepath provided and default #{DEFAULT_PROJECT_FILEPATH} not found"
+ end
+
+ # We'll never get here but return nil/empty for completeness
+ return nil, {}
+ end
+
+
+ # Determine if project configuration is available.
+ # - Simplest, default case simply tries to load default project file location.
+ # - Otherwise, attempts to load a filepath, the default environment variable,
+ # or both can be specified.
+ def config_available?(filepath:nil, env:{}, silent:true)
+ available = true
+
+ begin
+ load(filepath:filepath, env:env, silent:silent)
+ rescue
+ available = false
+ end
+
+ return available
+ end
+
+
+ def lookup_yaml_extension(config:)
+ return DEFAULT_YAML_FILE_EXTENSION if config[:extension].nil?
+
+ return DEFAULT_YAML_FILE_EXTENSION if config[:extension][:yaml].nil?
+
+ return config[:extension][:yaml]
+ end
+
+
+ # Pick apart a :mixins projcet configuration section and return components
+ # Layout mirrors :plugins section
+ def extract_mixins(config:)
+ # Get mixins config hash
+ _mixins = config[:mixins]
+
+ # If no :mixins section, return:
+ # - Empty enabled list
+ # - Empty load paths
+ return [], [] if _mixins.nil?
+
+ # Build list of load paths
+ # Configured load paths are higher in search path ordering
+ load_paths = _mixins[:load_paths] || []
+
+ # Get list of mixins
+ enabled = _mixins[:enabled] || []
+ enabled = enabled.clone # Ensure it's a copy of configuration section
+
+ # Handle any inline Ruby string expansion
+ load_paths.each do |load_path|
+ load_path.replace( @system_wrapper.module_eval( load_path ) ) if (load_path =~ RUBY_STRING_REPLACEMENT_PATTERN)
+ end
+
+ enabled.each do |mixin|
+ mixin.replace( @system_wrapper.module_eval( mixin ) ) if (mixin =~ RUBY_STRING_REPLACEMENT_PATTERN)
+ end
+
+ # Remove the :mixins section of the configuration
+ config.delete( :mixins )
+
+ return enabled, load_paths
+ end
+
+
+ # Validate :load_paths from :mixins section in project configuration
+ def validate_mixin_load_paths(load_paths)
+ validated = @path_validator.validate(
+ paths: load_paths,
+ source: 'Config :mixins ↳ :load_paths =>',
+ type: :directory
+ )
+
+ if !validated
+ raise 'Project configuration file section :mixins failed validation'
+ end
+ end
+
+
+ # Validate mixins list
+ def validate_mixins(mixins:, load_paths:, builtins:, source:, yaml_extension:)
+ validated = true
+
+ mixins.each do |mixin|
+ # Validate mixin filepaths
+ if @path_validator.filepath?( mixin )
+ if !@file_wrapper.exist?( mixin )
+ @loginator.log( "Cannot find mixin at #{mixin}", Verbosity::ERRORS )
+ validated = false
+ end
+
+ # Otherwise, validate that mixin name can be found in load paths or builtins
+ else
+ found = false
+ load_paths.each do |path|
+ if @file_wrapper.exist?( File.join( path, mixin + yaml_extension ) )
+ found = true
+ break
+ end
+ end
+
+ builtins.keys.each {|key| found = true if (mixin == key.to_s)}
+
+ if !found
+ msg = "#{source} '#{mixin}' cannot be found in mixin load paths as '#{mixin + yaml_extension}' or among built-in mixins"
+ @loginator.log( msg, Verbosity::ERRORS )
+ validated = false
+ end
+ end
+ end
+
+ return validated
+ end
+
+
+ # Yield ordered list of filepaths or built-in mixin names
+ def lookup_mixins(mixins:, load_paths:, builtins:, yaml_extension:)
+ _mixins = []
+
+ # Already validated, so we know:
+ # 1. Any mixin filepaths exists
+ # 2. Built-in mixin names exist in the internal hash
+
+ # Fill filepaths array with filepaths or builtin names
+ mixins.each do |mixin|
+ # Handle explicit filepaths
+ if !@path_validator.filepath?( mixin )
+ _mixins << mixin
+ next # Success, move on
+ end
+
+ # Find name in load_paths (we already know it exists from previous validation)
+ load_paths.each do |path|
+ filepath = File.join( path, mixin + yaml_extension )
+ if @file_wrapper.exist?( filepath )
+ _mixins << filepath
+ next # Success, move on
+ end
+ end
+
+ # Finally, just add the unmodified name to the list
+ # It's a built-in mixin
+ _mixins << mixin
+ end
+
+ return _mixins
+ end
+
+ ### Private ###
+
+ private
+
+ def load_filepath(filepath, method, silent)
+ begin
+ # Load the filepath we settled on as our project configuration
+ config = @yaml_wrapper.load( filepath )
+
+ # A blank configuration file is technically an option (assuming mixins are merged)
+ # Redefine config as empty hash
+ config = {} if config.nil?
+
+ # Log what the heck we loaded
+ if !silent
+ msg = "Loaded #{'(empty) ' if config.empty?}project configuration #{method} using #{filepath}"
+ @loginator.log( msg, Verbosity::NORMAL, LogLabels::CONSTRUCT )
+ end
+
+ return config
+ rescue Errno::ENOENT
+ # Handle special case of user-provided blank filepath
+ filepath = filepath.empty?() ? '' : filepath
+ raise "Could not find project filepath #{filepath} #{method}"
+
+ rescue StandardError => e
+ # Catch-all error handling
+ raise "Error loading project filepath #{filepath} #{method}: #{e.message}"
+ end
+
+ end
+
+end
\ No newline at end of file
diff --git a/ceedling.gemspec b/ceedling.gemspec
index 2c1f6b6d8..06549a05f 100644
--- a/ceedling.gemspec
+++ b/ceedling.gemspec
@@ -1,4 +1,11 @@
# -*- encoding: utf-8 -*-
+# =========================================================================
+# Ceedling - Test-Centered Build System for C
+# ThrowTheSwitch.org
+# Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+# SPDX-License-Identifier: MIT
+# =========================================================================
+
$LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), 'lib'))
require "ceedling/version"
require 'date'
@@ -10,11 +17,13 @@ Gem::Specification.new do |s|
s.authors = ["Mark VanderVoord", "Michael Karlesky", "Greg Williams"]
s.email = ["mark@vandervoord.net", "michael@karlesky.net", "barney.williams@gmail.com"]
s.homepage = "http://throwtheswitch.org/ceedling"
- s.summary = "Ceedling is a build automation tool for C unit test suites that packages up Unity, CMock, and Rake-based build management functionality"
+ s.summary = "Ceedling is a build automation tool for C unit tests and releases. It's a member of the ThrowTheSwitch.org family of tools. It's built upon Unity and CMock."
s.description = <<-DESC
Ceedling is a build automation tool that helps you create and run C unit test suites.
-Ceedling provides two core functions: [1] It packages up several tools including the C unit test framework Unity, the Ruby-based mock generation tool CMock, and a C exception library CException. [2] It extends Rake with functionality specific to generating, building, and executing C test suites.
+Ceedling provides two core functions:
+ [1] It packages up several tools including the C unit test framework Unity, the mock generation tool CMock, and other features.
+ [2] It simplifies tool configuration for embedded or native C toolchains and automates the running and reporting of tests.
Ceedling projects are created with a YAML configuration file. A variety of conventions within the tool simplify generating mocks from C files and assembling suites of unit test functions.
DESC
@@ -28,12 +37,13 @@ Ceedling projects are created with a YAML configuration file. A variety of conve
"source_code_uri" => "https://github.com/ThrowTheSwitch/Ceedling"
}
- s.required_ruby_version = ">= 2.7.0"
+ s.required_ruby_version = ">= 3.0.0"
s.add_dependency "thor", ">= 0.14"
s.add_dependency "rake", ">= 12", "< 14"
s.add_dependency "deep_merge", "~> 1.2"
s.add_dependency "constructor", "~> 2"
+ s.add_dependency "unicode-display_width", "~> 2.5"
# Files needed from submodules
s.files = []
@@ -45,9 +55,9 @@ Ceedling projects are created with a YAML configuration file. A variety of conve
s.files += Dir['vendor/unity/auto/**/*.rb']
s.files += Dir['vendor/unity/src/**/*.[ch]']
- s.files += Dir['**/*']
- s.test_files = Dir['test/**/*', 'spec/**/*', 'features/**/*']
- s.executables = Dir['bin/**/*'].map{|f| File.basename(f)}
+ s.files += Dir['**/*']
+ s.test_files = Dir['test/**/*', 'spec/**/*', 'features/**/*']
+ s.executables = ['ceedling'] # bin/ceedling
s.require_paths = ["lib", "vendor/cmock/lib"]
end
diff --git a/config/test_environment.rb b/config/test_environment.rb
index 377aa1935..affaacce3 100644
--- a/config/test_environment.rb
+++ b/config/test_environment.rb
@@ -1,3 +1,9 @@
+# =========================================================================
+# Ceedling - Test-Centered Build System for C
+# ThrowTheSwitch.org
+# Copyright (c) 2010-24 Mike Karlesky, Mark VanderVoord, & Greg Williams
+# SPDX-License-Identifier: MIT
+# =========================================================================
# Setup our load path:
[
diff --git a/docs/BreakingChanges.md b/docs/BreakingChanges.md
new file mode 100644
index 000000000..6c6646d7a
--- /dev/null
+++ b/docs/BreakingChanges.md
@@ -0,0 +1,203 @@
+# 🌱 Ceedling Breaking Changes
+
+These breaking changes are complemented by two other documents:
+
+1. 🔊 **[Release Notes](ReleaseNotes.md)** for announcements, education, acknowledgements, and known issues.
+1. 🪵 **[Changelog](Changelog.md)** for a structured list of additions, fixes, changes, and removals.
+
+---
+
+# [1.0.0 pre-release] — 2024-04-02
+
+## Explicit `:paths` ↳ `:include` entries in the project file
+
+The `:paths` ↳ `:include` entries in the project file must now be explicit and complete.
+
+Eaerlier versions of Ceedling were rather accomodating when assembling the search paths for header files. The full list of directories was pulled from multiple `:paths` entries with de-duplication. If you had header files in your `:source` directories but did not explicitly list those directories in your `:include` paths, Ceedling would helpfully figure it out and use all the paths.
+
+This behavior is no more. Why? For two interrelated reasons.
+
+1. For large or complex projects, expansive header file search path lists can exceed command line maximum lengths on some platforms. An enforced, tailored set of search paths helps prevent this problem.
+1. In order to support the desired behavior of `TEST_INCLUDE_PATH()` a concice set of “base” header file search paths is necessary. `:paths` ↳ `:include` is that base list.
+
+Using 0.32 Ceedling with older project files can lead to errors when generating mocks or compiler errors on finding header files. Add all relevant header file search paths to the `:paths` ↳ `:include` project file entry to fix this problem.
+
+## Format change for `:defines` in the project file
+
+To better support per-test-executable configurations, the format of `:defines` has changed. See the [official documentation](CeedlingPacket.md) for specifics.
+
+In brief:
+
+1. A more logically named hierarchy differentiates `#define`s for test preprocessing, test compilation, and release compilation.
+1. Previously, compilation symbols could be specified for a specific C file by name, but these symbols were only defined when compiling that specific file. Further, this matching was only against a file’s full name. Now, pattern matching is also an option.
+1. Filename matching for test compilation symbols happens against _only test file names_. More importantly, the configured symbols are applied in compilation of each C file that comprises a test executable. Each test executable is treated as a mini-project.
+
+Symbols specified for release builds are applied to all files in the release build.
+
+## Format change for `:flags` in the project file
+
+To better support per-test-executable configurations, the format and function of `:flags` has changed somewhat. See the [official documentation](CeedlingPacket.md) for specifics.
+
+In brief:
+
+1. The format of the `:flags` configuration section is largely the same as in previous versions of Ceedling. However, the behavior of the matching rules is slightly different with more matching options.
+1. Within the `:flags` ↳ `:test` context, all matching of file names is now limited to *_test files_*. For any test file name that matches, the specified flags are added to the named build step for _all files that comprise that test executable_. Previously, matching was against individual files (source, test, whatever), and flags were applied to only operations on that single file. Now, all files part of a test executable build get the same treatment as a mini-project.
+
+Flags specified for release builds are applied to all files in the release build.
+
+## `TEST_FILE()` ➡️ `TEST_SOURCE_FILE()`
+
+The previously undocumented `TEST_FILE()` build directive macro (#796) available within test files has been renamed and is now officially documented. See earlier section on this.
+
+## Quoted executables in tool definitions
+
+While unusual, some executables have names with spaces. This is more common on Windows than Unix derivatives, particularly with proprietary compiler toolchains.
+
+Originally, Ceedling would automatically add quotes around an executable containing a space when it built a full command line before passing it to a command shell.
+
+This automagical help can break tools in certain contexts. For example, script-based executables in many Linux shells do not work (e.g. `"python path/script.py" --arg`).
+
+Automatic quoting has been removed. If you need a quoted executable, simply explicitly include quotes in the appropriate string for your executable (this can occur in multiple locations throughout a Ceedling project). An example of a YAML tool defition follows:
+
+```yaml
+:tools:
+ :funky_compiler:
+ :executable: \"Code Cranker\"
+```
+
+## Build output directory structure changes
+
+### Test builds
+
+Each test is now treated as its own mini-project. Differentiating components of the same name that are a part of multiple test executables required further subdirectories in the build directory structure. Generated mocks, compiled object files, linked executables, and preprocessed output all end up one directory deeper than in previous versions of Ceedling. In each case, these files are found inside a subdirectory named for their containing test.
+
+### Release builds
+
+Release build object files were previously segregated by their source. The release build output directory had subdirectories `c/` and `asm/`. These subdirectories are no longer in use.
+
+## Changes to global constants & accessors
+
+Some global constant “collections” that were previously key elements of Ceedling have changed or gone away as the build pipeline is now able to process a configuration for each individual test executable in favor of for the entire suite.
+
+Similarly, various global constant project file accessors have changed, specifically the values within the configuration file they point to as various configuration sections have changed format (examples above).
+
+See the [official documentation](CeedlingPacket.md) on global constants & accessors for updated lists and information.
+
+## `raw_output_report` plugin
+
+This plugin (renamed -- see next section) no longer generates empty log files and no longer generates log files with _test_ and _pass_ in their filenames. Log files are now simply named `.raw.log`.
+
+## Consolidation of test report generation plugins ➡️ `report_tests_log_factory`
+
+The individual `json_tests_report`, `xml_tests_report`, and `junit_tests_report` plugins are superseded by a single plugin `report_tests_log_factory` able to generate each or all of the previous test reports as well as an HTML report and user-defined tests reports. The new plugin requires a small amount of extra configuration the previous individual plugins did not. See the [`report_tests_log_factory` documentation](../plugins/report_tests_log_factory).
+
+In addition, all references and naming connected to the previous `xml_tests_report` plugin have been updated to refer to _CppUnit_ rather than generic _XML_ as this is the actual format of the report that is processed.
+
+## Built-in Plugin Name Changes
+
+The following plugin names must be updated in the `:plugins` ↳ `:enabled` list of your Ceedling project file.
+
+This renaming was primarily enacted to more clearly organize and relate reporting-oriented plugins. Secondarily, some names were changed simply for greater clarity.
+
+Some test report generation plugins were not simply renamed but superseded by a new plugin (see preceding section).
+
+- `fake_function_framework` ➡️ `fff`
+- `compile_commands_json` ➡️ `compile_commands_json_db`
+- `json_tests_report`, `xml_tests_report` & `junit_tests_report` ➡️ `report_tests_log_factory`
+- `raw_output_report` ➡️ `report_tests_raw_output_log`
+- `stdout_gtestlike_tests_report` ➡️ `report_tests_gtestlike_stdout`
+- `stdout_ide_tests_report` ➡️ `report_tests_ide_stdout`
+- `stdout_pretty_tests_report` ➡️ `report_tests_pretty_stdout`
+- `stdout_teamcity_tests_report` ➡️ `report_tests_teamcity_stdout`
+- `warnings_report` ➡️ `report_build_warnings_log`
+- `test_suite_reporter` ➡️ `report_tests_log_factory`
+
+## `gcov` plugin coverage report generation name and behavior changes
+
+The `gcov` plugin and its [documentation](../plugins/gcov) has been significantly revised. See [release notes](ReleaseNotes.md) for all the details.
+
+The report generation task `utils:gcov` has been renamed and its behavior has been altered.
+
+Coverage reports are now generated automatically unless the manual report generation task is enabled with a configuration option (the manual report generation option disables the automatc option). See below. If automatic report generation is disabled, the task `report:gcov` becomes available to trigger report generation (a `gcov:` task must be executed before `report:gcov` just as was the case with `utils:gcov`).
+
+```yaml
+:gcov:
+ :report_task: TRUE
+```
+
+## Exit code handling (a.k.a. `:graceful_fail`)
+
+Be default Ceedling terminates with an exit code of `1` when a build succeeds but unit tests fail.
+
+A previously undocumented project configuration option `:graceful_fail` could force a Ceedling exit code of `0` upon test failures.
+
+This configuration option has moved (and is now [documented](CeedlingPacket.md)).
+
+Previously:
+```yaml
+:graceful_fail: TRUE
+```
+
+Now:
+```yaml
+:test_build:
+ :graceful_fail: TRUE
+```
+
+## Project file environment variable name change `CEEDLING_MAIN_PROJECT_FILE` ➡️ `CEEDLING_PROJECT_FILE`
+
+Options and support for loading a project configuration have expanded significantly, mostly notably with the addition of Mixins.
+
+The environment variable option for pointing Ceedling to a project file other than _project.yml_ in your working directory has been renamed `CEEDLING_MAIN_PROJECT_FILE` ➡️ `CEEDLING_PROJECT_FILE`.
+
+In addition, a previously undocumented feature for merging a second configuration via environment variable `CEEDLING_USER_PROJECT_FILE` has been removed. This feature has been superseded by the new Mixins functionality.
+
+Thorough documentation on Mixins and the new options for loading a project configuration can be found in _[CeedlingPacket](CeedlingPacket.md))_.
+
+
+# Subprojects Plugin Replaced
+
+The `subprojects` plugin has been completely replaced by the more-powerful `dependencies` plugin. To retain your previous functionality, you need to do a little reorganizing in your `project.yml` file. Obviously the `:subprojects` section is now called `:dependencies`. The collection of `:paths` is now `:deps`. We'll have a little more organizing to do once we're inside that section as well. Your `:source` is now organized in `:paths` ↳ `:source` and, since you're not fetching it form another project, the `:fetch` ↳ `:method` should be set to `:none`. The `:build_root` now becomes `:paths` ↳ `:build`. You'll also need to specify the name of the library produced under `:artifacts` ↳ `:static_libraries`.
+
+For example:
+
+```
+:subprojects:
+ :paths:
+ - :name: libprojectA
+ :source:
+ - ./subprojectA/
+ :include:
+ - ./subprojectA/inc
+ :build_root: ./subprojectA/build/dir
+ :defines:
+ - DEFINE_JUST_FOR_THIS_FILE
+ - AND_ANOTHER
+```
+
+The above subproject definition will now look like the following:
+
+```
+:dependencies:
+ :deps:
+ - :name: Project A
+ :paths:
+ :fetch: ./subprojectA/
+ :source: ./subprojectA/
+ :build: ./subprojectA/build/dir
+ :artifact: ./subprojectA/build/dir
+ :fetch:
+ :method: :none
+ :environment: []
+ :build:
+ - :build_lib
+ :artifacts:
+ :static_libraries:
+ - libprojectA.a
+ :dynamic_libraries: []
+ :includes:
+ - ../subprojectA/subprojectA.h
+ :defines:
+ - DEFINE_JUST_FOR_THIS_FILE
+ - AND_ANOTHER
+```
\ No newline at end of file
diff --git a/docs/CODE_OF_CONDUCT.md b/docs/CODE_OF_CONDUCT.md
new file mode 100644
index 000000000..84e34806a
--- /dev/null
+++ b/docs/CODE_OF_CONDUCT.md
@@ -0,0 +1,138 @@
+
+# ThrowTheSwitch.org Code of Conduct
+
+Thank you for participating in a ThrowTheSwitch.org community project! We want
+this to continue to be a warm and inviting place for everyone to share ideas
+and get help. To accomplish this goal, we've developed this Code of Conduct.
+
+## Our Pledge
+
+We as members, contributors, and leaders pledge to make participation in our
+community a harassment-free experience for everyone, regardless of age, body
+size, visible or invisible disability, ethnicity, sex characteristics, gender
+identity and expression, level of experience, education, socio-economic status,
+nationality, personal appearance, race, caste, color, religion, or sexual
+identity and orientation.
+
+We pledge to act and interact in ways that contribute to an open, welcoming,
+diverse, inclusive, and healthy community.
+
+## Our Standards
+
+Examples of behavior that contributes to a positive environment for our
+community include:
+
+* Demonstrating empathy and kindness toward other people
+* Being respectful of differing opinions, viewpoints, and experiences
+* Giving and gracefully accepting constructive feedback
+* Accepting responsibility and apologizing to those affected by our mistakes,
+ and learning from the experience
+* Focusing on what is best not just for us as individuals, but for the overall
+ community
+
+Examples of unacceptable behavior include:
+
+* The use of sexualized language or imagery, and sexual attention or advances of
+ any kind
+* Trolling, insulting or derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or email address,
+ without their explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Enforcement Responsibilities
+
+Community leaders are responsible for clarifying and enforcing our standards of
+acceptable behavior and will take appropriate and fair corrective action in
+response to any behavior that they deem inappropriate, threatening, offensive,
+or harmful.
+
+Community leaders have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct, and will communicate reasons for moderation
+decisions when appropriate.
+
+## Scope
+
+This Code of Conduct applies within all community spaces, and also applies when
+an individual is officially representing the community in public spaces.
+Examples of representing our community include using an official email address,
+posting via an official social media account, or acting as an appointed
+representative at an online or offline event.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported to the community leaders responsible for enforcement at
+hello@thingamabyte.com.
+
+All complaints will be reviewed and investigated promptly and fairly.
+
+All community leaders are obligated to respect the privacy and security of the
+reporter of any incident.
+
+## Enforcement Guidelines
+
+Community leaders will follow these Community Impact Guidelines in determining
+the consequences for any action they deem in violation of this Code of Conduct:
+
+### 1. Correction
+
+**Community Impact**: Use of inappropriate language or other behavior deemed
+unprofessional or unwelcome in the community.
+
+**Consequence**: A private, written warning from community leaders, providing
+clarity around the nature of the violation and an explanation of why the
+behavior was inappropriate. A public apology may be requested.
+
+### 2. Warning
+
+**Community Impact**: A violation through a single incident or series of
+actions.
+
+**Consequence**: A warning with consequences for continued behavior. No
+interaction with the people involved, including unsolicited interaction with
+those enforcing the Code of Conduct, for a specified period of time. This
+includes avoiding interactions in community spaces as well as external channels
+like social media. Violating these terms may lead to a temporary or permanent
+ban.
+
+### 3. Temporary Ban
+
+**Community Impact**: A serious violation of community standards, including
+sustained inappropriate behavior.
+
+**Consequence**: A temporary ban from any sort of interaction or public
+communication with the community for a specified period of time. No public or
+private interaction with the people involved, including unsolicited interaction
+with those enforcing the Code of Conduct, is allowed during this period.
+Violating these terms may lead to a permanent ban.
+
+### 4. Permanent Ban
+
+**Community Impact**: Demonstrating a pattern of violation of community
+standards, including sustained inappropriate behavior, harassment of an
+individual, or aggression toward or disparagement of classes of individuals.
+
+**Consequence**: A permanent ban from any sort of public interaction within the
+community.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 2.1, available at
+[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
+
+Community Impact Guidelines were inspired by
+[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
+
+For answers to common questions about this code of conduct, see the FAQ at
+[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
+[https://www.contributor-covenant.org/translations][translations].
+
+[homepage]: https://www.contributor-covenant.org
+[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
+[Mozilla CoC]: https://github.com/mozilla/diversity
+[FAQ]: https://www.contributor-covenant.org/faq
+[translations]: https://www.contributor-covenant.org/translations
diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md
new file mode 100644
index 000000000..b233a039b
--- /dev/null
+++ b/docs/CONTRIBUTING.md
@@ -0,0 +1,250 @@
+# Contributing to a ThrowTheSwitch.org Project
+
+👍🎉 _First off, thanks for taking the time to contribute!_ 🎉👍
+
+The following is a set of guidelines for contributing to any of ThrowTheSwitch.org's projects or the website itself, hosted at throwtheswitch.org or ThrowTheSwitch's organization on GitHub. These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request.
+
+### Table Of Contents
+
+- [Code of Conduct](#book-code-of-conduct)
+- [Asking Questions](#bulb-asking-questions)
+- [Opening an Issue](#inbox_tray-opening-an-issue)
+- [Feature Requests](#love_letter-feature-requests)
+- [Triaging Issues](#mag-triaging-issues)
+- [Submitting Pull Requests](#repeat-submitting-pull-requests)
+- [Writing Commit Messages](#memo-writing-commit-messages)
+- [Code Review](#white_check_mark-code-review)
+- [Coding Style](#nail_care-coding-style)
+- [Certificate of Origin](#medal_sports-certificate-of-origin)
+- [Credits](#pray-credits)
+
+## :book: Code of Conduct
+
+Please review our [Code of Conduct](CODE_OF_CONDUCT.md). It is in effect at all times. We expect it to be honored by everyone who contributes to this project. Be a Good Human!
+
+## :bulb: Asking Questions
+
+> **Note:** Please don't file an issue to ask a question. We have an official forum where the community chimes in with helpful advice if you have questions.
+
+* [ThrowTheSwitch Forums](https://throwtheswitch.org/forums)
+
+### What should I know before I get started?
+
+ThrowTheSwitch hosts a number of open source projects — Ceedling is the entrypoint for many users. Ceedling is actually built upon the foundation of Unity Test (a flexible C testing framework) and CMock (a mocking tool for C) and it coordinates many other open source and proprietary tools. Please do your best to focus your ideas and questions at the correct tool. We'll do our best to help you find your way, but there will be times where we'll have to direct your attention to another subtool.
+
+Here are some of the main projects hosted by ThrowTheSwitch.org:
+
+ - [Ceedling](https://www.github.com/throwtheswitch/ceedling) -- Build coordinator for testing C applications, especially embedded C (and optionally your release build too!)
+ - [CMock](https://www.github.com/throwtheswitch/cmock) -- Mocking tool for automatically creating stubs, mocks, and skeletons in C
+ - [Unity](https://www.github.com/throwtheswitch/unity) -- Unit Testing framework for C, specially embedded C.
+ - [MadScienceLabDocker](https://www.github.com/throwtheswitch/madsciencelabdocker) -- Docker image giving you a shortcut to getting running with Ceedling
+ - [CException](https://www.github.com/throwtheswitch/cexception) -- An exception framework for using simple exceptions in C.
+
+There are many more, but this list should be a good starting point.
+
+## :inbox_tray: Opening an Issue
+
+Before [creating an issue](https://help.github.com/en/github/managing-your-work-on-github/creating-an-issue), check if you are using the latest version of the project. If you are not up-to-date, see if updating fixes your issue first.
+
+### :beetle: Bug Reports and Other Issues
+
+A great way to contribute to the project is to send a detailed issue when you encounter a problem. We always appreciate a well-written, thorough bug report. :v:
+
+In short, since you are most likely a developer, **provide a ticket that you would like to receive**.
+
+- **Review the documentation** before opening a new issue.
+
+- **Do not open a duplicate issue!** Search through existing issues to see if your issue has previously been reported. If your issue exists, comment with any additional information you have. You may simply note "I have this problem too", which helps prioritize the most common problems and requests.
+
+- **Prefer using [reactions](https://github.blog/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/)**, not comments, if you simply want to "+1" an existing issue.
+
+- **Fully complete the provided issue template.** The bug report template requests all the information we need to quickly and efficiently address your issue. Be clear, concise, and descriptive. Provide as much information as you can, including steps to reproduce, stack traces, compiler errors, library versions, OS versions, and screenshots (if applicable).
+
+- **Use [GitHub-flavored Markdown](https://help.github.com/en/github/writing-on-github/basic-writing-and-formatting-syntax).** Especially put code blocks and console outputs in backticks (```). This improves readability.
+
+## :seedling: Feature Requests
+
+Feature requests are welcome! We don't have all the answers and we truly love the collaborative experience of building software together! That being said, we cannot guarantee your request will be accepted. We want to avoid [feature creep](https://en.wikipedia.org/wiki/Feature_creep). Your idea may be great, but also out-of-scope for the project. If accepted, we'll do our best to tackle it in a timely manner, but cannot make any commitments regarding the timeline for implementation and release. However, you are welcome to submit a pull request to help!
+
+- **Please don't open a duplicate feature request.** Search for existing feature requests first. If you find your feature (or one very similar) previously requested, comment on that issue.
+
+- **Fully complete the provided issue template.** The feature request template asks for all necessary information for us to begin a productive conversation.
+
+- Be precise about the proposed outcome of the feature and how it relates to existing features. Include implementation details if possible.
+
+## :mag: Triaging Issues
+
+You can triage issues which may include reproducing bug reports or asking for additional information, such as version numbers or reproduction instructions. Any help you can provide to quickly resolve an issue is very much appreciated!
+
+## :repeat: Submitting Pull Requests
+
+We **love** pull requests! Before [forking the repo](https://help.github.com/en/github/getting-started-with-github/fork-a-repo) and [creating a pull request](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/proposing-changes-to-your-work-with-pull-requests) for non-trivial changes, it is usually best to first open an issue to discuss the changes, or discuss your intended approach for solving the problem in the comments for an existing issue.
+
+For most contributions, after your first pull request is accepted and merged, you will be [invited to the project](https://help.github.com/en/github/setting-up-and-managing-your-github-user-account/inviting-collaborators-to-a-personal-repository) and given **push access**. :tada:
+
+*Note: All contributions will be licensed under the project's license.*
+
+- **Smaller is better.** Submit **one** pull request per bug fix or feature. A pull request should contain isolated changes pertaining to a single bug fix or feature implementation. **Do not** refactor or reformat code that is unrelated to your change. It is better to **submit many small pull requests** rather than a single large one. Enormous pull requests will take enormous amounts of time to review, or may be rejected altogether.
+
+- **Coordinate bigger changes.** For large and non-trivial changes, open an issue to discuss a strategy with the maintainers. Otherwise, you risk doing a lot of work for nothing!
+
+- **Prioritize understanding over cleverness.** Write code clearly and concisely. Remember that source code usually gets written once and read often. Ensure the code is clear to the reader. The purpose and logic should be obvious to a reasonably skilled developer, otherwise you should add a comment that explains it.
+
+- **Follow existing coding style and conventions.** Keep your code consistent with the style, formatting, and conventions in the rest of the code base. When possible, these will be enforced with a linter. Consistency makes it easier to review and modify in the future.
+
+- **Include test coverage.** Add unit tests when possible. Follow existing patterns for implementing tests.
+
+- **Update the example project** if one exists to exercise any new functionality you have added.
+
+- **Add documentation.** Document your changes with code doc comments or in existing guides.
+
+- **Update the CHANGELOG** for all enhancements and bug fixes. Include the corresponding issue number if one exists, and your GitHub username. (example: "- Fixed crash in profile view. #123 @jessesquires")
+
+- **Use the repo's default branch.** Branch from and [submit your pull request](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request-from-a-fork) to the repo's default branch. Usually this is `main`, but it could be `dev`, `develop`, or `master`.
+
+- **[Resolve any merge conflicts](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/resolving-a-merge-conflict-on-github)** that occur.
+
+- **Promptly address any CI failures**. If your pull request fails to build or pass tests, please push another commit to fix it.
+
+- When writing comments, use properly constructed sentences, including punctuation.
+
+- Use spaces, not tabs.
+
+## :memo: Writing Commit Messages
+
+Please [write a great commit message](https://chris.beams.io/posts/git-commit/).
+
+1. Separate subject from body with a blank line
+1. Limit the subject line to 50 characters
+1. Capitalize the subject line
+1. Do not end the subject line with a period
+1. Wrap the body at _about_ 72 characters
+1. Use the body to explain **why**, *not what and how* (the code shows that!)
+1. If applicable, prefix the title with the relevant component name or emoji (see below. examples: "[Docs] Fix typo", "[Profile] Fix missing avatar")
+
+```
+:palm_tree: Summary of Amazing Feature Here
+
+Add a more detailed explanation here, if necessary. Possibly give
+some background about the issue being fixed, etc. The body of the
+commit message can be several paragraphs. Further paragraphs come
+after blank lines and please do proper word-wrap.
+
+Wrap it to about 72 characters or so. In some contexts,
+the first line is treated as the subject of the commit and the
+rest of the text as the body. The blank line separating the summary
+from the body is critical (unless you omit the body entirely);
+various tools like `log`, `shortlog` and `rebase` can get confused
+if you run the two together.
+
+Explain the problem that this commit is solving. Focus on why you
+are making this change as opposed to how or what. The code explains
+how or what. Reviewers and your future self can read the patch,
+but might not understand why a particular solution was implemented.
+Are there side effects or other unintuitive consequences of this
+change? Here's the place to explain them.
+
+ - Bullet points are awesome, too
+
+ - A hyphen should be used for the bullet, preceded
+ by a single space, with blank lines in between
+
+Note the fixed or relevant GitHub issues at the end:
+
+Resolves: #123
+See also: #456, #789
+```
+
+## :white_check_mark: Pull Request Checklist
+
+Not all Pull Requests require these things, but here's a great list of things to check to see if it makes sense for your situation:
+
+ - [ ] Are the changes complete?
+ - [ ] Are there tests for the new functionality?
+ - [ ] Are the changes passing the style checks?
+ - [ ] Is there documentation for the new functionality?
+ - [ ] Has the change been added to `Changelog.md`?
+ - [ ] Has the change been added to `ReleaseNotes.md`?
+ - [ ] Have new config options been added as defaults to the `project.yml` files?
+
+## :heart: Who Loves Emoji?
+
+Commit comments, Issues, Feature Requests... they can all use a little sprucing up, right? Consider using the following emoji for a mix of function and :sparkles: dazzle! We encourage loosely following the conventions of [gitmoji](https://gitmoji.dev) for contributing to Ceedling.
+
+ - actions
+ - :seedling: `:seedling:` (or other plants) when growing new features. Choose your fav! :cactus: :herb: :evergreen_tree: :palm_tree: :deciduous_tree: :blossom:
+ - :art: `:art:` when improving the format/structure of the code
+ - :racehorse: `:racehorse:` when improving performance
+ - :non-potable_water: `:non-potable_water:` when plugging memory leaks
+ - :memo: `:memo:` when writing docs
+ - :bug: `:bug:` (or other insects) when fixing a bug. Maybe :beetle: :ant: or :honeybee: ?
+ - :fire: `:fire:` when removing code or files
+ - :green_heart: `:green_heart:` when fixing the CI build
+ - :white_check_mark: `:white_check_mark:` when adding tests
+ - :lock: `:lock:` when dealing with security
+ - :arrow_up: `:arrow_up:` when upgrading dependencies
+ - :arrow_down: `:arrow_down:` when downgrading dependencies
+ - :shirt: `:shirt:` when removing linter warnings
+
+ - platforms
+ - :penguin: `:penguin:` when fixing something on Linux
+ - :apple: `:apple:` when fixing something on macOS
+ - :checkered_flag: `:checkered_flag:` when fixing something on Windows
+
+## :white_check_mark: Code Review
+
+- **Review the code, not the author.** Look for and suggest improvements without disparaging or insulting the author. Provide actionable feedback and explain your reasoning.
+
+- **You are not your code.** When your code is critiqued, questioned, or constructively criticized, remember that you are not your code. Do not take code review personally.
+
+- **Always do your best.** No one writes bugs on purpose. Do your best, and learn from your mistakes.
+
+- Kindly note any violations to the guidelines specified in this document.
+
+## :violin: Coding Style
+
+Consistency is the most important. Following the existing style, formatting, and naming conventions of the file you are modifying and of the overall project. Failure to do so will result in a prolonged review process that has to focus on updating the superficial aspects of your code, rather than improving its functionality and performance.
+
+For example, if all private properties are prefixed with an underscore `_`, then new ones you add should be prefixed in the same way. Or, if methods are named using camelcase, like `thisIsMyNewMethod`, then do not diverge from that by writing `this_is_my_new_method`. You get the idea. If in doubt, please ask or search the codebase for something similar.
+
+When possible, style and format will be enforced with a linter.
+
+### C Styleguide
+
+C code is linted with [AStyle](https://astyle.sourceforge.net/).
+
+### Ruby Styleguide
+
+Ruby code is linted with [Rubocop](https://github.com/rubocop/rubocop)
+
+## :medal_sports: Certificate of Origin
+
+*Developer's Certificate of Origin 1.1*
+
+By making a contribution to this project, I certify that:
+
+> 1. The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or
+> 1. The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or
+> 1. The contribution was provided directly to me by some other person who certified (1), (2) or (3) and I have not modified it.
+> 1. I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved.
+
+## [No Brown M&M's](https://en.wikipedia.org/wiki/Van_Halen#Contract_riders)
+
+If you are reading this, bravo dear user and (hopefully) contributor for making it this far! You are awesome. :100:
+
+To confirm that you have read this guide and are following it as best as possible, **include this emoji at the top** of your issue or pull request: :pineapple: `:pineapple:`
+
+## :pray: Credits
+
+Written by [@jessesquires](https://github.com/jessesquires). Lovingly adapted to ThrowTheSwitch.org by [@mvandervoord](https://github.com/mvandervoord).
+
+**Please feel free to adopt this guide in your own projects. Fork it wholesale or remix it for your needs.**
+
+*Many of the ideas and prose for the statements in this document were based on or inspired by work from the following communities:*
+
+- [Alamofire](https://github.com/Alamofire/Alamofire/blob/master/CONTRIBUTING.md)
+- [CocoaPods](https://github.com/CocoaPods/CocoaPods/blob/master/CONTRIBUTING.md)
+- [Docker](https://github.com/moby/moby/blob/master/CONTRIBUTING.md)
+- [Linux](https://elinux.org/Developer_Certificate_Of_Origin)
+
+*We commend them for their efforts to facilitate collaboration in their projects.*
diff --git a/docs/Ceedling Powerful Plugins.pdf b/docs/Ceedling Powerful Plugins.pdf
deleted file mode 100755
index 4596b6ab1..000000000
Binary files a/docs/Ceedling Powerful Plugins.pdf and /dev/null differ
diff --git a/docs/CeedlingPacket.md b/docs/CeedlingPacket.md
index 97d59b09b..8bb132e24 100644
--- a/docs/CeedlingPacket.md
+++ b/docs/CeedlingPacket.md
@@ -1,26 +1,202 @@
-[All code is copyright © 2010-2021 Ceedling Project
-by Mike Karlesky, Mark VanderVoord, and Greg Williams.
-
-This Documentation Is Released Under a
-Creative Commons 3.0 Attribution Share-Alike License]
-
-What the What?
-
-Assembling build environments for C projects - especially with
-automated unit tests - is a pain. Whether it's Make or Rake or Premake
-or what-have-you, set up with an all-purpose build environment
-tool is tedious and requires considerable glue code to pull together
-the necessary tools and libraries. Ceedling allows you to generate
-an entire test and build environment for a C project from a single
-YAML configuration file. Ceedling is written in Ruby and works
-with the Rake build tool plus other goodness like Unity and CMock —
-the unit testing and mocking frameworks for C. Ceedling and
-its complementary tools can support the tiniest of embedded
-processors, the beefiest 64 bit power houses available, and
+
+# Ceedling
+
+All code is copyright © 2010-2023 Ceedling Project
+by Michael Karlesky, Mark VanderVoord, and Greg Williams.
+
+This Documentation is released under a
+[Creative Commons 4.0 Attribution Share-Alike Deed][CC4SA].
+
+[CC4SA]: https://creativecommons.org/licenses/by-sa/4.0/deed.en
+
+# Quick Start
+
+Ceedling is a fancypants build system that greatly simplifies building
+C projects. While it can certainly build release targets, it absolutely
+shines at running unit test suites.
+
+## Steps
+
+1. Install Ceedling
+1. Create a project
+ 1. Use Ceedling to generate an example project, or
+ 1. Add a Ceedling project file to the root of an existing project, or
+ 1. Create a project from scratch:
+ 1. Create a project directory
+ 1. Add source code and optionally test code however you'd like it organized
+ 1. Create a Ceedling project file in the root of your project directory
+1. Run Ceedling tasks from the working directory of your project
+
+Ceedling requires a command line C toolchain be available in your path. It's
+flexible enough to work with most anything on any platform. By default, Ceedling
+is ready to work with [GCC] out of the box (we recommend the [MinGW] project
+on Windows).
+
+A common build strategy with tooling other than GCC is to use your target
+toolchain for release builds (with or without Ceedling) but rely on Ceedling +
+GCC for test builds (more on all this [here][packet-section-2]).
+
+[GCC]: https://gcc.gnu.org
+
+## Ceedling Tasks
+
+Once you have Ceedling installed and a project file, Ceedling tasks go like this:
+
+* `ceedling test:all`, or
+* `ceedling release`, or, if you fancy,
+* `ceedling --verbosity=obnoxious clobber test:all gcov:all release`
+
+## Quick Start Documentation
+
+* [Installation][quick-start-1]
+* [Sample test code file + Example Ceedling projects][quick-start-2]
+* [Simple Ceedling project file][quick-start-3]
+* [Ceedling at the command line][quick-start-4]
+* [All your Ceedling project file options][quick-start-5]
+
+[quick-start-1]: #ceedling-installation--set-up
+[quick-start-2]: #commented-sample-test-file
+[quick-start-3]: #simple-sample-project-file
+[quick-start-4]: #now-what-how-do-i-make-it-go-the-command-line
+[quick-start-5]: #the-almighty-project-configuration-file-in-glorious-yaml
+
+
+
+---
+
+# Contents
+
+(Be sure to review **[breaking changes](BreakingChanges.md)** if you are working with
+a new release of Ceedling.)
+
+Building test suites in C requires much more scaffolding than for
+a release build. As such, much of Ceedling’s documentation is concerned
+with test builds. But, release build documentation is here too. We promise.
+It's just all mixed together.
+
+1. **[Ceedling, a C Build System for All Your Mad Scientisting Needs][packet-section-1]**
+
+ This section provides lots of background, definitions, and links for Ceedling
+ and its bundled frameworks. It also presents a very simple, example Ceedling
+ project file.
+
+1. **[Ceedling, Unity, and CMock’s Testing Abilities][packet-section-2]**
+
+ This section speaks to the philosophy of and practical options for unit testing
+ code in a variety of scenarios.
+
+1. **[How Does a Test Case Even Work?][packet-section-3]**
+
+ A brief overview of what a test case is and several simple examples illustrating
+ how test cases work.
+
+1. **[Commented Sample Test File][packet-section-4]**
+
+ This sample test file illustrates how to create test cases as well as many of the
+ conventions that Ceedling relies on to do its work. There's also a brief
+ discussion of what gets compiled and linked to create an executable test.
+
+1. **[Anatomy of a Test Suite][packet-section-5]**
+
+ This documentation explains how a unit test grows up to become a test suite.
+
+1. **[Ceedling Installation & Set Up][packet-section-6]**
+
+ This one is pretty self explanatory.
+
+1. **[Now What? How Do I Make It _GO_? The Command Line.][packet-section-7]**
+
+ Ceedling’s command line.
+
+1. **[Important Conventions & Behaviors][packet-section-8]**
+
+ Much of what Ceedling accomplishes — particularly in testing — is by convention.
+ Code and files structured in certain ways trigger sophisticated Ceedling features.
+
+1. **[Using Unity, CMock & CException][packet-section-9]**
+
+ Not only does Ceedling direct the overall build of your code, it also links
+ together several key tools and frameworks. Those can require configuration of
+ their own. Ceedling facilitates this.
+
+1. **[How to Load a Project Configuration. You Have Options, My Friend.][packet-section-10]**
+
+ You can use a command line flag, an environment variable, or rely on a default
+ file in your working directory to load your base configuration.
+
+ Once your base project configuration is loaded, you have **_Mixins_** for merging
+ additional configuration for different build scenarios as needed via command line,
+ environment variable, and/or your project configuration file.
+
+1. **[The Almighty Ceedling Project Configuration File (in Glorious YAML)][packet-section-11]**
+
+ This is the exhaustive documentation for all of Ceedling’s project file
+ configuration options — from project paths to command line tools to plugins and
+ much, much more.
+
+1. **[Which Ceedling][packet-section-12]**
+
+ Sometimes you may need to point to a different Ceedling to run.
+
+1. **[Build Directive Macros][packet-section-13]**
+
+ These code macros can help you accomplish your build goals When Ceedling’s
+ conventions aren’t enough.
+
+1. **[Ceedling Plugins][packet-section-14]**
+
+ Ceedling is extensible. It includes a number of built-in plugins for code coverage,
+ test report generation, continuous integration reporting, test file scaffolding
+ generation, sophisticated release builds, and more.
+
+1. **[Global Collections][packet-section-15]**
+
+ Ceedling is built in Ruby. Collections are globally available Ruby lists of paths,
+ files, and more that can be useful for advanced customization of a Ceedling project
+ file or in creating plugins.
+
+[packet-section-1]: #ceedling-a-c-build-system-for-all-your-mad-scientisting-needs
+[packet-section-2]: #ceedling-unity-and-c-mocks-testing-abilities
+[packet-section-3]: #how-does-a-test-case-even-work
+[packet-section-4]: #commented-sample-test-file
+[packet-section-5]: #anatomy-of-a-test-suite
+[packet-section-6]: #ceedling-installation--set-up
+[packet-section-7]: #now-what-how-do-i-make-it-go-the-command-line
+[packet-section-8]: #important-conventions--behaviors
+[packet-section-9]: #using-unity-cmock--cexception
+[packet-section-10]: #how-to-load-a-project-configuration-you-have-options-my-friend
+[packet-section-11]: #the-almighty-ceedling-project-configuration-file-in-glorious-yaml
+[packet-section-12]: #which-ceedling
+[packet-section-13]: #build-directive-macros
+[packet-section-14]: #ceedling-plugins
+[packet-section-15]: #global-collections
+
+---
+
+
+
+# Ceedling, a C Build System for All Your Mad Scientisting Needs
+
+Ceedling allows you to generate an entire test and release build
+environment for a C project from a single, short YAML configuration
+file.
+
+Ceedling and its bundled tools, Unity, CMock, and CException, don’t
+want to brag, but they’re also quite adept at supporting the tiniest of
+embedded processors, the beefiest 64-bit powerhouses available, and
everything in between.
-For a build project including unit tests and using the default
-toolchain gcc, the configuration file could be as simple as this:
+Assembling build environments for C projects — especially with
+automated unit tests — is a pain. No matter the all-purpose build
+environment tool you use, configuration is tedious and requires
+considerable glue code to pull together the necessary tools and
+libraries to run unit tests. The Ceedling bundle handles all this
+for you.
+
+## Simple Sample Project File
+
+For a project including Unity/CMock unit tests and using the default
+toolchain `gcc`, the configuration file could be as simple as this:
```yaml
:project:
@@ -32,39 +208,45 @@ toolchain gcc, the configuration file could be as simple as this:
- tests/**
:source:
- source/**
+ :include:
+ - inc/**
```
-From the command line, to build the release version of your project,
-you would simply run `ceedling release`. To run all your unit tests,
-you would run `ceedling test:all`. That's it!
+From the command line, to run all your unit tests, you would run
+`ceedling test:all`. To build the release version of your project,
+you would simply run `ceedling release`. That's it!
Of course, many more advanced options allow you to configure
your project with a variety of features to meet a variety of needs.
Ceedling can work with practically any command line toolchain
and directory structure – all by way of the configuration file.
-Further, because Ceedling piggy backs on Rake, you can add your
+Further, because Ceedling piggybacks on Rake, you can add your
own Rake tasks to accomplish project tasks outside of testing
and release builds. A facility for plugins also allows you to
-extend Ceedling's capabilities for needs such as custom code
+extend Ceedling’s capabilities for needs such as custom code
metrics reporting and coverage testing.
-What's with this Name?
+## What’s with This Name?
Glad you asked. Ceedling is tailored for unit tested C projects
-and is built upon / around Rake (Rake is a Make replacement implemented
-in the Ruby scripting language). So, we've got C, our Rake, and
-the fertile soil of a build environment in which to grow and tend
-your project and its unit tests. Ta da - _Ceedling_.
+and is built upon Rake (a Make replacement implemented in the Ruby
+scripting language). So, we've got C, our Rake, and the fertile
+soil of a build environment in which to grow and tend your project
+and its unit tests. Ta da - _Ceedling_.
-What Do You Mean "tailored for unit tested C projects"?
+## What Do You Mean “Tailored for unit tested C projects”?
Well, we like to write unit tests for our C code to make it lean and
-mean (that whole [Test-Driven Development][tdd]
-thing). Along the way, this style of writing C code spawned two
-tools to make the job easier: a unit test framework for C called
-_Unity_ and a mocking library called _CMock_. And, though it's
-not directly related to testing, a C framework for exception
-handling called _CException_ also came along.
+mean — that whole [Test-Driven Development][tdd] thing.
+
+Along the way, this style of writing C code spawned two
+tools to make the job easier:
+
+1. A unit test framework for C called _Unity_
+1. A mocking library called _CMock_
+
+And, though it's not directly related to testing, a C framework for
+exception handling called _CException_ also came along.
[tdd]: http://en.wikipedia.org/wiki/Test-driven_development
@@ -76,31 +258,41 @@ or created anew for each new project. Ceedling replaces all that
tedium and rework with a configuration file that ties everything
together.
-Though Ceedling is tailored for unit testing, it can also go right ahead
-and build your final binary release artifact for you as well. Or,
-Ceedling and your tests can live alongside your existing release build
-setup. That said, Ceedling is more powerful as a unit test build
-environment than it is a general purpose release build environment;
-complicated projects including separate bootloaders or multiple library
-builds, etc. are not its strong suit.
+Though Ceedling is tailored for unit testing, it can also go right
+ahead and build your final binary release artifact for you as well.
+That said, Ceedling is more powerful as a unit test build environment
+than it is a general purpose release build environment. Complicated
+projects including separate bootloaders or multiple library builds,
+etc. are not necessarily its strong suit (but the
+[`subprojects`](../plugins/subprojects/README.md) plugin can
+accomplish quite a bit here).
-Hold on. Back up. Ruby? Rake? YAML? Unity? CMock? CException?
+It's quite common and entirely workable to host Ceedling and your
+test suite alongside your existing release build setup. That is, you
+can use make, Visual Studio, SCons, Meson, etc. for your release build
+and Ceedling for your test build. Your two build systems will simply
+“point“ to the same project code.
-Seem overwhelming? It's not bad at all, and for the benefits tests
+## Hold on. Back up. Ruby? Rake? YAML? Unity? CMock? CException?
+
+Seems overwhelming? It's not bad at all. And, for the benefits testing
bring us, it's all worth it.
-[Ruby][] is a handy scripting
-language like Perl or Python. It's a modern, full featured language
-that happens to be quite handy for accomplishing tasks like code
-generation or automating one's workflow while developing in
-a compiled language such as C.
+### Ruby
+
+[Ruby] is a handy scripting language like Perl or Python. It's a modern,
+full featured language that happens to be quite handy for accomplishing
+tasks like code generation or automating one's workflow while developing
+in a compiled language such as C.
[Ruby]: http://www.ruby-lang.org/en/
-[Rake][] is a utility written in Ruby
-for accomplishing dependency tracking and task automation
-common to building software. It's a modern, more flexible replacement
-for [Make][]).
+### Rake
+
+[Rake] is a utility written in Ruby for accomplishing dependency
+tracking and task automation common to building software. It's a modern,
+more flexible replacement for [Make]).
+
Rakefiles are Ruby files, but they contain build targets similar
in nature to that of Makefiles (but you can also run Ruby code in
your Rakefile).
@@ -108,16 +300,29 @@ your Rakefile).
[Rake]: http://rubyrake.org/
[Make]: http://en.wikipedia.org/wiki/Make_(software)
-[YAML][] is a "human friendly data serialization standard for all
-programming languages." It's kinda like a markup language, but don't
-call it that. With a YAML library, you can [serialize][] data structures
+### YAML
+
+[YAML] is a "human friendly data serialization standard for all
+programming languages." It's kinda like a markup language but don’t
+call it that. With a YAML library, you can [serialize] data structures
to and from the file system in a textual, human readable form. Ceedling
uses a serialized data structure as its configuration input.
+YAML has some advanced features that can greatly
+[reduce duplication][yaml-anchors-aliases] in a configuration file
+needed in complex projects. YAML anchors and aliases are beyond the scope
+of this document but may be of use to advanced Ceedling users. Note that
+Ceedling does anticipate the use of YAML aliases. It proactively flattens
+YAML lists to remove any list nesting that results from the convenience of
+aliasing one list inside another.
+
[YAML]: http://en.wikipedia.org/wiki/Yaml
[serialize]: http://en.wikipedia.org/wiki/Serialization
+[yaml-anchors-aliases]: https://blog.daemonl.com/2016/02/yaml.html
-[Unity] is a [unit test framework][test] for C. It provides facilities
+### Unity
+
+[Unity] is a [unit test framework][unit-testing] for C. It provides facilities
for test assertions, executing tests, and collecting / reporting test
results. Unity derives its name from its implementation in a single C
source file (plus two C header files) and from the nature of its
@@ -125,16 +330,26 @@ implementation - Unity will build in any C toolchain and is configurable
for even the very minimalist of processors.
[Unity]: http://github.com/ThrowTheSwitch/Unity
-[test]: http://en.wikipedia.org/wiki/Unit_testing
+[unit-testing]: http://en.wikipedia.org/wiki/Unit_testing
+
+### CMock
-[CMock] is a tool written in Ruby able to generate entire
-[mock functions][mock] in C code from a given C header file. Mock
-functions are invaluable in [interaction-based unit testing][ut].
+[CMock]† is a tool written in Ruby able to generate [function mocks & stubs][test-doubles]
+in C code from a given C header file. Mock functions are invaluable in
+[interaction-based unit testing][interaction-based-tests].
CMock's generated C code uses Unity.
+† Through a [plugin][FFF-plugin], Ceedling also supports
+[FFF], _Fake Function Framework_, for [fake functions][test-doubles] as an
+alternative to CMock’s mocks and stubs.
+
[CMock]: http://github.com/ThrowTheSwitch/CMock
-[mock]: http://en.wikipedia.org/wiki/Mock_object
-[ut]: http://martinfowler.com/articles/mocksArentStubs.html
+[test-doubles]: https://blog.pragmatists.com/test-doubles-fakes-mocks-and-stubs-1a7491dfa3da
+[FFF]: https://github.com/meekrosoft/fff
+[FFF-plugin]: ../plugins/fff
+[interaction-based-tests]: http://martinfowler.com/articles/mocksArentStubs.html
+
+### CException
[CException] is a C source and header file that provide a simple
[exception mechanism][exn] for C by way of wrapping up the
@@ -146,2181 +361,4532 @@ up your return call trace.
[exn]: http://en.wikipedia.org/wiki/Exception_handling
[setjmp]: http://en.wikipedia.org/wiki/Setjmp.h
-Notes
------
+## Notes on Ceedling Dependencies and Bundled Tools
-* YAML support is included with Ruby - requires no special installation
- or configuration.
+* By using the preferred installation option of the Ruby Ceedling gem (see
+ later installation section), all other Ceedling dependencies will be
+ installed for you.
-* Unity, CMock, and CException are bundled with Ceedling, and
- Ceedling is designed to glue them all together for your project
- as seamlessly as possible.
+* Regardless of installation method, Unity, CMock, and CException are bundled
+ with Ceedling. Ceedling is designed to glue them all together for your
+ project as seamlessly as possible.
+* YAML support is included with Ruby. It requires no special installation
+ or configuration. If your project file contains properly formatted YAML
+ with the recognized names and options (see later sections), you are good
+ to go.
-Installation & Setup: What Exactly Do I Need to Get Started?
-------------------------------------------------------------
+
-As a [Ruby gem](http://docs.rubygems.org/read/chapter/1):
+# Ceedling, Unity, and CMock’s Testing Abilities
-1. [Download and install Ruby](http://www.ruby-lang.org/en/downloads/)
+The unit testing Ceedling, Unity, and CMock afford works in practically
+any context.
-2. Use Ruby's command line gem package manager to install Ceedling:
- `gem install ceedling`
- (Unity, CMock, and CException come along with Ceedling for free)
+The simplest sort of test suite is one crafted to run on the same host
+system using the same toolchain as the release artifact under development.
-3. Execute Ceedling at command line to create example project
- or an empty Ceedling project in your filesystem (executing
- `ceedling help` first is, well, helpful).
+But, Ceedling, Unity, and CMock were developed for use on a wide variety
+of systems and include features handy for low-level system development work.
+This is especially of interest to embedded systems developers.
-Gem install notes:
+## All your sweet, sweet test suite options
-1. Steps 1-2 are a one time affair for your local environment.
- When steps 1-2 are completed once, only step 3 is needed for
- each new project.
+Ceedling, Unity, and CMock help you create and run test suites using any
+of the following approaches. For more on this topic, please see this
+[handy dandy article][tts-which-build] and/or follow the links for each
+item listed below.
-Getting Started after Ceedling is installed:
+[tts-which-build]: https://throwtheswitch.org/build/which
-1. Once Ceedling is installed, you'll want to start to integrate it with new
- and old projects alike. If you wanted to start to work on a new project
- named `foo`, Ceedling can create the skeleton of the project using `ceedling
- new foo`. Likewise if you already have a project named `bar` and you want to
- integrate Ceedling into it, you would run `ceedling new bar` and Ceedling
- will create any files and directories it needs to run.
+1. **[Native][tts-build-native].** This option builds and runs code on your
+ host system.
+ 1. In the simplest case this means you are testing code that is intended
+ to run on the same sort of system as the test suite. Your test
+ compiler toolchain is the same as your release compiler toolchain.
+ 1. However, a native build can also mean your test compiler is different
+ than your release compiler. With some thought and effort, code for
+ another platform can be tested on your host system. This is often
+ the best approach for embedded and other specialized development.
+1. **[Emulator][tts-build-cross].** In this option, you build your test code with your target's
+ toolchain, and then run the test suite using an emulator provided for
+ that target. This is a good option for embedded and other specialized
+ development — if an emulator is available.
+1. **[On target][tts-build-cross].** The Ceedling bundle of tools can create test suites that
+ run on a target platform directly. Particularly in embedded development
+ — believe it or not — this is often the option of last resort. That is,
+ you should probably go with the other options in this list.
-2. Now that you have Ceedling integrated with a project, you can start using it.
- A good starting point to get use to Ceedling either in a new project or an
- existing project is creating a new module to get use to Ceedling by issuing
- the command `ceedling module:create[unicorn]`.
+[tts-build-cross]: https://throwtheswitch.org/build/cross
+[tts-build-native]: https://throwtheswitch.org/build/native
-General notes:
+
-1. Certain advanced features of Ceedling rely on gcc and cpp
- as preprocessing tools. In most linux systems, these tools
- are already available. For Windows environments, we recommend
- the [mingw project](http://www.mingw.org/) (Minimalist
- GNU for Windows). This represents an optional, additional
- setup / installation step to complement the list above. Upon
- installing mingw ensure your system path is updated or set
- [:environment][:path] in your `project.yml` file (see
- environment section later in this document).
+# How Does a Test Case Even Work?
-2. To use a project file name other than the default `project.yml`
- or place the project file in a directory other than the one
- in which you'll run Rake, create an environment variable
- `CEEDLING_MAIN_PROJECT_FILE` with your desired project
- file path.
+## Behold assertions
-3. To better understand Rake conventions, Rake execution,
- and Rakefiles, consult the [Rake tutorial, examples, and
- user guide](http://rubyrake.org/).
+In its simplest form, a test case is just a C function with no
+parameters and no return value that packages up logical assertions.
+If no assertions fail, the test case passes. Technically, an empty
+test case function is a passing test since there can be no failing
+assertions.
-4. When using Ceedling in Windows environments, a test file name may
- not include the sequences “patch” or “setup”. The Windows Installer
- Detection Technology (part of UAC), requires administrator
- privileges to execute file names with these strings.
+Ceedling relies on the [Unity] project for its unit test framework
+(i.e. the thing that provides assertions and counts up passing
+and failing tests).
-Now What? How Do I Make It GO?
-------------------------------
+An assertion is simply a logical comparison of expected and actual
+values. Unity provides a wide variety of different assertions to
+cover just about any scenario you might encounter. Getting
+assertions right is actually a bit tricky. Unity does all that
+hard work for you and has been thoroughly tested itself and battle
+hardened through use by many, many developers.
-We're getting a little ahead of ourselves here, but it's good
-context on how to drive this bus. Everything is done via the command
-line. We'll cover conventions and how to actually configure
-your project in later sections.
+### Super simple passing test case
-To run tests, build your release artifact, etc., you will be interacting
-with Rake on the command line. Ceedling works with Rake to present
-you with named tasks that coordinate the file generation and
-build steps needed to accomplish something useful. You can also
-add your own independent Rake tasks or create plugins to extend
-Ceedling (more on this later).
+```c
+#include "unity.h"
-* `ceedling [no arguments]`:
+void test_case(void) {
+ TEST_ASSERT_TRUE( (1 == 1) );
+}
+```
- Run the default Rake task (conveniently recognized by the name default
- by Rake). Neither Rake nor Ceedling provide a default task. Rake will
- abort if run without arguments when no default task is defined. You can
- conveniently define a default task in the Rakefile discussed in the
- preceding setup & installation section of this document.
+### Super simple failing test case
-* `ceedling -T`:
+```c
+#include "unity.h"
- List all available Rake tasks with descriptions (Rake tasks without
- descriptions are not listed). -T is a command line switch for Rake and
- not the same as tasks that follow.
+void test_a_different_case(void) {
+ TEST_ASSERT_TRUE( (1 == 2) );
+}
+```
-* `ceedling --trace`:
+### Realistic simple test case
- For advanced users troubleshooting a confusing build error, debug
- Ceedling or a plugin, --trace provides a stack trace of dependencies
- walked during task execution and any Ruby failures along the way. Note
- that --trace is a command line switch for Rake and is not the same as
- tasks that follow.
+In reality, we’re probably not testing the static value of an integer
+against itself. Instead, we’re calling functions in our source code
+and making assertions against return values.
-* `ceedling environment`:
+```c
+#include "unity.h"
+#include "my_math.h"
- List all configured environment variable names and string values. This
- task is helpful in verifying the evaluation of any Ruby expressions in
- the [:environment] section of your config file. *: Note: Ceedling may
- set some convenience environment variables by default.*
+void test_some_sums(void) {
+ TEST_ASSERT_EQUALS( 5, mySum( 2, 3) );
+ TEST_ASSERT_EQUALS( 6, mySum( 0, 6) );
+ TEST_ASSERT_EQUALS( -12, mySum( 20, -32) );
+}
+```
-* `ceedling paths:*`:
+If an assertion fails, the test case fails. As soon as an assertion
+fails, execution within that test case stops.
- List all paths collected from [:paths] entries in your YAML config
- file where * is the name of any section contained in [:paths]. This
- task is helpful in verifying the expansion of path wildcards / globs
- specified in the [:paths] section of your config file.
+Multiple test cases can live in the same test file. When all the
+test cases are run, their results are tallied into simple pass
+and fail metrics with a bit of metadata for failing test cases such
+as line numbers and names of test cases.
-* `ceedling files:assembly`
-* `ceedling files:include`
-* `ceedling files:source`
-* `ceedling files:support`
-* `ceedling files:test`
+Ceedling and Unity work together to both automatically run your test
+cases and tally up all the results.
- List all files and file counts collected from the relevant search
- paths specified by the [:paths] entries of your YAML config file. The
- files:assembly task will only be available if assembly support is
- enabled in the [:release_build] section of your configuration file.
+### Sample test case output
-* `ceedling options:*`:
+Successful test suite run:
- Load and merge configuration settings into the main project
- configuration. Each task is named after a `*.yml` file found in the
- configured options directory. See documentation for the configuration
- setting [:project][:options_paths] and for options files in advanced
- topics.
+```
+--------------------
+OVERALL TEST SUMMARY
+--------------------
+TESTED: 49
+PASSED: 49
+FAILED: 0
+IGNORED: 0
+```
-* `ceedling test:all`:
+A test suite with a failing test:
- Run all unit tests (rebuilding anything that's changed along the way).
+```
+-------------------
+FAILED TEST SUMMARY
+-------------------
+[test/TestModel.c]
+ Test: testInitShouldCallSchedulerAndTemperatureFilterInit
+ At line (21): "Function TaskScheduler_Init() called more times than expected."
+
+--------------------
+OVERALL TEST SUMMARY
+--------------------
+TESTED: 49
+PASSED: 48
+FAILED: 1
+IGNORED: 0
+```
-* `ceedling test:build_only`:
+### Advanced test cases with mocks
- Build all unit tests, object files and executable but not run them.
+Often you want to test not just what a function returns but how
+it interacts with other functions.
-* `ceedling test:delta`:
+The simple test cases above work well at the "edges" of a
+codebase (libraries, state management, some kinds of I/O, etc.).
+But, in the messy middle of your code, code calls other code.
+One way to handle testing this is with [mock functions][mocks] and
+[interaction-based testing][interaction-based-tests].
- Run only those unit tests for which the source or test files have
- changed (i.e. incremental build). Note: with the
- [:project][:use_test_preprocessor] configuration file option set,
- runner files are always regenerated limiting the total efficiency this
- text execution option can afford.
+Mock functions are functions with the same interface as the real
+code the mocks replace. A mocked function allows you to control
+how it behaves and wrap up assertions within a higher level idea
+of expectations.
-* `ceedling test:*`:
+What is meant by an expectation? Well… We _expect_ a certain
+function is called with certain arguments and that it will return
+certain values. With the appropriate code inside a mocked function
+all of this can be managed and checked.
- Execute the named test file or the named source file that has an
- accompanying test. No path. Examples: ceedling test:foo.c or ceedling
- test:test_foo.c
+You can write your own mocks, of course. But, it's generally better
+to rely on something else to do it for you. Ceedling uses the [CMock]
+framework to perform mocking for you.
-* `ceedling test:pattern[*]`:
+Here's some sample code you might want to test:
- Execute any tests whose name and/or path match the regular expression
- pattern (case sensitive). Example: ceedling "test:pattern[(I|i)nit]" will
- execute all tests named for initialization testing. Note: quotes may
- be necessary around the ceedling parameter to distinguish regex characters
- from command line operators.
+```c
+#include "other_code.h"
+
+void doTheThingYo(mode_t input) {
+ mode_t result = processMode(input);
+ if (result == MODE_3) {
+ setOutput(OUTPUT_F);
+ }
+ else {
+ setOutput(OUTPUT_D);
+ }
+}
+```
-* `ceedling test:path[*]`:
+And, here's what test cases using mocks for that code could look
+like:
- Execute any tests whose path contains the given string (case
- sensitive). Example: ceedling test:path[foo/bar] will execute all tests
- whose path contains foo/bar. Note: both directory separator characters
- / and \ are valid.
-
-* `ceedling test:* --test_case= `
- Execute test case which match **test_case_name**. Option available only after
- setting up **cmdline_args** to true under **test_runner** in project.yml:
-
- ```
- :test_runner:
- :cmdline_args: true
- ```
+```c
+#include "mock_other_code.h"
- For instance, if you have file test_gpio.c with defined 3 tests:
+void test_doTheThingYo_should_enableOutputF(void) {
+ // Mocks
+ processMode_ExpectAndReturn(MODE_1, MODE_3);
+ setOutput_Expect(OUTPUT_F);
- - test_gpio_start
- - test_gpio_configure_proper
- - test_gpio_configure_fail_pin_not_allowed
+ // Function under test
+ doTheThingYo(MODE_1);
+}
- and you want to run only configure tests, you can call:
+void test_doTheThingYo_should_enableOutputD(void) {
+ // Mocks
+ processMode_ExpectAndReturn(MODE_2, MODE_4);
+ setOutput_Expect(OUTPUT_D);
- ```ceedling test:gpio --test_case=configure```
+ // Function under test
+ doTheThingYo(MODE_2);
+}
+```
- ---
- **Limitation**
+Remember, the generated mock code you can’t see here has a whole bunch
+of smarts and Unity assertions inside it. CMock scans header files and
+then generates mocks (C code) from the function signatures it finds in
+those header files. It's kinda magical.
- The Unity implementation use test case name as substring which will be search in your test case names. If you pass only **gpio** and your file under test contains **gpio** in the name, it will run all tests from it. This is connected with the logic, how Unity generates test_ files. In such case, it is suggested to use full name of test case.
+### That was the basics, but you’ll need more
- ---
+For more on the assertions and mocking shown above, consult the
+documentation for [Unity] and [CMock] or the resources in
+Ceedling’s [README][/README.md].
-* `ceedling test:* --exclude_test_case= `
- Execute test case which does not match **test_case_name**. Option available only after
- setting up **cmdline_args** to true under **test_runner** in project.yml:
-
- ```
- :test_runner:
- :cmdline_args: true
- ```
+Ceedling, Unity, and CMock rely on a variety of
+[conventions to make your life easier][conventions-and-behaviors].
+Read up on these to understand how to build up test cases
+and test suites.
- For instance, if you have file test_gpio.c with defined 3 tests:
+Also take a look at the very next sections for more examples
+and details on how everything fits together.
- - test_gpio_start
- - test_gpio_configure_proper
- - test_gpio_configure_fail_pin_not_allowed
+[conventions-and-behaviors]: #important-conventions--behaviors
- and you want to run only start tests, you can call:
+
- ```ceedling test:gpio --exclude_test_case=configure```
+# Commented Sample Test File
- ---
- **Limitation**
+**Here is a beautiful test file to help get you started…**
- The Unity implementation use test case name as substring which will be search in your test case names. If you pass only **gpio** and your file under test contains **gpio** in the name, it will run all tests from it. This is connected with the logic, how Unity generates test_ files. In such case, it is suggested to use full name of test case.
+## Core concepts in code
- ---
+After absorbing this sample code, you'll have context for much
+of the documentation that follows.
+The sample test file below demonstrates the following:
-* `ceedling release`:
+1. Making use of the Unity & CMock test frameworks.
+1. Adding the source under test (`foo.c`) to the final test
+ executable by convention (`#include "foo.h"`).
+1. Adding two mocks to the final test executable by convention
+ (`#include "mock_bar.h` and `#include "mock_baz.h`).
+1. Adding a source file with no matching header file to the test
+ executable with a test build directive macro
+ `TEST_SOURCE_FILE("more.c")`.
+1. Creating two test cases with mock expectations and Unity
+ assertions.
- Build all source into a release artifact (if the release build option
- is configured).
+All other conventions and features are documented in the sections
+that follow.
-* `ceedling release:compile:*`:
+```c
+// test_foo.c -----------------------------------------------
+#include "unity.h" // Compile/link in Unity test framework
+#include "types.h" // Header file with no *.c file -- no compilation/linking
+#include "foo.h" // Corresponding source file, foo.c, under test will be compiled and linked
+#include "mock_bar.h" // bar.h will be found and mocked as mock_bar.c + compiled/linked in;
+#include "mock_baz.h" // baz.h will be found and mocked as mock_baz.c + compiled/linked in
- Sometimes you just need to compile a single file dagnabit. Example:
- ceedling release:compile:foo.c
+TEST_SOURCE_FILE("more.c") // foo.c depends on symbols from more.c, but more.c has no matching more.h
-* `ceedling release:assemble:*`:
+void setUp(void) {} // Every test file requires this function;
+ // setUp() is called by the generated runner before each test case function
- Sometimes you just need to assemble a single file doggonit. Example:
- ceedling release:assemble:foo.s
+void tearDown(void) {} // Every test file requires this function;
+ // tearDown() is called by the generated runner after each test case function
-* `ceedling module:create[Filename]`:
-* `ceedling module:create[Filename]`:
+// A test case function
+void test_Foo_Function1_should_Call_Bar_AndGrill(void)
+{
+ Bar_AndGrill_Expect(); // Function from mock_bar.c that instructs our mocking
+ // framework to expect Bar_AndGrill() to be called once
+ TEST_ASSERT_EQUAL(0xFF, Foo_Function1()); // Foo_Function1() is under test (Unity assertion):
+ // (a) Calls Bar_AndGrill() from bar.h
+ // (b) Returns a byte compared to 0xFF
+}
- It's often helpful to create a file automatically. What's better than
- that? Creating a source file, a header file, and a corresponding test
- file all in one step!
+// Another test case function
+void test_Foo_Function2_should_Call_Baz_Tec(void)
+{
+ Baz_Tec_ExpectAnd_Return(1); // Function from mock_baz.c that instructs our mocking
+ // framework to expect Baz_Tec() to be called once and return 1
+ TEST_ASSERT_TRUE(Foo_Function2()); // Foo_Function2() is under test (Unity assertion)
+ // (a) Calls Baz_Tec() in baz.h
+ // (b) Returns a value that can be compared to boolean true
+}
- There are also patterns which can be specified to automatically generate
- a bunch of files. Try `ceedling module:create[Poodles,mch]` for example!
+// end of test_foo.c ----------------------------------------
+```
- The module generator has several options you can configure.
- F.e. Generating the source/header/test file in a subdirectory (by adding when calling module:create).
- For more info, refer to the [Module Generator](https://github.com/ThrowTheSwitch/Ceedling/blob/master/docs/CeedlingPacket.md#module-generator) section.
+## Ceedling actions from the sample test code
-* `ceedling module:stub[Filename]`:
-* `ceedling module:stub[Filename]`:
+From the test file specified above Ceedling will generate
+`test_foo_runner.c`. This runner file will contain `main()` and will call
+both of the example test case functions.
- So what happens if you've created your API in your header (maybe even using
- TDD to do so?) and now you need to start to implement the corresponding C
- module? Why not get a head start by using `ceedilng module:stub[headername]`
- to automatically create a function skeleton for every function declared in
- that header? Better yet, you can call this again whenever you add new functions
- to that header to add just the new functions, leaving the old ones alone!
+The final test executable will be `test_foo.exe` (Windows) or `test_foo.out`
+for Unix-based systems (extensions are configurable. Based on the `#include`
+list and test directive macro above, the test executable will be the output
+of the linker having processed `unity.o`, `foo.o`, `mock_bar.o`, `mock_baz.o`,
+`more.o`, `test_foo.o`, and `test_foo_runner.o`.
-* `ceedling logging `:
+Ceedling finds the needed code files, generates mocks, generates a runner,
+compiles all the code files, and links everything into the test executable.
+Ceedling will then run the test executable and collect test results from it
+to be reported to the developer at the command line.
- Enable logging to /logs. Must come before test and release
- tasks to log their steps and output. Log names are a concatenation of
- project, user, and option files loaded. User and option files are
- documented in the advanced topics section of this document.
+## Incidentally, Ceedling comes with example projects
-* `ceedling verbosity[x] `:
+Ceedling comes with entire example projects you can extract.
- Change the default verbosity level. [x] ranges from 0 (quiet) to 4
- (obnoxious). Level [3] is the default. The verbosity task must precede
- all tasks in the command line list for which output is desired to be
- seen. Verbosity settings are generally most meaningful in conjunction
- with test and release tasks.
+1. Execute `ceedling examples` in your terminal to list available example
+ projects.
+1. Execute `ceedling example [destination]` to extract the
+ named example project.
-* `ceedling summary`:
+You can inspect the _project.yml_ file and source & test code. Run
+`ceedling help` from the root of the example projects to see what you can
+do, or just go nuts with `ceedling test:all`.
- If plugins are enabled, this task will execute the summary method of
- any plugins supporting it. This task is intended to provide a quick
- roundup of build artifact metrics without re-running any part of the
- build.
+
-* `ceedling clean`:
+# Anatomy of a Test Suite
- Deletes all toolchain binary artifacts (object files, executables),
- test results, and any temporary files. Clean produces no output at the
- command line unless verbosity has been set to an appreciable level.
+A Ceedling test suite is composed of one or more individual test executables.
-* `ceedling clobber`:
+The [Unity] project provides the actual framework for test case assertions
+and unit test sucess/failure accounting. If mocks are enabled, [CMock] builds
+on Unity to generate mock functions from source header files with expectation
+test accounting. Ceedling is the glue that combines these frameworks, your
+project's toolchain, and your source code into a collection of test
+executables you can run as a singular suite.
- Extends clean task's behavior to also remove generated files: test
- runners, mocks, preprocessor output. Clobber produces no output at the
- command line unless verbosity has been set to an appreciable level.
+## What is a test executable?
-* `ceedling options:export`:
+Put simply, in a Ceedling test suite, each test file becomes a test executable.
+Your test code file becomes a single test executable.
- This allows you to export a snapshot of your current tool configuration
- as a yaml file. You can specify the name of the file in brackets `[blah.yml]`
- or let it default to `tools.yml`. In either case, the produced file can be
- used as the tool configuration for you project if desired, and modified as you
- wish.
+`test_foo.c` ➡️ `test_foo.out` (or `test_foo.exe` on Windows)
-To better understand Rake conventions, Rake execution, and
-Rakefiles, consult the [Rake tutorial, examples, and user guide][guide].
+A single test executable generally comprises the following. Each item in this
+list is a C file compiled into an object file. The entire list is linked into
+a final test executable.
-[guide]: http://rubyrake.org/
+* One or more release C code files under test (`foo.c`)
+* `unity.c`.
+* A test C code file (`test_foo.c`).
+* A generated test runner C code file (`test_foo_runner.c`). `main()` is located
+ in the runner.
+* If using mocks:
+ * `cmock.c`
+ * One more mock C code files generated from source header files (`mock_bar.c`)
-At present, none of Ceedling's commands provide persistence.
-That is, they must each be specified at the command line each time
-they are needed. For instance, Ceedling's verbosity command
-only affects output at the time it's run.
+## Why multiple individual test executables in a suite?
-Individual test and release file tasks
-are not listed in `-T` output. Because so many files may be present
-it's unwieldy to list them all.
+For several reasons:
-Multiple rake tasks can be executed at the command line (order
-is executed as provided). For example, `ceedling
-clobber test:all release` will removed all generated files;
-build and run all tests; and then build all source - in that order.
-If any Rake task fails along the way, execution halts before the
-next task.
+* This greatly simplifies the building of your tests.
+* C lacks any concept of namespaces or reflection abilities able to segment and
+ distinguish test cases.
+* This allows the same release code to be built differently under different
+ testing scenarios. Think of how different `#define`s, compiler flags, and
+ linked libraries might come in handy for different tests of the same
+ release C code. One source file can be built and tested in different ways
+ with multiple test files.
-The `clobber` task removes certain build directories in the
-course of deleting generated files. In general, it's best not
-to add to source control any Ceedling generated directories
-below the root of your top-level build directory. That is, leave
-anything Ceedling & its accompanying tools generate out of source
-control (but go ahead and add the top-level build directory that
-holds all that stuff). Also, since Ceedling is pretty smart about
-what it rebuilds and regenerates, you needn't clobber often.
+## Ceedling’s role in your test suite
-Important Conventions
-=====================
+A test executable is not all that hard to create by hand, but it can be tedious,
+repetitive, and error-prone.
-Directory Structure, Filenames & Extensions
--------------------------------------------
+What Ceedling provides is an ability to perform the process repeatedly and simply
+at the push of a button, alleviating the tedium and any forgetfulness. Just as
+importantly, Ceedling also does all the work of running each of those test
+executables and tallying all the test results.
-Much of Ceedling's functionality is driven by collecting files
-matching certain patterns inside the paths it's configured
-to search. See the documentation for the [:extension] section
-of your configuration file (found later in this document) to
-configure the file extensions Ceedling uses to match and collect
-files. Test file naming is covered later in this section.
+
-Test files and source files must be segregated by directories.
-Any directory structure will do. Tests can be held in subdirectories
-within source directories, or tests and source directories
-can be wholly separated at the top of your project's directory
-tree.
+# Ceedling Installation & Set Up
-Search Path Order
------------------
-
-When Ceedling searches for files (e.g. looking for header files
-to mock) or when it provides search paths to any of the default
-gcc toolchain executables, it organizes / prioritizes its search
-paths. The order is always: test paths, support paths, source
-paths, and then include paths. This can be useful, for instance,
-in certain testing scenarios where we desire Ceedling or a compiler
-to find a stand-in header file in our support directory before
-the actual source header file of the same name.
-
-This convention only holds when Ceedling is using its default
-tool configurations and / or when tests are involved. If you define
-your own tools in the configuration file (see the [:tools] section
-documented later in this here document), you have complete control
-over what directories are searched and in what order. Further,
-test and support directories are only searched when appropriate.
-That is, when running a release build, test and support directories
-are not used at all.
-
-Source Files & Binary Release Artifacts
----------------------------------------
+**How Exactly Do I Get Started?**
-Your binary release artifact results from the compilation and
-linking of all source files Ceedling finds in the specified source
-directories. At present only source files with a single (configurable)
-extension are recognized. That is, `*.c` and `*.cc` files will not
-both be recognized - only one or the other. See the configuration
-options and defaults in the documentation for the [:extension]
-sections of your configuration file (found later in this document).
+The simplest way to get started is to install Ceedling as a Ruby gem. Gems are
+simply prepackaged Ruby-based software. Other options exist, but they are most
+useful for developing Ceedling
-Test Files & Executable Test Fixtures
--------------------------------------
+## As a [Ruby gem](http://docs.rubygems.org/read/chapter/1):
-Ceedling builds each individual test file with its accompanying
-source file(s) into a single, monolithic test fixture executable.
-Test files are recognized by a naming convention: a (configurable)
-prefix such as "`test_`" in the file name with the same file extension
-as used by your C source files. See the configuration options
-and defaults in the documentation for the [:project] and [:extension]
-sections of your configuration file (found later in this document).
-Depending on your configuration options, Ceedling can recognize
-a variety of test file naming patterns in your test search paths.
-For example: `test_some_super_functionality.c`, `TestYourSourceFile.cc`,
-or `testing_MyAwesomeCode.C` could each be valid test file
-names. Note, however, that Ceedling can recognize only one test
-file naming convention per project.
+1. [Download and install Ruby][ruby-install]. Ruby 3 is required.
-Ceedling knows what files to compile and link into each individual
-test executable by way of the #include list contained in each
-test file. Any C source files in the configured search directories
-that correspond to the header files included in a test file will
-be compiled and linked into the resulting test fixture executable.
-From this same #include list, Ceedling knows which files to mock
-and compile and link into the test executable (if you use mocks
-in your tests). That was a lot of clauses and information in a very
-few sentences; the example that follows in a bit will make it clearer.
+1. Use Ruby's command line gem package manager to install Ceedling:
+ `gem install ceedling`. Unity, CMock, and CException come along with
+ Ceedling at no extra charge.
-By naming your test functions according to convention, Ceedling
-will extract and collect into a runner C file calls to all your
-test case functions. This runner file handles all the execution
-minutiae so that your test file can be quite simple and so that
-you never forget to wire up a test function to be executed. In this
-generated runner lives the `main()` entry point for the resulting
-test executable. There are no configuration options for the
-naming convention of your test case functions. A test case function
-signature must have these three elements: void return, void
-parameter list, and the function name prepended with lowercase
-"`test`". In other words, a test function signature should look
-like this: `void test``[any name you like]``(void)`.
-
-A commented sample test file follows on the next page. Also, see
-the sample project contained in the Ceedling documentation
-bundle.
+1. Execute Ceedling at command line to create example project
+ or an empty Ceedling project in your filesystem (executing
+ `ceedling help` first is, well, helpful).
-```c
-// test_foo.c -----------------------------------------------
-#include "unity.h" // compile/link in Unity test framework
-#include "types.h" // header file with no *.c file -- no compilation/linking
-#include "foo.h" // source file foo.c under test
-#include "mock_bar.h" // bar.h will be found and mocked as mock_bar.c + compiled/linked in;
- // foo.c includes bar.h and uses functions declared in it
-#include "mock_baz.h" // baz.h will be found and mocked as mock_baz.c + compiled/linked in
- // foo.c includes baz.h and uses functions declared in it
+[ruby-install]: http://www.ruby-lang.org/en/downloads/
+### Gem install notes
-void setUp(void) {} // every test file requires this function;
- // setUp() is called by the generated runner before each test case function
+Steps 1-2 are a one time affair for your local environment. When steps 1-2
+are completed once, only step 3 is needed for each new project.
-void tearDown(void) {} // every test file requires this function;
- // tearDown() is called by the generated runner after each test case function
+## Getting Started after Ceedling is Installed
-// a test case function
-void test_Foo_Function1_should_Call_Bar_AndGrill(void)
-{
- Bar_AndGrill_Expect(); // setup function from mock_bar.c that instructs our
- // framework to expect Bar_AndGrill() to be called once
- TEST_ASSERT_EQUAL(0xFF, Foo_Function1()); // assertion provided by Unity
- // Foo_Function1() calls Bar_AndGrill() & returns a byte
-}
+1. Once Ceedling is installed, you'll want to start to integrate it with new
+ and old projects alike. If you wanted to start to work on a new project
+ named `foo`, Ceedling can create the skeleton of the project using `ceedling
+ new foo`. Likewise if you already have a project named `bar` and you want to
+ integrate Ceedling into it, you would run `ceedling new bar` and Ceedling
+ will create any files and directories it needs to run.
-// another test case function
-void test_Foo_Function2_should_Call_Baz_Tec(void)
-{
- Baz_Tec_ExpectAnd_Return(1); // setup function provided by mock_baz.c that instructs our
- // framework to expect Baz_Tec() to be called once and return 1
- TEST_ASSERT_TRUE(Foo_Function2()); // assertion provided by Unity
-}
+1. Now that you have Ceedling integrated with a project, you can start using it.
+ A good starting point to get use to Ceedling either in a new project or an
+ existing project is creating a new module to get use to Ceedling by issuing
+ the command `ceedling module:create[unicorn]`.
-// end of test_foo.c ----------------------------------------
-```
+## Grab Bag of Ceedling Notes
-From the test file specified above Ceedling will generate `test_foo_runner.c`;
-this runner file will contain `main()` and call both of the example
-test case functions.
-
-The final test executable will be `test_foo.exe` (for Windows
-machines or `test_foo.out` for linux systems - depending on default
-or configured file extensions). Based on the #include list above,
-the test executable will be the output of the linker having processed
-`unity.o`, `foo.o`, `mock_bar.o`, `mock_baz.o`, `test_foo.o`,
-and `test_foo_runner.o`. Ceedling finds the files, generates
-mocks, generates a runner, compiles all the files, and links
-everything into the test executable. Ceedling will then run
-the test executable and collect test results from it to be reported
-to the developer at the command line.
-
-For more on the assertions and mocks shown, consult the documentation
-for Unity and CMock.
-
-The Magic of Dependency Tracking
---------------------------------
-
-Ceedling is pretty smart in using Rake to build up your project's
-dependencies. This means that Ceedling automagically rebuilds
-all the appropriate files in your project when necessary: when
-your configuration changes, Ceedling or any of the other tools
-are updated, or your source or test files change. For instance,
-if you modify a header file that is mocked, Ceedling will ensure
-that the mock is regenerated and all tests that use that mock are
-rebuilt and re-run when you initiate a relevant testing task.
-When you see things rebuilding, it's for a good reason. Ceedling
-attempts to regenerate and rebuild only what's needed for a given
-execution of a task. In the case of large projects, assembling
-dependencies and acting upon them can cause some delay in executing
-tasks.
-
-With one exception, the trigger to rebuild or regenerate a file
-is always a disparity in timestamps between a target file and
-its source - if an input file is newer than its target dependency,
-the target is rebuilt or regenerated. For example, if the C source
-file from which an object file is compiled is newer than that object
-file on disk, recompilation will occur (of course, if no object
-file exists on disk, compilation will always occur). The one
-exception to this dependency behavior is specific to your input
-configuration. Only if your logical configuration changes
-will a system-wide rebuild occur. Reorganizing your input configuration
-or otherwise updating its file timestamp without modifying
-the values within the file will not trigger a rebuild. This behavior
-handles the various ways in which your input configuration can
-change (discussed later in this document) without having changed
-your actual project YAML file.
-
-Ceedling needs a bit of help to accomplish its magic with deep
-dependencies. Shallow dependencies are straightforward:
-a mock is dependent on the header file from which it's generated,
-a test file is dependent upon the source files it includes (see
-the preceding conventions section), etc. Ceedling handles
-these "out of the box." Deep dependencies are specifically a
-C-related phenomenon and occur as a consequence of include statements
-within C source files. Say a source file includes a header file
-and that header file in turn includes another header file which
-includes still another header file. A change to the deepest header
-file should trigger a recompilation of the source file, a relinking
-of all the object files comprising a test fixture, and a new execution
-of that test fixture.
-
-Ceedling can handle deep dependencies but only with the help
-of a C preprocessor. Ceedling is quite capable, but a full C preprocessor
-it ain't. Your project can be configured to use a C preprocessor
-or not. Simple projects or large projects constructed so as to
-be quite flat in their include structure generally don't need
-deep dependency preprocessing - and can enjoy the benefits of
-faster execution. Legacy code, on the other hand, will almost
-always want to be tested with deep preprocessing enabled. Set
-up of the C preprocessor is covered in the documentation for the
-[:project] and [:tools] section of the configuration file (later
-in this document). Ceedling contains all the configuration
-necessary to use the gcc preprocessor by default. That is, as
-long as gcc is in your system search path, deep preprocessing
-of deep dependencies is available to you by simply enabling it
-in your project configuration file.
-
-Ceedling's Build Output
------------------------
+1. Certain advanced features of Ceedling rely on `gcc` and `cpp`
+ as preprocessing tools. In most Linux systems, these tools
+ are already available. For Windows environments, we recommend
+ the [MinGW project](http://www.mingw.org/) (Minimalist
+ GNU for Windows). This represents an optional, additional
+ setup / installation step to complement the list above. Upon
+ installing MinGW ensure your system path is updated or set
+ `:environment` ↳ `:path` in your project file (see
+ `:environment` section later in this document).
-Ceedling requires a top-level build directory for all the stuff
-that it, the accompanying test tools, and your toolchain generate.
-That build directory's location is configured in the [:project]
-section of your configuration file (discussed later). There
-can be a ton of generated files. By and large, you can live a full
-and meaningful life knowing absolutely nothing at all about
-the files and directories generated below the root build directory.
+1. To better understand Rake conventions, Rake execution,
+ and Rakefiles, consult the [Rake tutorial, examples, and
+ user guide](http://rubyrake.org/).
-As noted already, it's good practice to add your top-level build
-directory to source control but nothing generated beneath it.
-You'll spare yourself headache if you let Ceedling delete and
-regenerate files and directories in a non-versioned corner
-of your project's filesystem beneath the top-level build directory.
+1. When using Ceedling in Windows environments, a test file name may
+ not include the sequences “patch” or “setup”. The Windows Installer
+ Detection Technology (part of UAC), requires administrator
+ privileges to execute file names with these strings.
-The `artifacts` directory is the one and only directory you may
-want to know about beneath the top-level build directory. The
-subdirectories beneath `artifacts` will hold your binary release
-target output (if your project is configured for release builds)
-and will serve as the conventional location for plugin output.
-This directory structure was chosen specifically because it
-tends to work nicely with Continuous Integration setups that
+
+
+# Now What? How Do I Make It _GO_? The Command Line.
+
+We’re getting a little ahead of ourselves here, but it's good
+context on how to drive this bus. Everything is done via the command
+line. We'll cover project conventions and how to actually configure
+your project in later sections.
+
+For now, let's talk about the command line.
+
+To run tests, build your release artifact, etc., you will be using the
+trusty command line. Ceedling is transitioning away from being built
+around Rake. As such, right now, interacting with Ceedling at the
+command line involves two different conventions:
+
+1. **Application Commands.** Application commands tell Ceedling what to
+ to do with your project. These create projects, load project files,
+ begin builds, output version information, etc. These include rich
+ help and operate similarly to popular command line tools like `git`.
+1. **Build & Plugin Tasks.** Build tasks actually execute test suites,
+ run release builds, etc. These tasks are created from your project
+ file. These are generated through Ceedling’s Rake-based code and
+ conform to its conventions — simplistic help, no option flags, but
+ bracketed arguments.
+
+In the case of running builds, both come into play at the command line.
+
+The two classes of command line arguments are clearly labelled in the
+summary of all commands provided by `ceedling help`.
+
+## Quick command line example to get you started
+
+To exercise the Ceedling command line quickly, follow these steps after
+[installing Ceedling](#ceedling-installation--set-up):
+
+1. Open a terminal and chnage directories to a location suitable for
+ an example project.
+1. Execute `ceedling example temp_sensor` in your terminal. The `example`
+ argument is an application command.
+1. Change directories into the new _temp_sensor/_ directory.
+1. Execute `ceedling test:all` in your terminal. The `test:all` is a
+ build task executed by the default (and omitted) `build` application
+ command.
+1. Take a look at the build and test suite console output as well as
+ the _project.yml_ file in the root of the example project.
+
+## Ceedling application commands
+
+Ceedling provides robust command line help for application commands.
+Execute `ceedling help` for a summary view of all application commands.
+Execute `ceedling help ` for detailed help.
+
+_Note:_ Because the built-in command line help is thorough, we will only
+briefly list and explain the available application commands.
+
+* `ceedling [no arguments]`:
+
+ Runs the default build tasks. Unless set in the project file, Ceedling
+ uses a default task of `test:all`. To override this behavior, set your
+ own default tasks in the project file (see later section).
+
+* `ceedling build ` or `ceedling `:
+
+ Runs the named build tasks. `build` is optional (i.e. `ceedling test:all`
+ is equivalent to `ceedling build test:all`). Various option flags
+ exist to control project configuration loading, verbosity levels,
+ logging, test task filters, etc.
+
+ See next section to understand the build & plugin tasks this application
+ command is able to execute. Run `ceedling help build` to understand all
+ the command line flags that work with build & plugin tasks.
+
+* `ceedling dumpconfig`:
+
+ Process project configuration and write final result to a YAML file.
+ Various option flags exist to control project configuration loading,
+ configuration manipulation, and configuration sub-section extraction.
+
+* `ceedling environment`:
+
+ Lists project related environment variables:
+
+ * All environment variable names and string values added to your
+ environment from within Ceedling and through the `:environment`
+ section of your configuration. This is especially helpful in
+ verifying the evaluation of any string replacement expressions in
+ your `:environment` config entries.
+ * All existing Ceedling-related environment variables set before you
+ ran Ceedling from the command line.
+
+* `ceedling example`:
+
+ Extracts an example project from within Ceedling to your local
+ filesystem. The available examples are listed with
+ `ceedling examples`. Various option flags control whether the example
+ contains vendored Ceedling and/or a documentation bundle.
+
+* `ceedling examples`:
+
+ Lists the available examples within Ceedling. To extract an example,
+ use `ceedling example`.
+
+* `ceedling help`:
+
+ Displays summary help for all application commands and detailed help
+ for each command. `ceedling help` also loads your project
+ configuration (if available) and lists all build tasks from it.
+ Various option flags control what project configuration is loaded.
+
+* `ceedling new`:
+
+ Creates a new project structure. Various option flags control whether
+ the new project contains vendored Ceedling, a documentation bundle,
+ and/or a starter project configuration file.
+
+* `ceedling upgrade`:
+
+ Upgrade vendored installation of Ceedling for an existing project
+ along with any locally installed documentation bundles.
+
+* `ceedling version`:
+
+ Displays version information for Ceedling and its components.
+
+## Ceedling build & plugin tasks
+
+Build task are loaded from your project configuration. Unlike
+application commands that are fixed, build tasks vary depending on your
+project configuration and the files within your project structure.
+
+Ultimately, build & plugin tasks are executed by the `build` application
+command (but the `build` keyword can be omitted — see above).
+
+* `ceedling paths:*`:
+
+ List all paths collected from `:paths` entries in your YAML config
+ file where `*` is the name of any section contained in `:paths`. This
+ task is helpful in verifying the expansion of path wildcards / globs
+ specified in the `:paths` section of your config file.
+
+* `ceedling files:assembly`
+* `ceedling files:header`
+* `ceedling files:source`
+* `ceedling files:support`
+* `ceedling files:test`
+
+ List all files and file counts collected from the relevant search
+ paths specified by the `:paths` entries of your YAML config file. The
+ `files:assembly` task will only be available if assembly support is
+ enabled in the `:release_build` or `:test_build` sections of your
+ configuration file.
+
+* `ceedling test:all`:
+
+ Run all unit tests.
+
+* `ceedling test:*`:
+
+ Execute the named test file or the named source file that has an
+ accompanying test. No path. Examples: `ceedling test:foo`, `ceedling
+ test:foo.c` or `ceedling test:test_foo.c`
+
+* `ceedling test:* --test-case= `
+ Execute individual test cases which match `test_case_name`.
+
+ For instance, if you have a test file _test_gpio.c_ containing the following
+ test cases (test cases are simply `void test_name(void)`:
+
+ - `test_gpio_start`
+ - `test_gpio_configure_proper`
+ - `test_gpio_configure_fail_pin_not_allowed`
+
+ … and you want to run only _configure_ tests, you can call:
+
+ `ceedling test:gpio --test-case=configure`
+
+ **Test case matching notes**
+
+ * Test case matching is on sub-strings. `--test_case=configure` matches on
+ the test cases including the word _configure_, naturally.
+ `--test-case=gpio` would match all three test cases.
+
+* `ceedling test:* --exclude_test_case= `
+ Execute test cases which do not match `test_case_name`.
+
+ For instance, if you have file test_gpio.c with defined 3 tests:
+
+ - `test_gpio_start`
+ - `test_gpio_configure_proper`
+ - `test_gpio_configure_fail_pin_not_allowed`
+
+ … and you want to run only start tests, you can call:
+
+ `ceedling test:gpio --exclude_test_case=configure`
+
+ **Test case exclusion matching notes**
+
+ * Exclude matching follows the same sub-string logic as discussed in the
+ preceding section.
+
+* `ceedling test:pattern[*]`:
+
+ Execute any tests whose name and/or path match the regular expression
+ pattern (case sensitive). Example: `ceedling "test:pattern[(I|i)nit]"`
+ will execute all tests named for initialization testing.
+
+ _Note:_ Quotes are likely necessary around the regex characters or
+ entire task to distinguish characters from shell command line operators.
+
+* `ceedling test:path[*]`:
+
+ Execute any tests whose path contains the given string (case
+ sensitive). Example: `ceedling test:path[foo/bar]` will execute all tests
+ whose path contains foo/bar. _Notes:_
+
+ 1. Both directory separator characters `/` and `\` are valid.
+ 1. Quotes may be necessary around the task to distinguish the parameter's
+ characters from shell command line operators.
+
+* `ceedling release`:
+
+ Build all source into a release artifact (if the release build option
+ is configured).
+
+* `ceedling release:compile:*`:
+
+ Sometimes you just need to compile a single file dagnabit. Example:
+ `ceedling release:compile:foo.c`
+
+* `ceedling release:assemble:*`:
+
+ Sometimes you just need to assemble a single file doggonit. Example:
+ `ceedling release:assemble:foo.s`
+
+* `ceedling summary`:
+
+ If plugins are enabled, this task will execute the summary method of
+ any plugins supporting it. This task is intended to provide a quick
+ roundup of build artifact metrics without re-running any part of the
+ build.
+
+* `ceedling clean`:
+
+ Deletes all toolchain binary artifacts (object files, executables),
+ test results, and any temporary files. Clean produces no output at the
+ command line unless verbosity has been set to an appreciable level.
+
+* `ceedling clobber`:
+
+ Extends clean task's behavior to also remove generated files: test
+ runners, mocks, preprocessor output. Clobber produces no output at the
+ command line unless verbosity has been set to an appreciable level.
+
+## Ceedling Command Line Tasks, Extra Credit
+
+### Combining Tasks At the Command Line
+
+Multiple build tasks can be executed at the command line.
+
+For example, `ceedling clobber test:all release` will remove all generated
+files; build and run all tests; and then build all source — in that order. If
+any task fails along the way, execution halts before the next task.
+
+Task order is executed as provided and can be important! Running `clobber` after
+a `test:` or `release:` task will not accomplish much.
+
+### Build Directory and Revision Control
+
+The `clobber` task removes certain build directories in the
+course of deleting generated files. In general, it's best not
+to add to source control any Ceedling generated directories
+below the root of your top-level build directory. That is, leave
+anything Ceedling & its accompanying tools generate out of source
+control (but go ahead and add the top-level build directory that
+holds all that stuff if you want).
+
+### Logging decorators
+
+Ceedling attempts to bring more joy to your console logging. This may include
+fancy Unicode characters, emoji, or color.
+
+By default, Ceedling makes an educated guess as to which platforms can best
+support this. Some platforms (we’re looking at you, Windows) do not typically
+have default font support in their terminals for these features. So, by default
+this feature is disabled on problematic platforms while enabled on others.
+
+An environment variable `CEEDLING_DECORATORS` forces decorators on or off with a
+`true` (`1`) or `false` (`0`) string value.
+
+If you find a monospaced font that provides emojis, etc. and works with Windows’
+command prompt, you can (1) Install the font (2) change your command prompt’s
+font (3) set `CEEDLING_DECORATORS` to `true`.
+
+
+
+# Important Conventions & Behaviors
+
+**How to get things done and understand what’s happening during builds**
+
+## Directory Structure, Filenames & Extensions
+
+Much of Ceedling’s functionality is driven by collecting files
+matching certain patterns inside the paths it's configured
+to search. See the documentation for the `:extension` section
+of your configuration file (found later in this document) to
+configure the file extensions Ceedling uses to match and collect
+files. Test file naming is covered later in this section.
+
+Test files and source files must be segregated by directories.
+Any directory structure will do. Tests can be held in subdirectories
+within source directories, or tests and source directories
+can be wholly separated at the top of your project's directory
+tree.
+
+## Search Path / File Collection Ordering
+
+Path order is important and needed by various functions. Ceedling
+itself needs a path order to find files such as header files
+that get mocked. Tasks are often ordered by the contents of file
+collections Ceedling builds. Toolchains rely on a search path
+order to compile code.
+
+Paths are organized and prioritized like this:
+
+1. Test paths
+1. Support paths
+1. Source paths
+1. Source include paths
+
+Of course, this list is context dependent. A release build pays
+no attention to test or support paths. And, as is documented
+elsewhere, header file search paths do not incorporate source
+file paths.
+
+This ordering can be useful to the user in certain testing scenarios
+where we desire Ceedling or a compiler to find a stand-in header
+file in our support directory before the actual source header
+file of the same name in the source include path list.
+
+If you define your own tools in the project configuration file (see
+the `:tools` section documented later in this here document), you have
+some control over what directories are searched and in what order.
+
+## Configuring Your Header File Search Paths
+
+Unless your project is relying exclusively on `extern` statements and
+uses no mocks for testing, Ceedling _**must**_ be told where to find
+header files. Without search path knowledge, mocks cannot be generated,
+and code cannot be compiled.
+
+Ceedling provides two mechanisms for configuring header file
+search paths:
+
+1. The [`:paths` ↳ `:include`](#paths--include) section within your
+ project file. This is available to both test and release builds.
+1. The [`TEST_INCLUDE_PATH(...)`](#test_include_path) build directive
+ macro. This is only available within test files.
+
+In testing contexts, you have three options for creating the header
+file search path list used by Ceedling:
+
+1. List all search paths within the `:paths` ↳ `:include` subsection
+ of your project file. This is the simplest and most common approach.
+1. Create the search paths for each test file using calls to the
+ `TEST_INCLUDE_PATH(...)` build directive macro within each test file.
+1. Blending the preceding options. In this approach the subsection
+ within your project file acts as a common, base list of search
+ paths while the build directive macro allows the list to be
+ expanded upon for each test file. This method is especially helpful
+ for large and/or complex projects—especially in trimming down
+ problematically long compiler command lines.
+
+## Conventions for Source Files & Binary Release Artifacts
+
+Your binary release artifact results from the compilation and
+linking of all source files Ceedling finds in the specified source
+directories. At present only source files with a single (configurable)
+extension are recognized. That is, `*.c` and `*.cc` files will not
+both be recognized - only one or the other. See the configuration
+options and defaults in the documentation for the `:extension`
+sections of your configuration file (found later in this document).
+
+## Conventions for Test Files & Executable Test Fixtures
+
+Ceedling builds each individual test file with its accompanying
+source file(s) into a single, monolithic test fixture executable.
+
+### Test File Naming Convention
+
+Ceedling recognizes test files by a naming convention — a (configurable)
+prefix such as "`test_`" at the beginning of the file name with the same
+file extension as used by your C source files. See the configuration options
+and defaults in the documentation for the `:project` and `:extension`
+sections of your configuration file (elsewhere in this document).
+
+Depending on your configuration options, Ceedling can recognize
+a variety of test file naming patterns in your test search paths.
+For example, `test_some_super_functionality.c`, `TestYourSourceFile.cc`,
+or `testing_MyAwesomeCode.C` could each be valid test file
+names. Note, however, that Ceedling can recognize only one test
+file naming convention per project.
+
+### Conventions for Source and Mock Files to Be Compiled & Linked
+
+Ceedling knows what files to compile and link into each individual
+test executable by way of the `#include` list contained in each
+test file and optional test directive macros.
+
+The `#include` list directs Ceedling in two ways:
+
+1. Any C source files in the configured project directories
+ corresponding to `#include`d header files will be compiled and
+ linked into the resulting test fixture executable.
+1. If you are using mocks, header files with the appropriate
+ mocking prefix (e.g. `mock_foo.h`) direct Ceedling to find the
+ source header file (e.g. `foo.h`), generate a mock from it, and
+ compile & link that generated code into into the test executable
+ as well.
+
+Sometimes the source file you need to add to your test executable has
+no corresponding header file — e.g. `file_abc.h` contains symbols
+present in `file_xyz.c`. In these cases, you can use the test
+directive macro `TEST_SOURCE_FILE(...)` to tell Ceedling to compile
+and link the desired source file into the test executable (see
+macro documentation elsewhere in this doc).
+
+That was a lot of information and many clauses in a very few
+sentences; the commented example test file code that follows in a
+bit will make it clearer.
+
+### Convention for Test Case Functions + Test Runner Generation
+
+By naming your test functions according to convention, Ceedling
+will extract and collect into a generated test runner C file the
+appropriate calls to all your test case functions. This runner
+file handles all the execution minutiae so that your test file
+can be quite simple. As a bonus, you'll never forget to wire up
+a test function to be executed.
+
+In this generated runner lives the `main()` entry point for the
+resulting test executable. There are no configurable options for
+the naming convention of your test case functions.
+
+A test case function signature must have these elements:
+
+1. `void` return
+1. `void` parameter list
+1. A function name prepended with lowercase "`test`".
+
+In other words, a test function signature should look like this:
+`void test(void)`.
+
+### Preprocessing behavior for tests
+
+Ceedling and CMock are advanced tools that both perform fairly sophisticated
+parsing.
+
+However, neither of these tools fully understand the entire C language,
+especially C's preprocessing statements.
+
+If your test files rely on macros and `#ifdef` conditionals, there's a good
+chance that Ceedling will break on trying to process your test files, or,
+alternatively, your test suite will not execute as expected.
+
+Similarly, generating mocks of header files with macros and `#ifdef`
+conditionals can get weird.
+
+Ceedling includes an optional ability to preprocess test files and header files
+before executing any operations on them. See the `:project` ↳
+`:use_test_preprocessor`). That is, Ceedling will expand preprocessor
+statements in test files before generating test runners from them and will
+expand preprocessor statements in header files before generating mocks from
+them.
+
+This ability uses `gcc`'s preprocessing mode and the `cpp` preprocessor tool to
+strip down / expand test files and headers to their applicable content which
+can then be processed by Ceedling and CMock. They must be in your search path
+if Ceedling’s preprocessing is enabled. Further, Ceedling’s features are
+directly tied to these tools' abilities and options. They should not be
+redefined for other toolchains.
+
+### Execution time (duration) reporting in Ceedling operations & test suites
+
+#### Ceedling’s logged run times
+
+Ceedling logs two execution times for every project run.
+
+It first logs the set up time necessary to process your project file, parse code
+files, build an internal representation of your project, etc. This duration does
+not capture the time necessary to load the Ruby runtime itself.
+
+```
+🌱 Ceedling set up completed in 223 milliseconds
+```
+
+Secondly, each Ceedling run also logs the time necessary to run all the tasks
+you specify at the command line.
+
+```
+🌱 Ceedling operations completed in 1.03 seconds
+```
+
+#### Ceedling test suite and Unity test executable run durations
+
+A test suite comprises one or more Unity test executables (see
+[Anatomy of a Test Suite][anatomy-test-suite]). Ceedling times indvidual Unity
+test executable run durations. It also sums these into a total test suite
+execution time. These duration values are typically used in generating test
+reports via plugins.
+
+Not all test report formats utilize duration values. For those that do, some
+effort is usually required to map Ceedling duration values to a relevant test
+suite abstraction within a given test report format.
+
+Because Ceedling can execute builds with multiple threads, care must be taken
+to interpret test suite duration values — particularly in relation to
+Ceedling’s logged run times.
+
+In a multi-threaded build it's quite common for the logged Ceedling project run
+time to be less than the total suite time in a test report. In multi-threaded
+builds on multi-core machines, test executables are run on different processors
+simultaneously. As such, the total on-processor time in a test report can
+exceed the operation time Ceedling itself logs to the console. Further, because
+multi-threading tends to introduce context switching and processor scheduling
+overhead, the run duration of a test executable may be reported as longer than
+a in a comparable single-threaded build.
+
+[anatomy-test-suite]: #anatomy-of-a-test-suite
+
+#### Unity test case run times
+
+Individual test case exection time tracking is specifically a [Unity] feature
+(see its documentation for more details). If enabled and if your platform
+supports the time mechanism Unity relies on, Ceedling will automatically
+collect test case time values — generally made use of by test report plugins.
+
+To enable test case duration measurements, they must be enabled as a Unity
+compilation option. Add `UNITY_INCLUDE_EXEC_TIME` to Unity's compilation
+symbols (`:unity` ↳ `:defines`) in your Ceedling project file (see example
+below). Unity test case durations as reported by Ceedling default to 0 if the
+compilation option is not set.
+
+```yaml
+:unity:
+ :defines:
+ - UNITY_INCLUDE_EXEC_TIME
+```
+
+_Note:_ Most test cases are quite short, and most computers are quite fast. As
+ such, Unity test case execution time is often reported as 0 milliseconds as
+ the CPU execution time for a test case typically remains in the microseconds
+ range. Unity would require special rigging that is inconsistently available
+ across platforms to measure test case durations at a finer resolution.
+
+## The Magic of Dependency Tracking
+
+Previous versions of Ceedling used features of Rake to offer
+various kinds of smart rebuilds--that is, only regenerating files,
+recompiling code files, or relinking executables when changes within
+the project had occurred since the last build. Optional Ceedling
+features discovered “deep dependencies” such that, for example, a
+change in a header file several nested layers deep in `#include`
+statements would cause all the correct test executables to be
+updated and run.
+
+These features have been temporarily disabled and/or removed for
+test suites and remain in limited form for release build while
+Ceedling undergoes a major overhaul.
+
+Please see the [Release Notes](ReleaseNotes.md).
+
+### Notes on (Not So) Smart Rebuids
+
+* New features that are a part of the Ceedling overhaul can
+ significantly speed up test suite execution and release builds
+ despite the present behavior of brute force running all build
+ steps. See the discussion of enabling multi-threaded builds in
+ later sections.
+
+* When smart rebuilds return, they will further speed up builds as
+ will other planned optimizations.
+
+## Ceedling’s Build Output (Files, That Is)
+
+Ceedling requires a top-level build directory for all the stuff
+that it, the accompanying test tools, and your toolchain generate.
+That build directory's location is configured in the top-level
+`:project` section of your configuration file (discussed later). There
+can be a ton of generated files. By and large, you can live a full
+and meaningful life knowing absolutely nothing at all about
+the files and directories generated below the root build directory.
+
+As noted already, it's good practice to add your top-level build
+directory to source control but nothing generated beneath it.
+You'll spare yourself headache if you let Ceedling delete and
+regenerate files and directories in a non-versioned corner
+of your project's filesystem beneath the top-level build directory.
+
+The `artifacts/` directory is the one and only directory you may
+want to know about beneath the top-level build directory. The
+subdirectories beneath `artifacts` will hold your binary release
+target output (if your project is configured for release builds)
+and will serve as the conventional location for plugin output.
+This directory structure was chosen specifically because it
+tends to work nicely with Continuous Integration setups that
recognize and list build artifacts for retrieval / download.
-The Almighty Project Configuration File (in Glorious YAML)
-----------------------------------------------------------
+## Build _Errors_ vs. Test _Failures_. Oh, and Exit Codes.
+
+### Errors vs. Failures
+
+Ceedling will run a specified build until an **_error_**. An error
+refers to a build step encountering an unrecoverable problem. Files
+not found, nonexistent paths, compilation errors, missing symbols,
+plugin exceptions, etc. are all errors that will cause Ceedling
+to immediately end a build.
+
+A **_failure_** refers to a test failure. That is, an assertion of
+an expected versus actual value failed within a unit test case.
+A test failure will not stop a build. Instead, the suite will run
+to completion with test failures collected and reported along with
+all test case statistics.
+
+### Ceedling Exit Codes
+
+In its default configuration, Ceedling terminates with an exit code
+of `1`:
+
+ * On any build error and immediately terminates upon that build
+ error.
+ * On any test case failure but runs the build to completion and
+ shuts down normally.
+
+This behavior can be especially handy in Continuous Integration
+environments where you typically want an automated CI build to break
+upon either build errors or test failures.
+
+If this exit code convention for test failures does not work for you,
+no problem-o. You may be of the mind that running a test suite to
+completion should yield a successful exit code (even if tests failed).
+Add the following to your project file to force Ceedling to finish a
+build with an exit code of 0 even upon test case failures.
+
+```yaml
+# Ceedling terminates with happy `exit(0)` even if test cases fail
+:test_build:
+ :graceful_fail: true
+```
+
+If you use the option for graceful failures in CI, you'll want to
+rig up some kind of logging monitor that scans Ceedling’s test
+summary report sent to `$stdout` and/or a log file. Otherwise, you
+could have a successful build but failing tests.
+
+### Notes on Unity Test Executable Exit Codes
+
+Ceedling works by collecting multiple Unity test executables together
+into a test suite ([more here](#anatomy-of-a-test-suite).
+
+A Unity test executable's exit code is the number of failed tests. An
+exit code of `0` means all tests passed while anything larger than zero
+is the number of test failures.
+
+Because of platform limitations on how big an exit code number can be
+and because of the logical complexities of distinguishing test failure
+counts from build errors or plugin problems, Ceedling conforms to a
+much simpler exit code convention than Unity: `0` = 🙂 while `1` = ☹️.
+
+
+
+# Using Unity, CMock & CException
+
+If you jumped ahead to this section but do not follow some of the
+lingo here, please jump back to an [earlier section for definitions
+and helpful links][helpful-definitions].
+
+[helpful-definitions]: #hold-on-back-up-ruby-rake-yaml-unity-cmock-cexception
+
+## An overview of how Ceedling supports, well, its supporting frameworks
+
+If you are using Ceedling for unit testing, this means you are using Unity,
+the C testing framework. Unity is fully built-in and enabled for test builds.
+It cannot be disabled.
+
+If you want to use mocks in your test cases, then you'll need to configure CMock.
+CMock is fully supported by Ceedling, enabled by default, but generally requires
+some set up for your project's needs.
+
+If you are incorporating CException into your release artifact, you'll need to
+both enable it and configure it. Enabling CException makes it available in
+both release builds and test builds.
+
+This section provides a high-level view of how the various tools become
+part of your builds and fit into Ceedling’s configuration file. Ceedling’s
+configuration file is discussed in detail in the next section.
+
+See [Unity], [CMock], and [CException]'s project documentation for all
+your configuration options. Ceedling offers facilities for providing these
+frameworks their compilation and configuration settings. Discussing
+these tools and all their options in detail is beyond the scope of Ceedling
+documentation.
+
+## Unity Configuration
+
+Unity is wholly compiled C code. As such, its configuration is entirely
+controlled by a variety of compilation symbols. These can be configured
+in Ceedling’s `:unity` project settings.
+
+### Example Unity configurations
+
+#### Itty bitty processor & toolchain with limited test execution options
+
+```yaml
+:unity:
+ :defines:
+ - UNITY_INT_WIDTH=16 # 16 bit processor without support for 32 bit instructions
+ - UNITY_EXCLUDE_FLOAT # No floating point unit
+```
+
+#### Great big gorilla processor that grunts and scratches
+
+```yaml
+:unity:
+ :defines:
+ - UNITY_SUPPORT_64 # Big memory, big counters, big registers
+ - UNITY_LINE_TYPE=\"unsigned int\" # Apparently, we’re writing lengthy test files,
+ - UNITY_COUNTER_TYPE=\"unsigned int\" # and we've got a ton of test cases in those test files
+ - UNITY_FLOAT_TYPE=\"double\" # You betcha
+```
+
+#### Example Unity configuration header file
+
+Sometimes, you may want to funnel all Unity configuration options into a
+header file rather than organize a lengthy `:unity` ↳ `:defines` list. Perhaps your
+symbol definitions include characters needing escape sequences in YAML that are
+driving you bonkers.
+
+```yaml
+:unity:
+ :defines:
+ - UNITY_INCLUDE_CONFIG_H
+```
+
+```c
+// unity_config.h
+#ifndef UNITY_CONFIG_H
+#define UNITY_CONFIG_H
+
+#include "uart_output.h" // Helper library for your custom environment
+
+#define UNITY_INT_WIDTH 16
+#define UNITY_OUTPUT_START() uart_init(F_CPU, BAUD) // Helper function to init UART
+#define UNITY_OUTPUT_CHAR(a) uart_putchar(a) // Helper function to forward char via UART
+#define UNITY_OUTPUT_COMPLETE() uart_complete() // Helper function to inform that test has ended
+
+#endif
+```
+
+### Routing Unity’s report output
+
+Unity defaults to using `putchar()` from C's standard library to
+display test results.
+
+For more exotic environments than a desktop with a terminal — e.g.
+running tests directly on a non-PC target — you have options.
+
+For instance, you could create a routine that transmits a character via
+RS232 or USB. Once you have that routine, you can replace `putchar()`
+calls in Unity by overriding the function-like macro `UNITY_OUTPUT_CHAR`.
+
+Even though this override can also be defined in Ceedling YAML, most
+shell environments do not handle parentheses as command line arguments
+very well. Consult your toolchain and shell documentation.
+
+If redefining the function and macros breaks your command line
+compilation, all necessary options and functionality can be defined in
+`unity_config.h`. Unity will need the `UNITY_INCLUDE_CONFIG_H` symbol in the
+`:unity` ↳ `:defines` list of your Ceedling project file (see example above).
+
+## CMock Configuration
+
+CMock is enabled in Ceedling by default. However, no part of it enters a
+test build unless mock generation is triggered in your test files.
+Triggering mock generation is done by an `#include` convention. See the
+section on [Ceedling conventions and behaviors][conventions] for more.
+
+You are welcome to disable CMock in the `:project` block of your Ceedling
+configuration file. This is typically only useful in special debugging
+scenarios or for Ceedling development itself.
+
+[conventions]: #important-conventions--behaviors
+
+CMock is a mixture of Ruby and C code. CMock's Ruby components generate
+C code for your unit tests. CMock's base C code is compiled and linked into
+a test executable in the same way that any C file is — including Unity,
+CException, and generated mock C code, for that matter.
+
+CMock's code generation can be configured using YAML similar to Ceedling
+itself. Ceedling’s project file is something of a container for CMock's
+YAML configuration (Ceedling also uses CMock's configuration, though).
+
+See the documentation for the top-level [`:cmock`][cmock-yaml-config]
+section within Ceedling’s project file.
+
+[cmock-yaml-config]: #cmock-configure-cmocks-code-generation--compilation
+
+Like Unity and CException, CMock's C components are configured at
+compilation with symbols managed in your Ceedling project file's
+`:cmock` ↳ `:defines` section.
+
+### Example CMock configurations
+
+```yaml
+:project:
+ # Shown for completeness -- CMock enabled by default in Ceedling
+ :use_mocks: TRUE
+
+:cmock:
+ :when_no_prototypes: :warn
+ :enforce_strict_ordering: TRUE
+ :defines:
+ # Memory alignment (packing) on 16 bit boundaries
+ - CMOCK_MEM_ALIGN=1
+ :plugins:
+ - :ignore
+ :treat_as:
+ uint8: HEX8
+ uint16: HEX16
+ uint32: UINT32
+ int8: INT8
+ bool: UINT8
+```
+
+## CException Configuration
+
+Like Unity, CException is wholly compiled C code. As such, its
+configuration is entirely controlled by a variety of `#define` symbols.
+These can be configured in Ceedling’s `:cexception` ↳ `:defines` project
+settings.
+
+Unlike Unity which is always available in test builds and CMock that
+defaults to available in test builds, CException must be enabled
+if you wish to use it in your project.
+
+### Example CException configurations
+
+```yaml
+:project:
+ # Enable CException for both test and release builds
+ :use_exceptions: TRUE
+
+:cexception:
+ :defines:
+ # Possible exception codes of -127 to +127
+ - CEXCEPTION_T='signed char'
+
+```
+
+
+
+# How to Load a Project Configuration. You Have Options, My Friend.
+
+Ceedling needs a project configuration to accomplish anything for you.
+Ceedling's project configuration is a large in-memory data structure.
+That data structure is loaded from a human-readable file format called
+YAML.
+
+The next section details Ceedling’s project configuration options in
+YAML. This section explains all your options for loading and modifying
+project configuration from files to begin with.
+
+## Overview of Project Configuration Loading & Smooshing
+
+Ceedling has a certain pipeline for loading and manipulating the
+configuration it uses to build your projects. It goes something like
+this:
+
+1. Load the base project configuration from a YAML file.
+1. Merge the base configuration with zero or more Mixins from YAML files.
+1. Load zero or more plugins that alter or merge additional configuration.
+1. Merge in default values to ensure all necessary configuration to run
+ is present.
+
+Ceedling provides reasonably verbose logging at startup telling you which
+configuration files were used and in what order they were merged.
+
+## Options for Loading Your Base Project Configuration
+
+You have three options for telling Ceedling what single base project
+configuration to load. These options are ordered below according to their
+precedence. If an option higher in the list is present, it is used.
+
+1. Command line option flags
+1. Environment variable
+1. Default file in working directory
+
+### `--project` command line flags
+
+Many of Ceedling's [application commands][packet-section-7] include an
+optional `--project` flag. When provided, Ceedling will load as its base
+configuration the YAML filepath provided.
+
+Example: `ceedling --project=my/path/build.yml test:all`
+
+_Note:_ Ceedling loads any relative paths within your configuration in
+relation to your working directory. This can cause a disconnect between
+configuration paths, working directory, and the path to your project
+file.
+
+If the filepath does not exist, Ceedling terminates with an error.
+
+### Environment variable `CEEDLING_PROJECT_FILE`
+
+If a `--project` flag is not used at the command line, but the
+environment variable `CEEDLING_PROJECT_FILE` is set, Ceedling will use
+the path it contains to load your project configuration. The path can
+be absolute or relative (to your working directory).
+
+If the filepath does not exist, Ceedling terminates with an error.
+
+### Default _project.yml_ in your working directory
+
+If neither a `--project` command line flag nor the environment variable
+`CEEDLING_PROJECT_FILE` are set, then Ceedling tries to load a file
+named _project.yml_ in your working directory.
+
+If this file does not exist, Ceedling terminates with an error.
+
+## Applying Mixins to Your Base Project Configuration
+
+Once you have a base configuation loaded, you may want to modify it for
+any number of reasons. Some example scenarios:
+
+* A single project actually contains mutiple build variations. You would
+ like to maintain a common configuration that is shared among build
+ variations.
+* Your repository contains the configuration needed by your Continuous
+ Integration server setup, but this is not fun to run locally. You would
+ like to modify the configuration locally with sources external to your
+ repository.
+* Ceedling's default `gcc` tools do not work for your project needs. You
+ would like the complex tooling configurations you most often need to
+ be maintained separately and shared among projects.
+
+Mixins allow you to merge configuration with your project configuration
+just after the base project file is loaded. The merge is so low-level
+and generic that you can, in fact, load an empty base configuration
+and merge in entire project configurations through mixins.
+
+## Mixins Example Plus Merging Rules
+
+Let’s start with an example that also explains how mixins are merged.
+Then, the documentation sections that follow will discuss everything
+in detail.
+
+### Mixins Example: Scenario
+
+In this example, we will load a base project configuration and then
+apply three mixins using each of the available means — command line,
+envionment variable, and `:mixins` section in the base project
+configuration file.
+
+#### Example environment variable
+
+`CEEDLING_MIXIN_1` = `./env.yml`
+
+#### Example command line
+
+`ceedling --project=base.yml --mixin=support/mixins/cmdline.yml `
+
+_Note:_ The `--mixin` flag supports more than filepaths and can be used
+multiple times in the same command line for multiple mixins (see later
+documentation section).
+
+The example command line above will produce the following logging output.
+
+```
+🌱 Loaded project configuration from command line argument using base.yml
+ + Merged command line mixin using support/mixins/cmdline.yml
+ + Merged CEEDLING_MIXIN_1 mixin using ./env.yml
+ + Merged project configuration mixin using ./enabled.yml
+```
+
+_Notes_
+
+* The logging output above referencing _enabled.yml_ comes from the
+ `:mixins` section within the base project configuration file provided below.
+* The resulting configuration in this example is missing settings required
+ by Ceedling. This will cause a validation build error that is not shown
+ here.
+
+### Mixins Example: Configuration files
+
+#### _base.yml_ — Our base project configuration file
+
+Our base project configuration file:
+
+1. Sets up a configuration file-baesd mixin. Ceedling will look for a mixin
+ named _enabled_ in the specified load paths. In this simple configuration
+ that means Ceedling looks for and merges _support/mixins/enabled.yml_.
+1. Creates a `:project` section in our configuration.
+1. Creates a `:plugins` section in our configuration and enables the standard
+ console test report output plugin.
+
+```yaml
+:mixins: # `:mixins` section only recognized in base project configuration
+ :enabled: # `:enabled` list supports names and filepaths
+ - enabled # Ceedling looks for name as enabled.yml in load paths and merges if found
+ :load_paths:
+ - support/mixins
+
+:project:
+ :build_root: build/
+
+:plugins:
+ :enabled:
+ - report_tests_pretty_stdout
+```
+
+#### _support/mixins/cmdline.yml_ — Mixin via command line filepath flag
+
+This mixin will merge a `:project` section with the existing `:project`
+section from the base project file per the deep merge rules (noted after
+the examples).
+
+```yaml
+:project:
+ :use_test_preprocessor: TRUE
+ :test_file_prefix: Test
+```
+
+#### _env.yml_ — Mixin via environment variable filepath
+
+This mixin will merge a `:plugins` section with the existing `:plugins`
+section from the base project file per the deep merge rules (noted
+after the examples).
+
+```yaml
+:plugins:
+ :enabled:
+ - compile_commands_json_db
+```
+
+#### _support/mixins/enabled.yml_ — Mixin via base project configuration file `:mixins` section
+
+This mixin listed in the base configuration project file will merge
+`:project` and `:plugins` sections with those that already exist from
+the base configuration plus earlier mixin merges per the deep merge
+rules (noted after the examples).
+
+```yaml
+:project:
+ :use_test_preprocessor: FALSE
+
+:plugins:
+ :enabled:
+ - gcov
+```
+
+### Mixins Example: Resulting project configuration
+
+Behold the project configuration following mixin merges:
+
+```yaml
+:project:
+ :build_root: build/ # From base.yml
+ :use_test_preprocessor: TRUE # Value in support/mixins/cmdline.yml overwrote value from support/mixins/enabled.yml
+ :test_file_prefix: Test # Added to :project from support/mixins/cmdline.yml
+
+:plugins:
+ :enabled: # :plugins ↳ :enabled from two mixins merged with oringal list in base.yml
+ - report_tests_pretty_stdout # From base.yml
+ - compile_commands_json_db # From env.yml
+ - gcov # From support/mixins/enabled.yml
+
+# Note: Original :mixins section is filtered out of resulting config
+```
+
+### Mixins deep merge rules
+
+Mixins are merged in a specific order. See the next documentation
+sections for details.
+
+Smooshing of mixin configurations into the base project configuration
+follows a few basic rules:
+
+* If a configuration key/value pair does not already exist at the time
+ of merging, it is added to the configuration.
+* If a simple value — e.g. boolean, string, numeric — already exists
+ at the time of merging, that value is replaced by the value being
+ merged in.
+* If a container — e.g. list or hash — already exists at the time of a
+ merge, the contents are _combined_. In the case of lists, merged
+ values are added to the end of the existing list.
+
+## Options for Loading Mixins
+
+You have three options for telling Ceedling what mixins to load. These
+options are ordered below according to their precedence. A Mixin higher
+in the list is merged earlier. In addition, options higher in the list
+force duplicate mixin filepaths to be ignored lower in the list.
+
+Unlike base project file loading that resolves to a single filepath,
+multiple mixins can be specified using any or all of these options.
+
+1. Command line option flags
+1. Environment variables
+1. Base project configuration file entries
+
+### `--mixin` command line flags
+
+As already discussed above, many of Ceedling's application commands
+include an optional `--project` flag. Most of these same commands
+also recognize optional `--mixin` flags. Note that `--mixin` can be
+used multiple times in a single command line.
+
+When provided, Ceedling will load the specified YAML file and merge
+it with the base project configuration.
+
+A Mixin flag can contain one of two types of values:
+
+1. A filename or filepath to a mixin yaml file. A filename contains
+ a file extension. A filepath includes a leading directory path.
+1. A simple name (no file extension and no path). This name is used
+ as a lookup in Ceedling's mixin load paths.
+
+Example: `ceedling --project=build.yml --mixin=foo --mixin=bar/mixin.yaml test:all`
+
+Simple mixin names (#2 above) require mixin load paths to search.
+A default mixin load path is always in the list and points to within
+Ceedling itself (in order to host eventual built-in mixins like
+built-in plugins). User-specified load paths must be added through
+the `:mixins` section of the base configuration project file. See
+the [documentation for the `:mixins` section of your project
+configuration][mixins-config-section] for more details.
+
+Order of precedence is set by the command line mixin order
+left-to-right.
+
+Filepaths may be relative (in relation to the working directory) or
+absolute.
+
+If the `--mixin` filename or filepath does not exist, Ceedling
+terminates with an error. If Ceedling cannot find a mixin name in
+any load paths, it terminates with an error.
+
+[mixins-config-section]: #base-project-configuration-file-mixins-section-entries
+
+### Mixin environment variables
+
+Mixins can also be loaded through environment variables. Ceedling
+recognizes environment variables with a naming scheme of
+`CEEDLING_MIXIN_#`, where `#` is any number greater than 0.
+
+Precedence among the environment variables is a simple ascending
+sort of the trailing numeric value in the environment variable name.
+For example, `CEEDLING_MIXIN_5` will be merged before
+`CEEDLING_MIXIN_99`.
+
+Mixin environment variables only hold filepaths. Filepaths may be
+relative (in relation to the working directory) or absolute.
+
+If the filepath specified by an environment variable does not exist,
+Ceedling terminates with an error.
+
+### Base project configuration file `:mixins` section entries
+
+Ceedling only recognizes a `:mixins` section in your base project
+configuration file. A `:mixins` section in a mixin is ignored. In addition,
+the `:mixins` section of a base project configuration file is filtered
+out of the resulting configuration.
+
+The `:mixins` configuration section can contain up to two subsections.
+Each subsection is optional.
+
+* `:enabled`
+
+ An optional array comprising (A) mixin filenames/filepaths and/or
+ (B) simple mixin names.
+
+ 1. A filename contains a file extension. A filepath includes a
+ directory path. The file content is YAML.
+ 1. A simple name (no file extension and no path) is used
+ as a file lookup among any configured load paths (see next
+ section) and as a lookup name among Ceedling’s built-in mixins
+ (currently none).
+
+ Enabled entries support [inline Ruby string expansion][inline-ruby-string-expansion].
+
+ **Default**: `[]`
+
+* `:load_paths`
+
+ Paths containing mixin files to be searched via mixin names. A mixin
+ filename in a load path has the form _.yml_ by default. If
+ an alternate filename extension has been specified in your project
+ configuration (`:extension` ↳ `:yaml`) it will be used for file
+ lookups in the mixin load paths instead of _.yml_.
+
+ Searches start in the path at the top of the list.
+
+ Both mixin names in the `:enabled` list (above) and on the command
+ line via `--mixin` flag use this list of load paths for searches.
+
+ Load paths entries support [inline Ruby string expansion][inline-ruby-string-expansion].
+
+ **Default**: `[]`
+
+Example `:mixins` YAML blurb:
+
+```yaml
+:mixins:
+ :enabled:
+ - foo # Search for foo.yml in proj/mixins & support/ and 'foo' among built-in mixins
+ - path/bar.yaml # Merge this file with base project conig
+ :load_paths:
+ - proj/mixins
+ - support
+```
+
+Relating the above example to command line `--mixin` flag handling:
+
+* A command line flag of `--mixin=foo` is equivalent to the `foo`
+ entry in the `:enabled` mixin configuration.
+* A command line flag of `--mixin=path/bar.yaml` is equivalent to the
+ `path/bar.yaml` entry in the `:enabled` mixin configuration.
+* Note that while command line `--mixin` flags work identically to
+ entries in `:mixins` ↳ `:enabled`, they are merged first instead of
+ last in the mixin precedence.
+
+
+
+# The Almighty Ceedling Project Configuration File (in Glorious YAML)
+
+## Some YAML Learnin’
+
+Please consult YAML documentation for the finer points of format
+and to understand details of our YAML-based configuration file.
+
+We recommend [Wikipedia's entry on YAML](http://en.wikipedia.org/wiki/Yaml)
+for this. A few highlights from that reference page:
+
+* YAML streams are encoded using the set of printable Unicode
+ characters, either in UTF-8 or UTF-16.
+
+* White space indentation is used to denote structure; however,
+ tab characters are never allowed as indentation.
+
+* Comments begin with the number sign (`#`), can start anywhere
+ on a line, and continue until the end of the line unless enclosed
+ by quotes.
+
+* List members are denoted by a leading hyphen (`-`) with one member
+ per line, or enclosed in square brackets (`[...]`) and separated
+ by comma space (`, `).
+
+* Hashes are represented using colon space (`: `) in the form
+ `key: value`, either one per line or enclosed in curly braces
+ (`{...}`) and separated by comma space (`, `).
+
+* Strings (scalars) are ordinarily unquoted, but may be enclosed
+ in double-quotes (`"`), or single-quotes (`'`).
+
+* YAML requires that colons and commas used as list separators
+ be followed by a space so that scalar values containing embedded
+ punctuation can generally be represented without needing
+ to be enclosed in quotes.
+
+* Repeated nodes are initially denoted by an ampersand (`&`) and
+ thereafter referenced with an asterisk (`*`). These are known as
+ anchors and aliases in YAML speak.
+
+## Notes on Project File Structure and Documentation That Follows
+
+* Each of the following sections represent top-level entries
+ in the YAML configuration file. Top-level means the named entries
+ are furthest to the left in the hierarchical configuration file
+ (not at the literal top of the file).
+
+* Unless explicitly specified in the configuration file by you,
+ Ceedling uses default values for settings.
+
+* At minimum, these settings must be specified for a test suite:
+ * `:project` ↳ `:build_root`
+ * `:paths` ↳ `:source`
+ * `:paths` ↳ `:test`
+ * `:paths` ↳ `:include` and/or use of `TEST_INCLUDE_PATH(...)`
+ build directive macro within your test files
+
+* At minimum, these settings must be specified for a release build:
+ * `:project` ↳ `:build_root`
+ * `:paths` ↳ `:source`
+
+* As much as is possible, Ceedling validates your settings in
+ properly formed YAML.
+
+* Improperly formed YAML will cause a Ruby error when the YAML
+ is parsed. This is usually accompanied by a complaint with
+ line and column number pointing into the project file.
+
+* Certain advanced features rely on `gcc` and `cpp` as preprocessing
+ tools. In most Linux systems, these tools are already available.
+ For Windows environments, we recommend the [MinGW] project
+ (Minimalist GNU for Windows).
+
+* Ceedling is primarily meant as a build tool to support automated
+ unit testing. All the heavy lifting is involved there. Creating
+ a simple binary release build artifact is quite trivial in
+ comparison. Consequently, most default options and the construction
+ of Ceedling itself is skewed towards supporting testing, though
+ Ceedling can, of course, build your binary release artifact
+ as well. Note that some complex binary release builds are beyond
+ Ceedling’s abilities. See the Ceedling plugin [subprojects] for
+ extending release build abilities.
+
+[MinGW]: http://www.mingw.org/
+
+## Ceedling-specific YAML Handling & Conventions
+
+### Inline Ruby string expansion
+
+Ceedling is able to execute inline Ruby string substitution code within the
+entries of certain project file configuration elements.
+
+This evaluation occurs when the project file is loaded and processed into a
+data structure for use by the Ceedling application.
+
+_Note:_ One good option for validating and troubleshooting inline Ruby string
+exapnsion is use of `ceedling dumpconfig` at the command line. This application
+command causes your project configuration to be processed and written to a
+YAML file with any inline Ruby string expansions, well, expanded along with
+defaults set, plugin actions applied, etc.
+
+#### Ruby string expansion syntax
+
+To exapnd the string result of Ruby code within a configuration value string,
+wrap the Ruby code in the substitution pattern `#{…}`.
+
+Inline Ruby string expansion may constitute the entirety of a configuration
+value string, may be embedded within a string, or may be used multiple times
+within a string.
+
+Because of the `#` it’s a good idea to wrap any string values in your YAML that
+rely on this feature with quotation marks. Quotation marks for YAML strings are
+optional. However, the `#` can cause a YAML parser to see a comment. As such,
+explicitly indicating a string to the YAML parser with enclosing quotation
+marks alleviates this problem.
+
+#### Ruby string expansion example
+
+```yaml
+:some_config_section:
+ :some_key:
+ - "My env string #{ENV['VAR1']}"
+ - "My utility result string #{`util --arg`.strip()}"
+```
+
+In the example above, the two YAML strings will include the strings returned by
+the Ruby code within `#{…}`:
+
+1. The first string uses Ruby’s environment variable lookup `ENV[…]` to fetch
+the value assigned to variable `VAR1`.
+1. The second string uses Ruby’s backtick shell execution ``…`` to insert the
+string generated by a command line utility.
+
+#### Project file sections that offer inline Ruby string expansion
+
+* `:mixins`
+* `:environment`
+* `:paths` plus any second tier configuration key name ending in `_path` or
+ `_paths`
+* `:flags`
+* `:defines`
+* `:tools`
+* `:release_build` ↳ `:artifacts`
+
+See each section’s documentation for details.
+
+[inline-ruby-string-expansion]: #inline-ruby-string-expansion
+
+### Path handling
+
+Any second tier setting keys anywhere in YAML whose names end in `_path` or
+`_paths` are automagically processed like all Ceedling-specific paths in the
+YAML to have consistent directory separators (i.e. `/`) and to take advantage
+of inline Ruby string expansion (see preceding section for details).
+
+## Let’s Be Careful Out There
+
+Ceedling performs validation of the values you set in your
+configuration file (this assumes your YAML is correct and will
+not fail format parsing, of course).
+
+That said, validation is limited to only those settings Ceedling
+uses and those that can be reasonably validated. Ceedling does
+not limit what can exist within your configuration file. In this
+way, you can take full advantage of YAML as well as add sections
+and values for use in your own custom plugins (documented later).
+
+The consequence of this is simple but important. A misspelled
+configuration section or value name is unlikely to cause Ceedling
+any trouble. Ceedling will happily process that section
+or value and simply use the properly spelled default maintained
+internally - thus leading to unexpected behavior without warning.
+
+## `:project`: Global project settings
+
+**_Note:_** In future versions of Ceedling, test and release build
+settings presently organized beneath `:project` will be renamed and
+migrated to the `:test_build` and `:release_build` sections.
+
+* `:build_root`
+
+ Top level directory into which generated path structure and files are
+ placed. Note: this is one of the handful of configuration values that
+ must be set. The specified path can be absolute or relative to your
+ working directory.
+
+ **Default**: (none)
+
+* `:default_tasks`
+
+ A list of default build / plugin tasks Ceedling should execute if
+ none are provided at the command line.
+
+ _Note:_ These are build & plugin tasks (e.g. `test:all` and `clobber`).
+ These are not application commands (e.g. `dumpconfig`) or command
+ line flags (e.g. `--verbosity`). See the documentation
+ [on using the command line][command-line] to understand the distinction
+ between application commands and build & plugin tasks.
+
+ Example YAML:
+ ```yaml
+ :project:
+ :default_tasks:
+ - clobber
+ - test:all
+ - release
+ ```
+ **Default**: ['test:all']
+
+ [command-line]: #now-what-how-do-i-make-it-go-the-command-line
+
+* `:use_mocks`
+
+ Configures the build environment to make use of CMock. Note that if
+ you do not use mocks, there's no harm in leaving this setting as its
+ default value.
+
+ **Default**: TRUE
+
+* `:use_test_preprocessor`
+
+ This option allows Ceedling to work with test files that contain
+ conditional compilation statements (e.g. `#ifdef`) and header files you
+ wish to mock that contain conditional preprocessor statements and/or
+ macros.
+
+ See the [documentation on test preprocessing][test-preprocessing] for more.
+
+ With this option enabled, the `gcc` & `cpp` tools must exist in an
+ accessible system search path.
+
+ [test-preprocessing]: #preprocessing-behavior-for-tests
+
+ **Default**: FALSE
+
+* `:test_file_prefix`
+
+ Ceedling collects test files by convention from within the test file
+ search paths. The convention includes a unique name prefix and a file
+ extension matching that of source files.
+
+ Why not simply recognize all files in test directories as test files?
+ By using the given convention, we have greater flexibility in what we
+ do with C files in the test directories.
+
+ **Default**: "test_"
+
+* `:release_build`
+
+ When enabled, a release Rake task is exposed. This configuration
+ option requires a corresponding release compiler and linker to be
+ defined (`gcc` is used as the default).
+
+ Ceedling is primarily concerned with facilitating the complicated
+ mechanics of automating unit tests. The same mechanisms are easily
+ capable of building a final release binary artifact (i.e. non test
+ code — the thing that is your final working software that you execute
+ on target hardware). That said, if you have complicated release
+ builds, you should consider a traditional build tool for these.
+ Ceedling shines at executing test suites.
+
+ More release configuration options are available in the `:release_build`
+ section.
+
+ **Default**: FALSE
+
+* `:compile_threads`
+
+ A value greater than one enables parallelized build steps. Ceedling
+ creates a number of threads up to `:compile_threads` for build steps.
+ These build steps execute batched operations including but not
+ limited to mock generation, code compilation, and running test
+ executables.
+
+ Particularly if your build system includes multiple cores, overall
+ build time will drop considerably as compared to running a build with
+ a single thread.
+
+ Tuning the number of threads for peak performance is an art more
+ than a science. A special value of `:auto` instructs Ceedling to
+ query the host system's number of virtual cores. To this value it
+ adds a constant of 4. This is often a good value sufficient to "max
+ out" available resources without overloading available resources.
+
+ `:compile_threads` is used for all release build steps and all test
+ suite build steps except for running the test executables that make
+ up a test suite. See next section for more.
+
+ **Default**: 1
+
+* `:test_threads`
+
+ The behavior of and values for `:test_threads` are identical to
+ `:compile_threads` with one exception.
+
+ `test_threads:` specifically controls the number of threads used to
+ run the test executables comprising a test suite.
+
+ Why the distinction from `:compile_threads`? Some test suite builds
+ rely not on native executables but simulators running cross-compiled
+ code. Some simulators are limited to running only a single instance at
+ a time. Thus, with this and the previous setting, it becomes possible
+ to parallelize nearly all of a test suite build while still respecting
+ the limits of certain simulators depended upon by test executables.
+
+ **Default**: 1
+
+* `:which_ceedling`
+
+ This is an advanced project option primarily meant for development work
+ on Ceedling itself. This setting tells the code that launches the
+ Ceedling application where to find the code to launch.
+
+ This entry can be either a directory path or `gem`.
+
+ See the section [Which Ceedling](#which_ceedling) for full details.
+
+ **Default**: `gem`
+
+### Example `:project` YAML blurb
+
+```yaml
+:project:
+ :build_root: project_awesome/build
+ :use_exceptions: FALSE
+ :use_test_preprocessor: TRUE
+ :options_paths:
+ - project/options
+ - external/shared/options
+ :release_build: TRUE
+ :compile_threads: :auto
+```
+
+* `:use_backtrace`
+
+ When a test executable encounters a ☠️ **Segmentation Fault** or other crash
+ condition, the executable immediately terminates and no further details for
+ test suite reporting are collected. By default, Ceedling reports a single
+ failure for the entire test file, noting that it crashed.
+
+ But, fear not. You can bring your dead unit tests back to life.
+
+ You have three options for this setting:
+
+ 1. `:none` is the default and will produce the test failure report described
+ above.
+ 1. `:simple` causes Ceedling to re-run each test case in the test executable
+ individually to identify and report the problematic test case(s).
+ 1. `:gdb` uses the [`gdb`][gdb] debugger to identify and report the
+ troublesome line of code triggering the crash. If this option is enabled,
+ but `gdb` is not available to Ceedling, project validation will terminate
+ with an error at startup.
+
+ May 17, 2024: While `:simple` is a recognized value only the `:none` and
+ `:gdb` options are currently implemented.
+
+ **Default**: :none
+
+ [gdb]: https://www.sourceware.org/gdb/
+
+## `:mixins` Configuring mixins to merge
+
+This section of a project configuration file is documented in the
+[discussion of project files and mixins][mixins-config-section].
+
+**_Notes:_**
+
+* A `:mixins` section is only recognized within a base project configuration
+ file. Any `:mixins` sections within mixin files are ignored.
+* A `:mixins` section in a Ceedling configuration is entirely filtered out of
+ the resulting configuration. That is, it is unavailable for use by plugins
+ and will not be present in any output from `ceedling dumpconfig`.
+* A `:mixins` section supports [inline Ruby string expansion][inline-ruby-string-expansion].
+ See the full documetation on Mixins for details.
+
+## `:test_build` Configuring a test build
+
+**_Note:_** In future versions of Ceedling, test-related settings presently
+organized beneath `:project` will be renamed and migrated to this section.
+
+* `:use_assembly`
+
+ This option causes Ceedling to enable an assembler tool and collect a
+ list of assembly file sources for use in a test suite build.
+
+ The default assembler is the GNU tool `as`; it may be overridden in
+ the `:tools` section.
+
+ In order to inject assembly code into the build of a test executable,
+ two conditions must be true:
+
+ 1. The assembly files must be visible to Ceedling by way of `:paths` and
+ `:extension` settings for assembly files.
+ 1. Ceedling must be told into which test executable build to insert a
+ given assembly file. The easiest way to do so is with the
+ `TEST_SOURCE_FILE()` build directive macro (documented in a later section).
+
+ **Default**: FALSE
+
+### Example `:test_build` YAML blurb
+
+```yaml
+:test_build:
+ :use_assembly: TRUE
+```
+
+## `:release_build` Configuring a release build
+
+**_Note:_** In future versions of Ceedling, release build-related settings
+presently organized beneath `:project` will be renamed and migrated to
+this section.
+
+* `:output`
+
+ The name of your release build binary artifact to be found in /artifacts/release. Ceedling sets the default artifact file
+ extension to that as is explicitly specified in the `:extension`
+ section or as is system specific otherwise.
+
+ **Default**: `project.exe` or `project.out`
+
+* `:use_assembly`
+
+ This option causes Ceedling to enable an assembler tool and add any
+ assembly code present in the project to the release artifact's build.
+
+ The default assembler is the GNU tool `as`; it may be overridden
+ in the `:tools` section.
+
+ The assembly files must be visible to Ceedling by way of `:paths` and
+ `:extension` settings for assembly files.
+
+ **Default**: FALSE
+
+* `:artifacts`
+
+ By default, Ceedling copies to the _/artifacts/release_
+ directory the output of the release linker and (optionally) a map
+ file. Many toolchains produce other important output files as well.
+ Adding a file path to this list will cause Ceedling to copy that file
+ to the artifacts directory.
+
+ The artifacts directory is helpful for organizing important build
+ output files and provides a central place for tools such as Continuous
+ Integration servers to point to build output. Selectively copying
+ files prevents incidental build cruft from needlessly appearing in the
+ artifacts directory.
+
+ Note that [inline Ruby string expansion][inline-ruby-string-expansion]
+ is available in artifact paths.
+
+ **Default**: `[]` (empty)
+
+### Example `:release_build` YAML blurb
+
+```yaml
+:release_build:
+ :output: top_secret.bin
+ :use_assembly: TRUE
+ :artifacts:
+ - build/release/out/c/top_secret.s19
+```
+
+## Project `:paths` configuration
+
+**Paths for build tools and building file collections**
+
+Ceedling relies on various path and file collections to do its work. File
+collections are automagically assembled from paths, matching globs / wildcards,
+and file extensions (see project configuration `:extension`).
+
+Entries in `:paths` help create directory-based bulk file collections. The
+`:files` configuration section is available for filepath-oriented tailoring of
+these buk file collections.
+
+Entries in `:paths` ↳ `:include` also specify search paths for header files.
+
+All of the configuration subsections that follow default to empty lists. In
+YAML, list items can be comma separated within brackets or organized per line
+with a dash. An empty list can only be denoted as `[]`. Typically, you will see
+Ceedling project files use lists broken up per line.
+
+```yaml
+:paths:
+ :support: [] # Empty list (internal default)
+ :source:
+ - files/code # Typical list format
+
+```
+
+Examples that illustrate the many `:paths` entry features follow all
+the various path-related documentation sections.
+
+* :paths
↳ :test
+
+ All C files containing unit test code. Note: this is one of the
+ handful of configuration values that must be set for a test suite.
+
+ **Default**: `[]` (empty)
+
+* :paths
↳ :source
+
+ All C files containing release code (code to be tested)
+
+ Note: this is one of the handful of configuration values that must
+ be set for either a release build or test suite.
+
+ **Default**: `[]` (empty)
+
+* :paths
↳ :support
+
+ Any C files you might need to aid your unit testing. For example, on
+ occasion, you may need to create a header file containing a subset of
+ function signatures matching those elsewhere in your code (e.g. a
+ subset of your OS functions, a portion of a library API, etc.). Why?
+ To provide finer grained control over mock function substitution or
+ limiting the size of the generated mocks.
+
+ **Default**: `[]` (empty)
+
+* :paths
↳ :include
+
+ See these two important discussions to fully understand your options
+ for header file search paths:
+
+ * [Configuring Your Header File Search Paths][header-file-search-paths]
+ * [`TEST_INCLUDE_PATH(...)` build directive macro][test-include-path-macro]
+
+ [header-file-search-paths]: #configuring-your-header-file-search-paths
+ [test-include-path-macro]: #test_include_path
+
+ This set of paths specifies the locations of your header files. If
+ your header files are intermixed with source files, you must duplicate
+ some or all of your `:paths` ↳ `:source` entries here.
+
+ In its simplest use, your include paths list can be exhaustive.
+ That is, you list all path locations where your project's header files
+ reside in this configuration list.
+
+ However, if you have a complex project or many, many include paths that
+ create problematically long search paths at the compilation command
+ line, you may treat your `:paths` ↳ `:include` list as a base, common
+ list. Having established that base list, you can then extend it on a
+ test-by-test basis with use of the `TEST_INCLUDE_PATH(...)` build
+ directive macro in your test files.
+
+ **Default**: `[]` (empty)
+
+* :paths
↳ :test_toolchain_include
+
+ System header files needed by the test toolchain - should your
+ compiler be unable to find them, finds the wrong system include search
+ path, or you need a creative solution to a tricky technical problem.
+
+ Note that if you configure your own toolchain in the `:tools` section,
+ this search path is largely meaningless to you. However, this is a
+ convenient way to control the system include path should you rely on
+ the default [GCC] tools.
+
+ **Default**: `[]` (empty)
+
+* :paths
↳ :release_toolchain_include
+
+ Same as preceding albeit related to the release toolchain.
+
+ **Default**: `[]` (empty)
+
+* :paths
↳ :libraries
+
+ Library search paths. [See `:libraries` section][libraries].
+
+ **Default**: `[]` (empty)
+
+ [libraries]: #libraries
+
+* :paths
↳ :<custom>
+
+ Any paths you specify for custom list. List is available to tool
+ configurations and/or plugins. Note a distinction – the preceding names
+ are recognized internally to Ceedling and the path lists are used to
+ build collections of files contained in those paths. A custom list is
+ just that - a custom list of paths.
+
+### `:paths` configuration options & notes
+
+1. A path can be absolute (fully qualified) or relative.
+1. A path can include a glob matcher (more on this below).
+1. A path can use [inline Ruby string expansion][inline-ruby-string-expansion].
+1. Subtractive paths are possible and useful. See the documentation below.
+1. Path order beneath a subsection (e.g. `:paths` ↳ `:include`) is preserved
+ when the list is iterated internally or passed to a tool.
+
+### `:paths` Globs
+
+Globs are effectively fancy wildcards. They are not as capable as full regular
+expressions but are easier to use. Various OSs and programming languages
+implement them differently.
+
+For a quick overview, see this [tutorial][globs-tutorial].
+
+Ceedling supports globs so you can specify patterns of directories without the
+need to list each and every required path.
+
+Ceedling `:paths` globs operate similarlry to [Ruby globs][ruby-globs] except
+that they are limited to matching directories within `:paths` entries and not
+also files. In addition, Ceedling adds a useful convention with certain uses of
+the `*` and `**` operators.
+
+Glob operators include the following: `*`, `**`, `?`, `[-]`, `{,}`.
+
+* `*`
+ * When used within a character string, `*` is simply a standard wildcard.
+ * When used after a path separator, `/*` matches all subdirectories of depth 1
+ below the parent path, not including the parent path.
+* `**`: All subdirectories recursively discovered below the parent path, not
+ including the parent path. This pattern only makes sense after a path
+ separator `/**`.
+* `?`: Single alphanumeric character wildcard.
+* `[x-y]`: Single alphanumeric character as found in the specified range.
+* `{x, y, ...}`: Matching any of the comma-separated patterns. Two or more
+ patterns may be listed within the brackets. Patterns may be specific
+ character sequences or other glob operators.
+
+Special conventions:
+
+* If a globified path ends with `/*` or `/**`, the resulting list of directories
+ also includes the parent directory.
+
+See the example `:paths` YAML blurb section.
+
+[globs-tutotrial]: http://ruby.about.com/od/beginningruby/a/dir2.htm
+[ruby-globs]: https://ruby-doc.org/core-3.0.0/Dir.html#method-c-glob
+
+### Subtractive `:paths` entries
+
+Globs are super duper helpful when you have many paths to list. But, what if a
+single glob gets you 20 nested paths, but you actually want to exclude 2 of
+those paths?
+
+Must you revert to listing all 18 paths individually? No, my friend, we've got
+you. Behold, subtractive paths.
+
+Put simply, with an optional preceding decorator `-:`, you can instruct Ceedling
+to remove certain directory paths from a collection after it builds that
+collection.
+
+By default, paths are additive. For pretty alignment in your YAML, you may also
+use `+:`, but strictly speaking, it's not necessary.
+
+Subtractive paths may be simple paths or globs just like any other path entry.
+
+See examples below.
+
+### Example `:paths` YAML blurbs
+
+_Note:_ Ceedling standardizes paths for you. Internally, all paths use forward
+ slash `/` path separators (including on Windows), and Ceedling cleans up
+ trailing path separators to be consistent internally.
+
+#### Simple `:paths` entries
+
+```yaml
+:paths:
+ # All /*. => test/release compilation input
+ :source:
+ - project/src/ # Resulting source list has just two relative directory paths
+ - project/aux # (Traversal goes no deeper than these simple paths)
+
+ # All => compilation search paths + mock search paths
+ :include: # All => compilation input
+ - project/src/inc # Include paths are subdirectory of src/
+ - /usr/local/include/foo # Header files for a prebuilt library at fully qualified path
+
+ # All /*. => test compilation input + test suite executables
+ :test:
+ - ../tests # Tests have parent directory above working directory
+```
+
+#### Common `:paths` globs with subtractive path entries
+
+```yaml
+:paths:
+ :source:
+ - +:project/src/** # Recursive glob yields all subdirectories of any depth plus src/
+ - -:project/src/exp # Exclude experimental code in exp/ from release or test builds
+ # `+:` is decoration for pretty alignment; only `-:` changes a list
+
+ :include:
+ - +:project/src/**/inc # Include every subdirectory inc/ beneath src/
+ - -:project/src/exp/inc # Remove header files subdirectory for experimental code
+```
+
+#### Advanced `:paths` entries with globs and string expansion
+
+```yaml
+:paths:
+ :test:
+ - test/**/f??? # Every 4 character “f-series" subdirectory beneath test/
+
+ :my_things: # Custom path list
+ - "#{PROJECT_ROOT}/other" # Inline Ruby string expansion using Ceedling global constant
+```
+
+```yaml
+:paths:
+ :test:
+ - test/{foo,b*,xyz} # Path list will include test/foo/, test/xyz/, and any subdirectories
+ # beneath test/ beginning with 'b', including just test/b/
+```
+
+Globs and inline Ruby string expansion can require trial and error to arrive at
+your intended results. Ceedling provides as much validation of paths as is
+practical.
+
+Use the `ceedling paths:*` and `ceedling files:*` command line tasks —
+documented in a preceding section — to verify your settings. (Here `*` is
+shorthand for `test`, `source`, `include`, etc. Confusing? Sorry.)
+
+The command line option `ceedling dumpconfig` can also help your troubleshoot
+your configuration file. This application command causes Ceedling to process
+your configuration file and write the result to another YAML file for your
+inspection.
+
+## `:files` Modify file collections
+
+**File listings for tailoring file collections**
+
+Ceedling relies on file collections to do its work. These file collections are
+automagically assembled from paths, matching globs / wildcards, and file
+extensions (see project configuration `:extension`).
+
+Entries in `:files` accomplish filepath-oriented tailoring of the bulk file
+collections created from `:paths` directory listings and filename pattern
+matching.
+
+On occasion you may need to remove from or add individual files to Ceedling’s
+file collections.
+
+The path grammar documented in the `:paths` configuration section largely
+applies to `:files` path entries - albeit with regard to filepaths and not
+directory paths. The `:files` grammar and YAML examples are documented below.
+
+* :files
↳ :test
+
+ Modify the collection of unit test C files.
+
+ **Default**: `[]` (empty)
+
+* :files
↳ :source
+
+ Modify the collection of all source files used in unit test builds and release builds.
+
+ **Default**: `[]` (empty)
+
+* :files
↳ :assembly
+
+ Modify the (optional) collection of assembly files used in release builds.
+
+ **Default**: `[]` (empty)
+
+* :files
↳ :include
+
+ Modify the collection of all source header files used in unit test builds (e.g. for mocking) and release builds.
+
+ **Default**: `[]` (empty)
+
+* :files
↳ :support
+
+ Modify the collection of supporting C files available to unit tests builds.
+
+ **Default**: `[]` (empty)
+
+* :files
↳ :libraries
+
+ Add a collection of library paths to be included when linking.
+
+ **Default**: `[]` (empty)
+
+### `:files` configuration options & notes
+
+1. A path can be absolute (fully qualified) or relative.
+1. A path can include a glob matcher (more on this below).
+1. A path can use [inline Ruby string expansion][inline-ruby-string-expansion].
+1. Subtractive paths prepended with a `-:` decorator are possible and useful.
+ See the documentation below.
+
+### `:files` Globs
+
+Globs are effectively fancy wildcards. They are not as capable as full regular
+expressions but are easier to use. Various OSs and programming languages
+implement them differently.
+
+For a quick overview, see this [tutorial][globs-tutorial].
+
+Ceedling supports globs so you can specify patterns of files as well as simple,
+ordinary filepaths.
+
+Ceedling `:files` globs operate identically to [Ruby globs][ruby-globs] except
+that they ignore directory paths. Only filepaths are recognized.
+
+Glob operators include the following: `*`, `**`, `?`, `[-]`, `{,}`.
+
+* `*`
+ * When used within a character string, `*` is simply a standard wildcard.
+ * When used after a path separator, `/*` matches all subdirectories of depth
+ 1 below the parent path, not including the parent path.
+* `**`: All subdirectories recursively discovered below the parent path, not
+ including the parent path. This pattern only makes sense after a path
+ separator `/**`.
+* `?`: Single alphanumeric character wildcard.
+* `[x-y]`: Single alphanumeric character as found in the specified range.
+* `{x, y, ...}`: Matching any of the comma-separated patterns. Two or more
+ patterns may be listed within the brackets. Patterns may be specific
+ character sequences or other glob operators.
+
+### Subtractive `:files` entries
+
+Tailoring a file collection includes adding to it but also subtracting from it.
+
+Put simply, with an optional preceding decorator `-:`, you can instruct Ceedling
+to remove certain file paths from a collection after it builds that
+collection.
+
+By default, paths are additive. For pretty alignment in your YAML, you may also
+use `+:`, but strictly speaking, it's not necessary.
+
+Subtractive paths may be simple paths or globs just like any other path entry.
+
+See examples below.
+
+### Example `:files` YAML blurbs
+
+#### Simple `:files` tailoring
+
+```yaml
+:paths:
+ # All /*. => test/release compilation input
+ :source:
+ - src/**
+
+:files:
+ :source:
+ - +:callbacks/serial_comm.c # Add source code outside src/
+ - -:src/board/atm134.c # Remove board code
+```
+
+#### Advanced `:files` tailoring
+
+```yaml
+:paths:
+ # All /*. => test compilation input + test suite executables
+ :test:
+ - test/**
+
+:files:
+ :test:
+ # Remove every test file anywhere beneath test/ whose name ends with 'Model'.
+ # String replacement inserts a global constant that is the file extension for
+ # a C file. This is an anchor for the end of the filename and automaticlly
+ # uses file extension settings.
+ - "-:test/**/*Model#{EXTENSION_SOURCE}"
+
+ # Remove test files at depth 1 beneath test/ with 'analog' anywhere in their names.
+ - -:test/*{A,a}nalog*
+
+ # Remove test files at depth 1 beneath test/ that are of an “F series”
+ # test collection FAxxxx, FBxxxx, and FCxxxx where 'x' is any character.
+ - -:test/F[A-C]????
+```
+
+## `:environment:` Insert environment variables into shells running tools
+
+Ceedling creates environment variables from any key / value pairs in the
+environment section. Keys become an environment variable name in uppercase. The
+values are strings assigned to those environment variables. These value strings
+are either simple string values in YAML or the concatenation of a YAML array
+of strings.
+
+`:environment` entries are processed in the configured order (later entries
+can reference earlier entries).
+
+`:environment` variable value strings can include
+[inline Ruby string expansion][inline-ruby-string-expansion].
+
+### Special case: `PATH` handling
+
+In the specific case of specifying an environment key named `:path`, an array
+of string values will be concatenated with the appropriate platform-specific
+path separation character (i.e. `:` on Unix-variants, `;` on Windows).
+
+All other instances of environment keys assigned a value of a YAML array use
+simple concatenation.
+
+### Example `:environment` YAML blurb
+
+```yaml
+:environment:
+ - :license_server: gizmo.intranet # LICENSE_SERVER set with value "gizmo.intranet"
+ - :license: "#{`license.exe`}" # LICENSE set to string generated from shelling out to
+ # xecute license.exe; note use of enclosing quotes to
+ # prevent a YAML comment.
+
+ - :path: # Concatenated with path separator (see special case above)
+ - Tools/gizmo/bin # Prepend existing PATH with gizmo path
+ - "#{ENV['PATH']}" # Pattern #{…} triggers ruby evaluation string expansion
+ # Note: value string must be quoted because of '#' to
+ # prevent a YAML comment.
+
+ - :logfile: system/logs/thingamabob.log #LOGFILE set with path for a log file
+```
+
+## `:extension` Filename extensions used to collect lists of files searched in `:paths`
+
+Ceedling uses path lists and wildcard matching against filename extensions to collect file lists.
+
+* `:header`:
+
+ C header files
+
+ **Default**: .h
+
+* `:source`:
+
+ C code files (whether source or test files)
+
+ **Default**: .c
+
+* `:assembly`:
+
+ Assembly files (contents wholly assembler instructions)
+
+ **Default**: .s
+
+* `:object`:
-Please consult YAML documentation for the finer points of format
-and to understand details of our YAML-based configuration file.
-We recommend [Wikipedia's entry on YAML](http://en.wikipedia.org/wiki/Yaml)
-for this. A few highlights from that reference page:
+ Resulting binary output of C code compiler (and assembler)
-* YAML streams are encoded using the set of printable Unicode
- characters, either in UTF-8 or UTF-16
+ **Default**: .o
-* Whitespace indentation is used to denote structure; however
- tab characters are never allowed as indentation
+* `:executable`:
-* Comments begin with the number sign ( # ), can start anywhere
- on a line, and continue until the end of the line unless enclosed
- by quotes
+ Binary executable to be loaded and executed upon target hardware
-* List members are denoted by a leading hyphen ( - ) with one member
- per line, or enclosed in square brackets ( [ ] ) and separated
- by comma space ( , )
+ **Default**: .exe or .out (Win or Linux)
-* Hashes are represented using the colon space ( : ) in the form
- key: value, either one per line or enclosed in curly braces
- ( { } ) and separated by comma space ( , )
+* `:testpass`:
-* Strings (scalars) are ordinarily unquoted, but may be enclosed
- in double-quotes ( " ), or single-quotes ( ' )
+ Test results file (not likely to ever need a redefined value)
-* YAML requires that colons and commas used as list separators
- be followed by a space so that scalar values containing embedded
- punctuation can generally be represented without needing
- to be enclosed in quotes
+ **Default**: .pass
-* Repeated nodes are initially denoted by an ampersand ( & ) and
- thereafter referenced with an asterisk ( * )
+* `:testfail`:
-Notes on what follows:
+ Test results file (not likely to ever need a redefined value)
-* Each of the following sections represent top-level entries
- in the YAML configuration file.
+ **Default**: .fail
-* Unless explicitly specified in the configuration file, default
- values are used by Ceedling.
+* `:dependencies`:
-* These three settings, at minimum, must be specified:
- * [:project][:build_root]
- * [:paths][:source]
- * [:paths][:test]
+ File containing make-style dependency rules created by the `gcc` preprocessor
-* As much as is possible, Ceedling validates your settings in
- properly formed YAML.
+ **Default**: .d
-* Improperly formed YAML will cause a Ruby error when the YAML
- is parsed. This is usually accompanied by a complaint with
- line and column number pointing into the project file.
+### Example `:extension` YAML blurb
-* Certain advanced features rely on gcc and cpp as preprocessing
- tools. In most linux systems, these tools are already available.
- For Windows environments, we recommend the [mingw project](http://www.mingw.org/)
- (Minimalist GNU for Windows).
+```yaml
+:extension:
+ :source: .cc
+ :executable: .bin
+```
-* Ceedling is primarily meant as a build tool to support automated
- unit testing. All the heavy lifting is involved there. Creating
- a simple binary release build artifact is quite trivial in
- comparison. Consequently, most default options and the construction
- of Ceedling itself is skewed towards supporting testing though
- Ceedling can, of course, build your binary release artifact
- as well. Note that complex binary release artifacts (e.g.
- application + bootloader or multiple libraries) are beyond
- Ceedling's release build ability.
-
-Conventions / features of Ceedling-specific YAML:
-
-* Any second tier setting keys anywhere in YAML whose names end
- in `_path` or `_paths` are automagically processed like all
- Ceedling-specific paths in the YAML to have consistent directory
- separators (i.e. "/") and to take advantage of inline Ruby
- string expansion (see [:environment] setting below for further
- explanation of string expansion).
-
-**Let's Be Careful Out There:** Ceedling performs validation
-on the values you set in your configuration file (this assumes
-your YAML is correct and will not fail format parsing, of course).
-That said, validation is limited to only those settings Ceedling
-uses and those that can be reasonably validated. Ceedling does
-not limit what can exist within your configuration file. In this
-way, you can take full advantage of YAML as well as add sections
-and values for use in your own custom plugins (documented later).
-The consequence of this is simple but important. A misspelled
-configuration section name or value name is unlikely to cause
-Ceedling any trouble. Ceedling will happily process that section
-or value and simply use the properly spelled default maintained
-internally - thus leading to unexpected behavior without warning.
+## `:defines` Command line symbols used in compilation
-project: global project settings
+Ceedling’s internal, default compiler tool configurations (see later `:tools` section)
+execute compilation of test and source C files.
-* `build_root`:
+These default tool configurations are a one-size-fits-all approach. If you need to add to
+the command line symbols for individual tests or a release build, the `:defines` section
+allows you to easily do so.
- Top level directory into which generated path structure and files are
- placed. Note: this is one of the handful of configuration values that
- must be set. The specified path can be absolute or relative to your
- working directory.
+Particularly in testing, symbol definitions in the compilation command line are often needed:
- **Default**: (none)
+1. You may wish to control aspects of your test suite. Conditional compilation statements
+ can control which test cases execute in which circumstances. (Preprocessing must be
+ enabled, `:project` ↳ `:use_test_preprocessor`.)
-* `use_exceptions`:
+1. Testing means isolating the source code under test. This can leave certain symbols
+ unset when source files are compiled in isolation. Adding symbol definitions in your
+ Ceedling project file for such cases is one way to meet this need.
- Configures the build environment to make use of CException. Note that
- if you do not use exceptions, there's no harm in leaving this as its
- default value.
+Entries in `:defines` modify the command lines for compilers used at build time. In the
+default case, symbols listed beneath `:defines` become `-D` arguments.
- **Default**: TRUE
+### `:defines` verification (Ceedling does none)
-* `use_mocks`:
+Ceedling does no verification of your configured `:define` symbols.
- Configures the build environment to make use of CMock. Note that if
- you do not use mocks, there's no harm in leaving this setting as its
- default value.
+Unity, CMock, and CException conditional compilation statements, your toolchain's
+preprocessor, and/or your toolchain's compiler will complain appropriately if your
+specified symbols are incorrect, incomplete, or incompatible.
- **Default**: TRUE
+### `:defines` organization: Contexts and Matchers
-* `use_test_preprocessor`:
+The basic layout of `:defines` involves the concept of contexts.
- This option allows Ceedling to work with test files that contain
- conditional compilation statements (e.g. #ifdef) and header files you
- wish to mock that contain conditional preprocessor statements and/or
- macros.
+General case:
+```yaml
+:defines:
+ ::
+ -
+ - ...
+```
+
+Advanced matching for test build handling only:
+```yaml
+:defines:
+ :test:
+ :
+ -
+ - ...
+```
- Ceedling and CMock are advanced tools with sophisticated parsers.
- However, they do not include entire C language preprocessors.
- Consequently, with this option enabled, Ceedling will use gcc's
- preprocessing mode and the cpp preprocessor tool to strip down /
- expand test files and headers to their applicable content which can
- then be processed by Ceedling and CMock.
+A context is the build context you want to modify — `:test` or `:release`. Plugins
+can also hook into `:defines` with their own context.
- With this option enabled, the gcc & cpp tools must exist in an
- accessible system search path and test runner files are always
- regenerated.
+You specify the symbols you want to add to a build step beneath a `:`. In many
+cases this is a simple YAML list of strings that will become symbols defined in a
+compiler's command line.
- **Default**: FALSE
+Specifically in the `:test` context you also have the option to create test file matchers
+that create symbol definitions for some subset of your test build. Note that file
+matchers and the simpler list format cannot be mixed for `:defines` ↳ `:test`.
-* `use_preprocessor_directives`:
+* :defines
↳ :release
- After standard preprocessing when `use_test_preprocessor` is used
- macros are fully expanded to C code. Some features, for example
- TEST_CASE() or TEST_RANGE() from Unity require not-fully preprocessed
- file to be detected by Ceedling. To do this gcc directives-only
- option is used to expand only conditional compilation statements,
- handle directives, but do not expand macros preprocessor and leave
- the other content of file untouched.
+ This project configuration entry adds the items of a simple YAML list as symbols to
+ the compilation of every C file in a release build.
+
+ **Default**: `[]` (empty)
- With this option enabled, `use_test_preprocessor` must be also enabled
- and gcc must exist in an accessible system search path. For other
- compilers behavior can be changed by `test_file_preprocessor_directives`
- compiler tool.
+* :defines
↳ :preprocess
+
+ This project configuration entry adds the specified items as symbols to any needed
+ preprocessing of components in a test executable's build. (Preprocessing must be enabled,
+ `:project` ↳ `:use_test_preprocessor`.)
+
+ Preprocessing here refers to handling macros, conditional includes, etc. in header files
+ that are mocked and in complex test files before runners are generated from them.
+
+ Symbols may be represented in a simple YAML list or with a more sophisticated file matcher
+ YAML key plus symbol list. Both are documented below.
+
+ _Note:_ Left unspecified, `:preprocess` symbols default to be identical to `:test`
+ symbols. Override this behavior by adding `:defines` ↳ `:preprocess` symbols. If you want
+ no additional symbols for preprocessing regardless of `test` symbols, simply specify an
+ empty list `[]`.
+
+ **Default**: `[]` (empty)
- **Default**: FALSE
+* :defines
↳ :test
-* `use_deep_dependencies`:
+ This project configuration entry adds the specified items as symbols to compilation of C
+ components in a test executable's build.
+
+ Symbols may be represented in a simple YAML list or with a more sophisticated file matcher
+ YAML key plus symbol list. Both are documented below.
+
+ **Default**: `[]` (empty)
- The base rules and tasks that Ceedling creates using Rake capture most
- of the dependencies within a standard project (e.g. when the source
- file accompanying a test file changes, the corresponding test fixture
- executable will be rebuilt when tests are re-run). However, deep
- dependencies cannot be captured this way. If a typedef or macro
- changes in a header file three levels of #include statements deep,
- this option allows the appropriate incremental build actions to occur
- for both test execution and release builds.
+* :defines
↳ :<plugin context>
- This is accomplished by using the dependencies discovery mode of gcc.
- With this option enabled, gcc must exist in an accessible system
- search path.
+ Some advanced plugins make use of build contexts as well. For instance, the Ceedling
+ Gcov plugin uses a context of `:gcov`, surprisingly enough. For any plugins with tools
+ that take advantage of Ceedling’s internal mechanisms, you can add to those tools'
+ compilation symbols in the same manner as the built-in contexts.
- **Default**: FALSE
+### `:defines` options
-* `generate_deep_dependencies`:
+* `:use_test_definition`:
- When `use_deep_dependencies` is set to TRUE, Ceedling will run a separate
- build step to generate the deep dependencies. If you are using gcc as your
- primary compiler, or another compiler that can generate makefile rules as
- a side effect of compilation, then you can set this to FALSE to avoid the
- extra build step but still use the deep dependencies data when deciding
- which source files to rebuild.
+ If enabled, add a symbol to test compilation derived from the test file name. The
+ resulting symbol is a sanitized, uppercase, ASCII version of the test file name.
+ Any non ASCII characters (e.g. Unicode) are replaced by underscores as are any
+ non-alphanumeric characters. Underscores and dashes are preserved. The symbol name
+ is wrapped in underscores unless they already exist in the leading and trailing
+ positions. Example: _test_123abc-xyz😵.c_ ➡️ `_TEST_123ABC-XYZ_`.
- **Default**: TRUE
+ **Default**: False
-* `test_file_prefix`:
+### Simple `:defines` configuration
- Ceedling collects test files by convention from within the test file
- search paths. The convention includes a unique name prefix and a file
- extension matching that of source files.
+A simple and common need is configuring conditionally compiled features in a code base.
+The following example illustrates using simple YAML lists for symbol definitions at
+compile time.
- Why not simply recognize all files in test directories as test files?
- By using the given convention, we have greater flexibility in what we
- do with C files in the test directories.
+```yaml
+:defines:
+ :test:
+ - FEATURE_X=ON
+ - PRODUCT_CONFIG_C
+ :release:
+ - FEATURE_X=ON
+ - PRODUCT_CONFIG_C
+```
- **Default**: "test_"
+Given the YAML blurb above, the two symbols will be defined in the compilation command
+lines for all C files in a test suite build or release build.
-* `options_paths`:
+### Advanced `:defines` per-test matchers
- Just as you may have various build configurations for your source
- codebase, you may need variations of your project configuration.
+Ceedling treats each test executable as a mini project. As a reminder, each test file,
+together with all C sources and frameworks, becomes an individual test executable of
+the same name.
- By specifying options paths, Ceedling will search for other project
- YAML files, make command line tasks available (ceedling options:variation
- for a variation.yml file), and merge the project configuration of
- these option files in with the main project file at runtime. See
- advanced topics.
+_In the `:test` context only_, symbols may be defined for only those test executable
+builds that match file name criteria. Matchers match on test file names only, and the
+specified symbols are added to the build step for all files that are components of
+matched test executables.
- Note these Rake tasks at the command line - like verbosity or logging
- control - must come before the test or release task they are meant to
- modify.
+In short, for instance, this means your compilation of _TestA_ can have different
+symbols than compilation of _TestB_. Those symbols will be applied to every C file
+that is compiled as part those individual test executable builds. Thus, in fact, with
+separate test files unit testing the same source C file, you may exercise different
+conditional compilations of the same source. See the example in the section below.
- **Default**: `[]` (empty)
+#### `:defines` per-test matcher examples with YAML
-* `release_build`:
+Before detailing matcher capabilities and limits, here are examples to illustrate the
+basic ideas of test file name matching.
- When enabled, a release Rake task is exposed. This configuration
- option requires a corresponding release compiler and linker to be
- defined (gcc is used as the default).
+This example builds on the previous simple symbol list example. The imagined scenario
+is that of unit testing the same single source C file with different product features
+enabled.
- More release configuration options are available in the release_build
- section.
+```yaml
+# Imagine three test files all testing aspects of a single source file Comms.c with
+# different features enabled via conditional compilation.
+:defines:
+ :test:
+ # Tests for FeatureX configuration
+ :CommsFeatureX: # Matches a C test file name including 'CommsFeatureX'
+ - FEATURE_X=ON
+ - FEATURE_Z=OFF
+ - PRODUCT_CONFIG_C
+ # Tests for FeatureZ configuration
+ :CommsFeatureZ: # Matches a C test file name including 'CommsFeatureZ'
+ - FEATURE_X=OFF
+ - FEATURE_Z=ON
+ - PRODUCT_CONFIG_C
+ # Tests of base functionality
+ :CommsBase: # Matches a C test file name including 'CommsBase'
+ - FEATURE_X=OFF
+ - FEATURE_Z=OFF
+ - PRODUCT_BASE
+```
- **Default**: FALSE
+This example illustrates each of the test file name matcher types.
+
+```yaml
+:defines:
+ :test:
+ :*: # Wildcard: Add '-DA' for compilation all files for all tests
+ - A
+ :Model: # Substring: Add '-DCHOO' for compilation of all files of any test with 'Model' in its name
+ - CHOO
+ :/M(ain|odel)/: # Regex: Add '-DBLESS_YOU' for all files of any test with 'Main' or 'Model' in its name
+ - BLESS_YOU
+ :Comms*Model: # Wildcard: Add '-DTHANKS' for all files of any test that have zero or more characters
+ - THANKS # between 'Comms' and 'Model'
+```
+
+#### Using `:defines` per-test matchers
+These matchers are available:
-Example `[:project]` YAML blurb
+1. Wildcard (`*`)
+ 1. If specified in isolation, matches all tests.
+ 1. If specified within a string, matches any test filename with that
+ wildcard expansion.
+1. Substring — Matches on part of a test filename (up to all of it, including
+ full path).
+1. Regex (`/.../`) — Matches test file names against a regular expression.
+
+Notes:
+* Substring filename matching is case sensitive.
+* Wildcard matching is effectively a simplified form of regex. That is, multiple
+ approaches to matching can match the same filename.
+
+Symbols by matcher are cumulative. This means the symbols from more than one
+matcher can be applied to compilation for the components of any one test
+executable.
+
+Referencing the example above, here are the extra compilation symbols for a
+handful of test executables:
+
+* _test_Something_: `-DA`
+* _test_Main_: `-DA -DBLESS_YOU`
+* _test_Model_: `-DA -DCHOO -DBLESS_YOU`
+* _test_CommsSerialModel_: `-DA -DCHOO -DBLESS_YOU -DTHANKS`
+
+The simple `:defines` list format remains available for the `:test` context. The
+YAML blurb below is equivalent to the plain wildcard matcher above. Of course,
+this format is limited in that it applies symbols to the compilation of all C
+files for all test executables.
```yaml
-:project:
- :build_root: project_awesome/build
- :use_exceptions: FALSE
- :use_test_preprocessor: TRUE
- :use_deep_dependencies: TRUE
- :options_paths:
- - project/options
- - external/shared/options
- :release_build: TRUE
+:defines:
+ :test:
+ - A # Equivalent to wildcard '*' test file matching
```
-Ceedling is primarily concerned with facilitating the somewhat
-complicated mechanics of automating unit tests. The same mechanisms
-are easily capable of building a final release binary artifact
-(i.e. non test code; the thing that is your final working software
-that you execute on target hardware).
+#### Distinguishing similar or identical filenames with `:defines` per-test matchers
-* `use_backtrace_gdb_reporter`:
- Set this value to true if you project use gcc compiler and you want to collect
- backtrace from test runners which fail with **Segmentation fault** error.
- The .fail files will contain testsuite with information, which test failed.
- Backtrace is fully integrated with **junit_tests_report** plugin.
+You may find yourself needing to distinguish test files with the same name or test
+files with names whose base naming is identical.
- **Default**: FALSE
+Of course, identical test filenames have a natural distinguishing feature in their
+containing directory paths. Files of the same name can only exist in different
+directories. As such, your matching must include the path.
-* `output`:
+```yaml
+:defines:
+ :test:
+ :hardware/test_startup: # Match any test names beginning with 'test_startup' in hardware/ directory
+ - A
+ :network/test_startup: # Match any test names beginning with 'test_startup' in network/ directory
+ - B
+```
- The name of your release build binary artifact to be found in /artifacts/release. Ceedling sets the default artifact file
- extension to that as is explicitly specified in the [:extension]
- section or as is system specific otherwise.
+It's common in C file naming to use the same base name for multiple files. Given the
+following example list, care must be given to matcher construction to single out
+test_comm_startup.c.
- **Default**: `project.exe` or `project.out`
+* tests/test_comm_hw.c
+* tests/test_comm_startup.c
+* tests/test_comm_startup_timers.c
-* `use_assembly`:
+```yaml
+:defines:
+ :test:
+ :test_comm_startup.c: # Full filename with extension distinguishes this file test_comm_startup_timers.c
+ - FOO
+```
- If assembly code is present in the source tree, this option causes
- Ceedling to create appropriate build directories and use an assembler
- tool (default is the GNU tool as - override available in the [:tools]
- section.
+The preceding examples use substring matching, but, regular expression matching
+could also be appropriate.
- **Default**: FALSE
+#### Using YAML anchors & aliases for complex testing scenarios with `:defines`
-* `artifacts`:
+See the short but helpful article on [YAML anchors & aliases][yaml-anchors-aliases] to
+understand these features of YAML.
- By default, Ceedling copies to the /artifacts/release
- directory the output of the release linker and (optionally) a map
- file. Many toolchains produce other important output files as well.
- Adding a file path to this list will cause Ceedling to copy that file
- to the artifacts directory. The artifacts directory is helpful for
- organizing important build output files and provides a central place
- for tools such as Continuous Integration servers to point to build
- output. Selectively copying files prevents incidental build cruft from
- needlessly appearing in the artifacts directory. Note that inline Ruby
- string replacement is available in the artifacts paths (see discussion
- in the [:environment] section).
+Particularly in testing complex projects, per-test file matching may only get you so
+far in meeting your symbol definition needs. For instance, you may need to use the
+same symbols across many test files, but no convenient name matching scheme works.
+Advanced YAML features can help you copy the same symbols into multiple `:defines`
+test file matchers.
- **Default**: `[]` (empty)
+The following advanced example illustrates how to create a set of file matches for
+test preprocessing that are identical to test compilation with one addition.
-Example `[:release_build]` YAML blurb
+In brief, this example uses YAML to copy all the `:test` file matchers into
+`:preprocess` and add an additional symbol to the list for all test file
+wildcard matching.
```yaml
-:release_build:
- :output: top_secret.bin
- :use_assembly: TRUE
- :artifacts:
- - build/release/out/c/top_secret.s19
+:defines:
+ :test: &config-test-defines # YAML anchor
+ :*: &match-all-tests # YAML anchor
+ - PRODUCT_FEATURE_X
+ - ASSERT_LEVEL=2
+ - USES_RTOS=1
+ :test_foo:
+ - DRIVER_FOO=1u
+ :test_bar:
+ - DRIVER_BAR=5u
+ :preprocess:
+ <<: *config-test-defines # Insert all :test defines file matchers via YAML alias
+ :*: # Override wildcard matching key in copy of *config-test-defines
+ - *match-all-tests # Copy test defines for all files via YAML alias
+ - RTOS_SPECIAL_THING # Add single additional symbol to all test executable preprocessing
+ # test_foo, test_bar, and any other matchers are present because of <<: above
```
-**paths**: options controlling search paths for source and header
-(and assembly) files
+## `:libraries`
-* `test`:
+Ceedling allows you to pull in specific libraries for release and test builds with a
+few levels of support.
- All C files containing unit test code. Note: this is one of the
- handful of configuration values that must be set.
+* :libraries
↳ :test
+ Libraries that should be injected into your test builds when linking occurs.
+
+ These can be specified as naked library names or with relative paths if search paths
+ are specified with `:paths` ↳ `:libraries`. Otherwise, absolute paths may be used
+ here.
+
+ These library files **must** exist when tests build.
+
**Default**: `[]` (empty)
-* `source`:
-
- All C files containing release code (code to be tested). Note: this is
- one of the handful of configuration values that must be set.
+* :libraries
↳ :release
+
+ Libraries that should be injected into your release build when linking occurs.
+
+ These can be specified as naked library names or with relative paths if search paths
+ are specified with `:paths` ↳ `:libraries`. Otherwise, absolute paths may be used
+ here.
+
+ These library files **must** exist when the release build occurs **unless** you
+ are using the _subprojects_ plugin. In that case, the plugin will attempt to build
+ the needed library for you as a dependency.
+
+ **Default**: `[]` (empty)
+* :libraries
↳ :system
+
+ Libraries listed here will be injected into releases and tests.
+
+ These libraries are assumed to be findable by the configured linker tool, should need
+ no path help, and can be specified by common linker shorthand for libraries.
+
+ For example, specifying `m` will include the math library per the GCC convention. The
+ file itself on a Unix-like system will be `libm` and the `gcc` command line argument
+ will be `-lm`.
+
**Default**: `[]` (empty)
-* `support`:
+### `:libraries` options
- Any C files you might need to aid your unit testing. For example, on
- occasion, you may need to create a header file containing a subset of
- function signatures matching those elsewhere in your code (e.g. a
- subset of your OS functions, a portion of a library API, etc.). Why?
- To provide finer grained control over mock function substitution or
- limiting the size of the generated mocks.
+* `:flag`:
- **Default**: `[]` (empty)
+ Command line argument format for specifying a library.
-* `include`:
+ **Default**: `-l${1}` (GCC format)
- Any header files not already in the source search path. Note there's
- no practical distinction between this search path and the source
- search path; it's merely to provide options or to support any
- peculiar source tree organization.
+* `:path_flag`:
- **Default**: `[]` (empty)
+ Command line argument format for adding a library search path.
-* `test_toolchain_include`:
+ Library search paths may be added to your project with `:paths` ↳ `:libraries`.
- System header files needed by the test toolchain - should your
- compiler be unable to find them, finds the wrong system include search
- path, or you need a creative solution to a tricky technical problem.
- Note that if you configure your own toolchain in the [:tools] section,
- this search path is largely meaningless to you. However, this is a
- convenient way to control the system include path should you rely on
- the default gcc tools.
+ **Default**: `-L "${1}”` (GCC format)
- **Default**: `[]` (empty)
+### `:libraries` example with YAML blurb
-* `release_toolchain_include`:
+```yaml
+:paths:
+ :libraries:
+ - proj/libs # Linker library search paths
- Same as preceding albeit related to the release toolchain.
+:libraries:
+ :test:
+ - test/commsstub.lib # Imagined communication library that logs to console without traffic
+ :release:
+ - release/comms.lib # Imagined production communication library
+ :system:
+ - math # Add system math library to test & release builds
+ :flag: -Lib=${1} # This linker does not follow the gcc convention
+```
- **Default**: `[]` (empty)
+### `:libraries` notes
-* ``
+* If you've specified your own link step, you are going to want to add `${4}` to your
+ argument list in the position where library files should be added to the command line.
+ For `gcc`, this is often at the very end. Other tools may vary. See the `:tools`
+ section for more.
- Any paths you specify for custom list. List is available to tool
- configurations and/or plugins. Note a distinction. The preceding names
- are recognized internally to Ceedling and the path lists are used to
- build collections of files contained in those paths. A custom list is
- just that - a custom list of paths.
+## `:flags` Configure preprocessing, compilation & linking command line flags
+
+Ceedling’s internal, default tool configurations (see later `:tools` section) execute
+compilation and linking of test and source files among other needs.
-Notes on path grammar within the [:paths] section:
+These default tool configurations are a one-size-fits-all approach. If you need to add to
+the command line flags for individual tests or a release build, the `:flags` section allows
+you to easily do so.
-* Order of search paths listed in [:paths] is preserved when used by an
- entry in the [:tools] section
+Entries in `:flags` modify the command lines for tools used at build time.
-* Wherever multiple path lists are combined for use Ceedling prioritizes
- path groups as follows:
- test paths, support paths, source paths, include paths.
+### Flags organization: Contexts, Operations, and Matchers
- This can be useful, for instance, in certain testing scenarios where
- we desire Ceedling or the compiler to find a stand-in header file before
- the actual source header file of the same name.
+The basic layout of `:flags` involves the concepts of contexts and operations.
+
+General case:
+```yaml
+:flags:
+ ::
+ ::
+ -
+ - ...
+```
-* Paths:
+Advanced matching for test build handling only:
+```yaml
+:flags:
+ :test:
+ ::
+ :
+ -
+ - ...
+```
- 1. can be absolute or relative
+A context is the build context you want to modify — `:test` or `:release`. Plugins can
+also hook into `:flags` with their own context.
- 2. can be singly explicit - a single fully specified path
+An operation is the build step you wish to modify — `:preprocess`, `:compile`, `:assemble`,
+or `:link`.
- 3. can include a glob operator (more on this below)
+* The `:preprocess` operation is only available in the `:test` context.
+* The `:assemble` operation is only available within the `:test` or `:release` contexts if
+ assembly support has been enabled in `:test_build` or `:release_build`, respectively, and
+ assembly files are a part of the project.
- 4. can use inline Ruby string replacement (see [:environment]
- section for more)
+You specify the flags you want to add to a build step beneath `:` ↳ `:`.
+In many cases this is a simple YAML list of strings that will become flags in a tool's
+command line.
- 5. default as an addition to a specific search list (more on this
- in the examples)
+Specifically in the `:test` context you also have the option to create test file matchers
+that apply flags to some subset of your test build. Note that file matchers and the simpler
+flags list format cannot be mixed for `:flags` ↳ `:test`.
- 6. can act to subtract from a glob included in the path list (more
- on this in the examples)
+* :flags
↳ :release
↳ :compile
-[Globs](http://ruby.about.com/od/beginningruby/a/dir2.htm)
-as used by Ceedling are wildcards for specifying directories
-without the need to list each and every required search path.
-Ceedling globs operate just as Ruby globs except that they are
-limited to matching directories and not files. Glob operators
-include the following * ** ? [-] {,} (note: this list is space separated
-and not comma separated as commas are used within the bracket
-operators).
+ This project configuration entry adds the items of a simple YAML list as flags to
+ compilation of every C file in a release build.
+
+ **Default**: `[]` (empty)
-* `*`:
+* :flags
↳ :release
↳ :link
- All subdirectories of depth 1 below the parent path and including the
- parent path
+ This project configuration entry adds the items of a simple YAML list as flags to
+ the link step of a release build artifact.
+
+ **Default**: `[]` (empty)
-* `**`:
+* :flags
↳ :test
↳ :preprocess
+
+ This project configuration entry adds the specified items as flags to any needed
+ preprocessing of components in a test executable's build. (Preprocessing must be enabled,
+ `:project` ↳ `:use_test_preprocessor`.)
+
+ Preprocessing here refers to handling macros, conditional includes, etc. in header files
+ that are mocked and in complex test files before runners are generated from them.
+
+ Flags may be represented in a simple YAML list or with a more sophisticated file matcher
+ YAML key plus flag list. Both are documented below.
+
+ _Note:_ Left unspecified, `:preprocess` flags default to behaving identically to `:compile`
+ flags. Override this behavior by adding `:test` ↳ `:preprocess` flags. If you want no
+ additional flags for preprocessing regardless of test compilation flags, simply specify
+ an empty list `[]`.
+
+ **Default**: `[]` (empty)
- All subdirectories recursively discovered below the parent path and
- including the parent path
+* :flags
↳ :test
↳ :compile
-* `?`:
+ This project configuration entry adds the specified items as flags to compilation of C
+ components in a test executable's build.
+
+ Flags may be represented in a simple YAML list or with a more sophisticated file matcher
+ YAML key plus flag list. Both are documented below.
+
+ **Default**: `[]` (empty)
- Single alphanumeric character wildcard
+* :flags
↳ :test
↳ :link
-* `[x-y]`:
+ This project configuration entry adds the specified items as flags to the link step of
+ test executables.
+
+ Flags may be represented in a simple YAML list or with a more sophisticated file matcher
+ YAML key plus flag list. Both are documented below.
+
+ **Default**: `[]` (empty)
- Single alphanumeric character as found in the specified range
+* :flags
↳ :<plugin context>
-* `{x,y}`:
+ Some advanced plugins make use of build contexts as well. For instance, the Ceedling
+ Gcov plugin uses a context of `:gcov`, surprisingly enough. For any plugins with tools
+ that take advantage of Ceedling’s internal mechanisms, you can add to those tools'
+ flags in the same manner as the built-in contexts and operations.
- Single alphanumeric character from the specified list
+### Simple `:flags` configuration
-Example [:paths] YAML blurbs
+A simple and common need is enforcing a particular C standard. The following example
+illustrates simple YAML lists for flags.
```yaml
-:paths:
- :source: #together the following comprise all source search paths
- - project/source/* #expansion yields all subdirectories of depth 1 plus parent directory
- - project/lib #single path
- :test: #all test search paths
- - project/**/test? #expansion yields any subdirectory found anywhere in the project that
- #begins with "test" and contains 5 characters
+:flags:
+ :release:
+ :compile:
+ - -std=c99 # Add `-std=c99` to compilation of all C files in the release build
+ :test:
+ :compile:
+ - -std=c99 # Add `-std=c99` to the compilation of all C files in all test executables
+```
-:paths:
- :source: #all source search paths
- - +:project/source/** #all subdirectories recursively discovered plus parent directory
- - -:project/source/os/generated #subtract os/generated directory from expansion of above glob
- #note that '+:' notation is merely aesthetic; default is to add
+Given the YAML blurb above, when test or release compilation occurs, the flag specifying
+the C standard will be in the command line for compilation of all C files.
+
+### Advanced `:flags` per-test matchers
+
+Ceedling treats each test executable as a mini project. As a reminder, each test file,
+together with all C sources and frameworks, becomes an individual test executable of
+the same name.
+
+_In the `:test` context only_, flags can be applied to build step operations —
+preprocessing, compilation, and linking — for only those test executables that match
+file name criteria. Matchers match on test file names only, and the specified flags
+are added to the build step for all files that are components of matched test
+executables.
+
+In short, for instance, this means your compilation of _TestA_ can have different flags
+than compilation of _TestB_. And, in fact, those flags will be applied to every C file
+that is compiled as part those individual test executable builds.
+
+#### `:flags` per-test matcher examples with YAML
- :test: #all test search paths
- - project/test/bootloader #explicit, single search paths (searched in the order specified)
- - project/test/application
- - project/test/utilities
+Before detailing matcher capabilities and limits, here are examples to illustrate the
+basic ideas of test file name matching.
- :custom: #custom path list
- - "#{PROJECT_ROOT}/other" #inline Ruby string expansion
+```yaml
+:flags:
+ :test:
+ :compile:
+ :*: # Wildcard: Add '-foo' for all files for all tests
+ - -foo
+ :Model: # Substring: Add '-Wall' for all files of any test with 'Model' in its name
+ - -Wall
+ :/M(ain|odel)/: # Regex: Add 🏴☠️ flag for all files of any test with 'Main' or 'Model' in its name
+ - -🏴☠️
+ :Comms*Model:
+ - --freak # Wildcard: Add your `--freak` flag for all files of any test name with zero or more
+ # characters between 'Comms' and 'Model'
+ :link:
+ :tests/comm/TestUsart.c: # Substring: Add '--bar --baz' to the link step of the TestUsart executable
+ - --bar
+ - --baz
```
-Globs and inline Ruby string expansion can require trial and
-error to arrive at your intended results. Use the `ceedling paths:*`
-command line options (documented in preceding section) to verify
-your settings.
+#### Using `:flags` per-test matchers
-Ceedling relies on file collections automagically assembled
-from paths, globs, and file extensions. File collections greatly
-simplify project set up. However, sometimes you need to remove
-from or add individual files to those collections.
+These matchers are available:
+1. Wildcard (`*`)
+ 1. If specified in isolation, matches all tests.
+ 1. If specified within a string, matches any test filename with that
+ wildcard expansion.
+1. Substring — Matches on part of a test filename (up to all of it, including
+ full path).
+1. Regex (`/.../`) — Matches test file names against a regular expression.
-* `test`:
+Notes:
+* Substring filename matching is case sensitive.
+* Wildcard matching is effectively a simplified form of regex. That is,
+ multiple approaches to matching can match the same filename.
- Modify the collection of unit test C files.
+Flags by matcher are cumulative. This means the flags from more than one matcher can be
+applied to an operation on any one test executable.
- **Default**: `[]` (empty)
+Referencing the example above, here are the extra compilation flags for a handful of
+test executables:
-* `source`:
+* _test_Something_: `-foo`
+* _test_Main_: `-foo -🏴☠️`
+* _test_Model_: `-foo -Wall -🏴☠️`
+* _test_CommsSerialModel_: `-foo -Wall -🏴☠️ --freak`
- Modify the collection of all source files used in unit test builds and release builds.
+The simple `:flags` list format remains available for the `:test` context. The YAML
+blurb below is equivalent to the plain wildcard matcher above. Of course, this format is
+limited in that it applies flags to all C files for all test executables.
- **Default**: `[]` (empty)
+```yaml
+:flags:
+ :test:
+ :compile: # Equivalent to wildcard '*' test file matching
+ - -foo
+```
-* `assembly`:
+#### Distinguishing similar or identical filenames with `:flags` per-test matchers
- Modify the (optional) collection of assembly files used in release builds.
+You may find yourself needing to distinguish test files with the same name or test
+files with names whose base naming is identical.
- **Default**: `[]` (empty)
+Of course, identical test filenames have a natural distinguishing feature in their
+containing directory paths. Files of the same name can only exist in different
+directories. As such, your matching must include the path.
+
+```yaml
+:flags:
+ :test:
+ :compile:
+ :hardware/test_startup: # Match any test names beginning with 'test_startup' in hardware/ directory
+ - A
+ :network/test_startup: # Match any test names beginning with 'test_startup' in network/ directory
+ - B
+```
-* `include`:
+It's common in C file naming to use the same base name for multiple files. Given the
+following example list, care must be given to matcher construction to single out
+test_comm_startup.c.
- Modify the collection of all source header files used in unit test builds (e.g. for mocking) and release builds.
+* tests/test_comm_hw.c
+* tests/test_comm_startup.c
+* tests/test_comm_startup_timers.c
+
+```yaml
+:flags:
+ :test:
+ :compile:
+ :test_comm_startup.c: # Full filename with extension distinguishes this file test_comm_startup_timers.c
+ - FOO
+```
+The preceding examples use substring matching, but, regular expression matching
+could also be appropriate.
+
+#### Using YAML anchors & aliases for complex testing scenarios with `:flags`
+
+See the short but helpful article on [YAML anchors & aliases][yaml-anchors-aliases] to
+understand these features of YAML.
+
+Particularly in testing complex projects, per-test file matching may only get you so
+far in meeting your build step flag needs. For instance, you may need to set various
+flags for operations across many test files, but no convenient name matching scheme
+works. Advanced YAML features can help you copy the same flags into multiple `:flags`
+test file matchers.
+
+Please see the discussion in `:defines` for a complete example.
+
+## `:cexception` Configure CException’s features
+
+* `:defines`:
+
+ List of symbols used to configure CException's features in its source and header files
+ at compile time.
+
+ See [Using Unity, CMock & CException](#using-unity-cmock--cexception) for much more on
+ configuring and making use of these frameworks in your build.
+
+ To manage overall command line length, these symbols are only added to compilation when
+ a CException C source file is compiled.
+
+ No symbols must be set unless CException's defaults are inappropriate for your
+ environment and needs.
+
+ Note CException must be enabled for it to be added to a release or test build and for
+ these symbols to be added to a build of CException (see link referenced earlier for more).
+
**Default**: `[]` (empty)
-* `support`:
+## `:cmock` Configure CMock’s code generation & compilation
- Modify the collection of supporting C files available to unit tests builds.
+Ceedling sets values for a subset of CMock settings. All CMock
+options are available to be set, but only those options set by
+Ceedling in an automated fashion are documented below. See CMock
+documentation.
- **Default**: `[]` (empty)
+Ceedling sets values for a subset of CMock settings. All CMock
+options are available to be set, but only those options set by
+Ceedling in an automated fashion are documented below.
+See [CMock] documentation.
-* `libraries`:
+* `:enforce_strict_ordering`:
- Add a collection of library paths to be included when linking.
+ Tests fail if expected call order is not same as source order
- **Default**: `[]` (empty)
+ **Default**: TRUE
+* `:mock_path`:
-Note: All path grammar documented in [:paths] section applies
-to [:files] path entries - albeit at the file path level and not
-the directory level.
+ Path for generated mocks
-Example [:files] YAML blurb
+ **Default**: /tests/mocks
-```yaml
-:files:
- :source:
- - callbacks/comm.c # entry defaults to file addition
- - +:callbacks/comm*.c # add all comm files matching glob pattern
- - -:source/board/atm134.c # not our board
- :test:
- - -:test/io/test_output_manager.c # remove unit tests from test build
-```
+* `:verbosity`:
-**environment:** inserts environment variables into the shell
-instance executing configured tools
+ If not set, defaults to Ceedling’s verbosity level
-Ceedling creates environment variables from any key / value
-pairs in the environment section. Keys become an environment
-variable name in uppercase. The values are strings assigned
-to those environment variables. These value strings are either
-simple string values in YAML or the concatenation of a YAML array.
+* `:defines`:
-Ceedling is able to execute inline Ruby string substitution
-code to set environment variables. This evaluation occurs when
-the project file is first processed for any environment pair's
-value string including the Ruby string substitution pattern
-`#{…}`. Note that environment value strings that _begin_ with
-this pattern should always be enclosed in quotes. YAML defaults
-to processing unquoted text as a string; quoting text is optional.
-If an environment pair's value string begins with the Ruby string
-substitution pattern, YAML will interpret the string as a Ruby
-comment (because of the `#`). Enclosing each environment value
-string in quotes is a safe practice.
+ Adds list of symbols used to configure CMock's C code features in its source and header
+ files at compile time.
+
+ See [Using Unity, CMock & CException](#using-unity-cmock--cexception) for much more on
+ configuring and making use of these frameworks in your build.
+
+ To manage overall command line length, these symbols are only added to compilation when
+ a CMock C source file is compiled.
+
+ No symbols must be set unless CMock's defaults are inappropriate for your environment
+ and needs.
+
+ **Default**: `[]` (empty)
-[:environment] entries are processed in the configured order
-(later entries can reference earlier entries).
+* `:plugins`:
-Special case: PATH handling
+ To add to the list Ceedling provides CMock, simply add `:cmock` ↳ `:plugins`
+ to your configuration and specify your desired additional plugins.
-In the specific case of specifying an environment key named _path_,
-an array of string values will be concatenated with the appropriate
-platform-specific path separation character (e.g. ':' on linux,
-';' on Windows). All other instances of environment keys assigned
-YAML arrays use simple concatenation.
+ See [CMock's documentation][cmock-docs] to understand plugin options.
-Example [:environment] YAML blurb
+ [cmock-docs]: https://github.com/ThrowTheSwitch/CMock/blob/master/docs/CMock_Summary.md
-```yaml
-:environment:
- - :license_server: gizmo.intranet #LICENSE_SERVER set with value "gizmo.intranet"
- - :license: "#{`license.exe`}" #LICENSE set to string generated from shelling out to
- #execute license.exe; note use of enclosing quotes
+* `:includes`:
- - :path: #concatenated with path separator (see special case above)
- - Tools/gizmo/bin #prepend existing PATH with gizmo path
- - "#{ENV['PATH']}" #pattern #{…} triggers ruby evaluation string substitution
- #note: value string must be quoted because of '#'
+ If `:cmock` ↳ `:unity_helper` set, prepopulated with unity_helper file
+ name (no path).
- - :logfile: system/logs/thingamabob.log #LOGFILE set with path for a log file
-```
+ The `:cmock` ↳ `:includes` list works identically to the plugins list
+ above with regard to adding additional files to be inserted within
+ mocks as #include statements.
-**extension**: configure file name extensions used to collect lists of files searched in [:paths]
+### Notes on Ceedling’s nudges for CMock strict ordering
-* `header`:
+The last four settings above are directly tied to other Ceedling
+settings; hence, why they are listed and explained here.
+
+The first setting above, `:enforce_strict_ordering`, defaults
+to `FALSE` within CMock. However, it is set to `TRUE` by default
+in Ceedling as our way of encouraging you to use strict ordering.
+
+Strict ordering is teeny bit more expensive in terms of code
+generated, test execution time, and complication in deciphering
+test failures. However, it's good practice. And, of course, you
+can always disable it by overriding the value in the Ceedling
+project configuration file.
+
+## `:unity` Configure Unity’s features
+
+* `:defines`:
+
+ Adds list of symbols used to configure Unity's features in its source and header files
+ at compile time.
+
+ See [Using Unity, CMock & CException](#using-unity-cmock--cexception) for much more on
+ configuring and making use of these frameworks in your build.
+
+ To manage overall command line length, these symbols are only added to compilation when
+ a Unity C source file is compiled.
+
+ No symbols must be set unless Unity's defaults are inappropriate for your environment
+ and needs.
+
+ **Default**: `[]` (empty)
- C header files
+## `:test_runner` Configure test runner generation
- **Default**: .h
+The format of Ceedling test files — the C files that contain unit test cases —
+is intentionally simple. It's pure code and all legit, simple C with `#include`
+statements, test case functions, and optional `setUp()` and `tearDown()`
+functions.
+
+To create test executables, we need a `main()` and a variety of calls to the
+Unity framework to “hook up” all your test cases into a test suite. You can do
+this by hand, of course, but it's tedious and needed updates are easily
+forgotten.
+
+So, Unity provides a script able to generate a test runner in C for you. It
+relies on [conventions] used in in your test files. Ceedling takes this a step
+further by calling this script for you with all the needed parameters.
+
+Test runner generation is configurable. The `:test_runner` section of your
+Ceedling project file allows you to pass options to Unity's runner generation
+script. A YAML hash beneath `:test_runner` is provided directly to that script.
-* `source`:
+[Test runner configuration options are documented in the Unity project][unity-runner-options].
- C code files (whether source or test files)
+Example configuration:
- **Default**: .c
+```yaml
+:test_runner:
+ # Insert additional #include statements in a generated runner
+ :includes:
+ - Foo.h
+ - Bar.h
+```
-* `assembly`:
+[ceedling-conventions]: #important-conventions--behaviors
+[unity-runner-options]: https://github.com/ThrowTheSwitch/Unity/blob/master/docs/UnityHelperScriptsGuide.md#options-accepted-by-generate_test_runnerrb
- Assembly files (contents wholly assembly instructions)
+## `:tools` Configuring command line tools used for build steps
- **Default**: .s
+Ceedling requires a variety of tools to work its magic. By default, the GNU
+toolchain (`gcc`, `cpp`, `as` — and `gcov` via plugin) are configured and ready
+for use with no additions to your project configuration YAML file.
-* `object`:
+A few items before we dive in:
- Resulting binary output of C code compiler (and assembler)
+1. Sometimes Ceedling’s built-in tools are _nearly_ what you need but not
+ quite. If you only need to add some arguments to all uses of tool's command
+ line, Ceedling offers a shortcut to do so. See the
+ [final section of the `:tools`][tool-args-shortcut] documentation for
+ details.
+1. If you need fine-grained control of the arguments Ceedling uses in the build
+ steps for test executables, see the documentation for [`:flags`][flags].
+ Ceedling allows you to control the command line arguments for each test
+ executable build — with a variety of pattern matching options.
+1. If you need to link libraries — your own or standard options — please see
+ the [top-level `:libraries` section][libraries] available for your
+ configuration file. Ceedling supports a number of useful options for working
+ with pre-compiled libraries. If your library linking needs are super simple,
+ the shortcut in (1) might be the simplest option.
- **Default**: .o
+[flags]: #flags-configure-preprocessing-compilation--linking-command-line-flags
+[tool-args-shortcut]: #ceedling-tool-arguments-addition-shortcut
-* `executable`:
+### Ceedling tools for test suite builds
- Binary executable to be loaded and executed upon target hardware
+Our recommended approach to writing and executing test suites relies on the GNU
+toolchain. _*Yes, even for embedded system work on platforms with their own,
+proprietary C toolchain.*_ Please see
+[this section of documentation][sweet-suite] to understand this recommendation
+among all your options.
- **Default**: .exe or .out (Win or linux)
+You can and sometimes must run a Ceedling test suite in an emulator or on
+target, and Ceedling allows you to do this through tool definitions documented
+here. Generally, you'll likely want to rely on the default definitions.
-* `testpass`:
+[sweet-suite]: #all-your-sweet-sweet-test-suite-options
- Test results file (not likely to ever need a new value)
+### Ceedling tools for release builds
- **Default**: .pass
+More often than not, release builds require custom tool definitions. The GNU
+toolchain is configured for Ceeding release builds by default just as with test
+builds. You'll likely need your own definitions for `:release_compiler`,
+`:release_linker`, and possibly `:release_assembler`.
-* `testfail`:
+### Ceedling plugin tools
- Test results file (not likely to ever need a new value)
+Ceedling plugins are free to define their own tools that are loaded into your
+project configuration at startup. Plugin tools are defined using the same
+mechanisns as Ceedling’s built-in tools and are called the same way. That is,
+all features available to you for working with tools as an end users are
+generally available for working with plugin-based tools. This presumes a
+plugin author followed guidance and convention in creating any command line
+actions.
- **Default**: .fail
+### Ceedling tool definitions
-* `dependencies`:
+Contained in this section are details on Ceedling’s default tool definitions.
+For sake of space, the entirety of a given definition is not shown. If you need
+to get in the weeds or want a full example, see the file `defaults.rb` in
+Ceedling’s lib/ directory.
- File containing make-style dependency rules created by gcc preprocessor
+#### Tool definition overview
- **Default**: .d
+Listed below are the built-in tool names, corresponding to build steps along
+with the numbered parameters that Ceedling uses to fill out a full command line
+for the named tool. The full list of fundamental elements for a tool definition
+are documented in the sections that follow along with examples.
+Not every numbered parameter listed immediately below must be referenced in a
+Ceedling tool definition. If `${4}` isn’t referenced by your custom tool,
+Ceedling simply skips it while expanding a tool definition into a command line.
-Example [:extension] YAML blurb
+The numbered parameters below are references that expand / are replaced with
+actual values when the corresponding command line is constructed. If the values
+behind these parameters are lists, Ceedling expands the containing reference
+multiple times with the contents of the value. A conceptual example is
+instructive…
- :extension:
- :source: .cc
- :executable: .bin
+#### Simplified tool definition / expansion example
-**defines**: command line defines used in test and release compilation by configured tools
+A partial tool definition:
-* `test`:
+```yaml
+:tools:
+ :power_drill:
+ :executable: dewalt.exe
+ :arguments:
+ - "--X${3}"
+```
- Defines needed for testing. Useful for:
+Let's say that `${3}` is a list inside Ceedling, `[2, 3, 7]`. The expanded tool
+command line for `:tools` ↳ `:power_drill` would look like this:
- 1. test files containing conditional compilation statements (i.e.
- tests active in only certain contexts)
+```shell
+ > dewalt.exe --X2 --X3 --X7
+```
- 2. testing legacy source wherein the isolation of source under test
- afforded by Ceedling and its complementary tools leaves certain
- symbols unset when source files are compiled in isolation
+#### Ceedling’s default build step tool definitions
- **Default**: `[]` (empty)
+* `:test_compiler`:
-* `test_preprocess`:
+ Compiler for test & source-under-test code
- If [:project][:use_test_preprocessor] or
- [:project][:use_deep_dependencies] is set and code is structured in a
- certain way, the gcc preprocessor may need symbol definitions to
- properly preprocess files to extract function signatures for mocking
- and extract deep dependencies for incremental builds.
+ - `${1}`: Input source
+ - `${2}`: Output object
+ - `${3}`: Optional output list
+ - `${4}`: Optional output dependencies file
+ - `${5}`: Header file search paths
+ - `${6}`: Command line #defines
- **Default**: `[]` (empty)
+ **Default**: `gcc`
-* ``:
+* `:test_linker`:
- Replace standard `test` definitions for specified ``definitions. For example:
-```yaml
- :defines:
- :test:
- - FOO_STANDARD_CONFIG
- :test_foo_config:
- - FOO_SPECIFIC_CONFIG
-```
- `ceedling test:foo_config` will now have `FOO_SPECIFIC_CONFIG` defined instead of
- `FOO_STANDARD_CONFIG`. None of the other tests will have `FOO_SPECIFIC_SPECIFIC`.
+ Linker to generate test fixture executables
- **Default**: `[]` (empty)
+ - `${1}`: input objects
+ - `${2}`: output binary
+ - `${3}`: optional output map
+ - `${4}`: optional library list
+ - `${5}`: optional library path list
-* `release`:
+ **Default**: `gcc`
- Defines needed for the release build binary artifact.
+* `:test_fixture`:
- **Default**: `[]` (empty)
+ Executable test fixture
-* `release_preprocess`:
+ - `${1}`: simulator as executable with`${1}` as input binary file argument or native test executable
- If [:project][:use_deep_dependencies] is set and code is structured in
- a certain way, the gcc preprocessor may need symbol definitions to
- properly preprocess files for incremental release builds due to deep
- dependencies.
+ **Default**: `${1}`
- **Default**: `[]` (empty)
+* `:test_includes_preprocessor`:
-* `use_test_definition`:
+ Extractor of #include statements
- When this option is used the `-D` flag is added to the build option.
+ - `${1}`: input source file
- **Default**: FALSE
+ **Default**: `cpp`
-Example [:defines] YAML blurb
+* `:test_file_preprocessor`:
-```yaml
-:defines:
- :test:
- - UNIT_TESTING #for select cases in source to allow testing with a changed behavior or interface
- - OFF=0
- - ON=1
- - FEATURE_X=ON
- :source:
- - FEATURE_X=ON
-```
+ Preprocessor of test files (macros, conditional compilation statements)
+ - `${1}`: input source file
+ - `${2}`: preprocessed output source file
+ **Default**: `gcc`
-**libraries**: command line defines used in test and release compilation by configured tools
+* `:release_compiler`:
-Ceedling allows you to pull in specific libraries for the purpose of release and test builds.
-It has a few levels of support for this. Start by adding a :libraries main section in your
-configuration. In this section, you can optionally have the following subsections:
+ Compiler for release source code
-* `test`:
+ - `${1}`: input source
+ - `${2}`: output object
+ - `${3}`: optional output list
+ - `${4}`: optional output dependencies file
- Library files that should be injected into your tests when linking occurs.
- These can be specified as either relative or absolute paths. These files MUST
- exist when the test attempts to build.
+ **Default**: `gcc`
-* `release`:
+* `:release_assembler`:
- Library files that should be injected into your release when linking occurs. These
- can be specified as either relative or absolute paths. These files MUST exist when
- the release attempts to build UNLESS you are using the subprojects plugin. In that
- case, it will attempt to build that library for you as a dynamic dependency.
+ Assembler for release assembly code
-* `system`:
+ - `${1}`: input assembly source file
+ - `${2}`: output object file
- These libraries are assumed to be in the tool path somewhere and shouldn't need to be
- specified. The libraries added here will be injected into releases and tests. For example
- if you specify `-lm` you can include the math library. The `-l` portion is only necessary
- if the `:flag` prefix below doesn't specify it already for you other libraries.
+ **Default**: `as`
-* `flag`:
+* `:release_linker`:
- This is the method of adding an argument for each library. For example, gcc really likes
- it when you specify “-l${1}”
+ Linker for release source code
-* `path_flag`:
+ - `${1}`: input objects
+ - `${2}`: output binary
+ - `${3}`: optional output map
+ - `${4}`: optional library list
+ - `${5}`: optional library path list
- This is the method of adding an path argument for each library path. For example, gcc really
- likes it when you specify “-L \"${1}\"”
+ **Default**: `gcc`
-Notes:
+#### Tool defintion configurable elements
-* If you've specified your own link step, you are going to want to add ${4} to your argument
-list in the place where library files should be added to the command call. For gcc, this is
-often the very end. Other tools may vary.
+1. `:executable` - Command line executable (required).
+ Note: If an executable contains a space (e.g. `Code Cruncher`), and the
+ shell executing the command line generated from the tool definition needs
+ the name quoted, add escaped quotes in the YAML:
-**flags**: configure per-file compilation and linking flags
+ ```yaml
+ :tools:
+ :test_compiler:
+ :executable: \"Code Cruncher\"
+ ```
-Ceedling tools (see later [:tools] section) are used to configure
-compilation and linking of test and source files. These tool
-configurations are a one-size-fits-all approach. Should individual files
-require special compilation or linking flags, the settings in the
-[:flags] section work in conjunction with tool definitions by way of
-argument substitution to achieve this.
+1. `:arguments` - List (array of strings) of command line arguments and
+ substitutions (required).
+
+1. `:name` - Simple name (i.e. "nickname") of tool beyond its
+ executable name. This is optional. If not explicitly set
+ then Ceedling will form a name from the tool's YAML entry key.
+
+1. `:stderr_redirect` - Control of capturing `$stderr` messages
+ {`:none`, `:auto`, `:win`, `:unix`, `:tcsh`}.
+ Defaults to `:none` if unspecified. You may create a custom entry by
+ specifying a simple string instead of any of the recognized
+ symbols. As an example, the `:unix` symbol maps to the string `2>&1`
+ that is automatically inserted at the end of a command line.
+
+ This option is rarely necessary. `$stderr` redirection was originally
+ often needed in early versions of Ceedling. Shell output stream handling
+ is now automatically handled. This option is preserved for possible edge
+ cases.
+
+1. `:optional` - By default a tool you define is required for operation. This
+ means a build will be aborted if Ceedling cannot find your tool’s executable
+ in your environment. However, setting `:optional` to `true` causes this
+ check to be skipped. This is most often needed in plugin scenarios where a
+ tool is only needed if an accompanying configuration option requires it. In
+ such cases, a programmatic option available in plugin Ruby code using the
+ Ceedling class `ToolValidator` exists to process tool definitions as needed.
+
+#### Tool element runtime substitution
+
+To accomplish useful work on multiple files, a configured tool will most often
+require that some number of its arguments or even the executable itself change
+for each run. Consequently, every tool’s argument list and executable field
+possess two means for substitution at runtime.
+
+Ceedling provides two kinds of inline Ruby execution and a notation for
+populating tool elements with dynamically gathered values within the build
+environment.
-* `release`:
+##### Tool element runtime substitution: Inline Ruby execution
- [:compile] or [:link] flags for release build
+Specifically for tool configuration elements, Ceedling provides two types of
+inline Ruby execution.
-* `test`:
+1. `"#{...}"`: This notation is that of the beloved
+ [inline Ruby string expansion][inline-ruby-string-expansion] available in
+ a variety of configuration file sections. This string expansion occurs once
+ at startup.
- [:compile] or [:link] flags for test build
+1. `{...}`: This notation causes inline Ruby execution similarly to the
+ preceding except that the substitution occurs each time the tool is executed.
-Notes:
+ Why might you need this? Say you have a collection of paths on disk and some
+ of those paths include spaces. Further suppose that a single tool that must
+ use those paths requires those spaces to be escaped, but all other uses of
+ those paths requires the paths to remain unchanged. You could use this
+ Ceedling feature to insert Ruby code that iterates those paths and escapes
+ those spaces in the array as used by the tool of this example.
-* Ceedling works with the [:release] and [:test] build contexts
- as-is; plugins can add additional contexts
+##### Tool element runtime substitution: Notational substitution
-* Only [:compile] and [:link] are recognized operations beneath
- a context
+A Ceedling tool's other form of dynamic substitution relies on a `$` notation.
+These `$` operators can exist anywhere in a string and can be decorated in any
+way needed. To use a literal `$`, escape it as `\\$`.
-* File specifiers do not include a path or file extension
+* `$`: Simple substitution for value(s) globally available within the runtime
+ (most often a string or an array).
-* File specifiers are case sensitive (must match original file
- name)
+* `${#}`: When a Ceedling tool's command line is expanded from its configured
+ representation, runs of that tool will be made with a parameter list of
+ substitution values. Each numbered substitution corresponds to a position in
+ a parameter list.
-* File specifiers do support regular expressions if encased in quotes
+ * In the case of a compiler `${1}` will be a C code file path, and `$
+ {2}` will be the file path of the resulting object file.
-* '`*`' is a special (optional) file specifier to provide flags
- to all files not otherwise specified
+ * For a linker `${1}` will be an array of object files to link, and `$
+ {2}` will be the resulting binary executable.
+ * For an executable test fixture `${1}` is either the binary executable
+ itself (when using a local toolchain such as GCC) or a binary input file
+ given to a simulator in its arguments.
-Example [:flags] YAML blurb
+### Example `:tools` YAML blurb
```yaml
-:flags:
- :release:
- :compile:
- :main: # add '-Wall' to compilation of main.c
- - -Wall
- :fan: # add '--O2' to compilation of fan.c
- - --O2
- :'test_.+': # add '-pedantic' to all test-files
- - -pedantic
- :*: # add '-foo' to compilation of all files not main.c or fan.c
- - -foo
- :test:
- :compile:
- :main: # add '--O1' to compilation of main.c as part of test builds including main.c
- - --O1
- :link:
- :test_main: # add '--bar --baz' to linking of test_main.exe
- - --bar
- - --baz
+:tools:
+ :test_compiler:
+ :executable: compiler # Exists in system search path
+ :name: 'acme test compiler'
+ :arguments:
+ - -I"${5}" # Expands to -I search paths from `:paths` section + build directive path macros
+ - -D"${6}" # Expands to all -D defined symbols from `:defines` section
+ - --network-license # Simple command line argument
+ - -optimize-level 4 # Simple command line argument
+ - "#{`args.exe -m acme.prj`}" # In-line Ruby call to shell out & build string of arguments
+ - -c ${1} # Source code input file
+ - -o ${2} # Object file output
+
+ :test_linker:
+ :executable: /programs/acme/bin/linker.exe # Full file path
+ :name: 'acme test linker'
+ :arguments:
+ - ${1} # List of object files to link
+ - -l$-lib: # In-line YAML array substitution to link in foo-lib and bar-lib
+ - foo
+ - bar
+ - -o ${2} # Binary output artifact
+
+ :test_fixture:
+ :executable: tools/bin/acme_simulator.exe # Relative file path to command line simulator
+ :name: 'acme test fixture'
+ :stderr_redirect: :win # Inform Ceedling what model of $stderr capture to use
+ :arguments:
+ - -mem large # Simple command line argument
+ - -f "${1}" # Binary executable input file for simulator
```
-**import**: Load additional config files
+#### `:tools` example blurb notes
+
+* `${#}` is a replacement operator expanded by Ceedling with various
+ strings, lists, etc. assembled internally. The meaning of each
+ number is specific to each predefined default tool (see
+ documentation above).
-In some cases it is nice to have config files (project.yml, options files) which can
-load other config files, for commonly re-used definitions (target processor,
-common code modules, etc).
+* See [search path order][##-search-path-order] to understand how
+ the `-I"${5}"` term is expanded.
+
+* At present, `$stderr` redirection is primarily used to capture
+ errors from test fixtures so that they can be displayed at the
+ conclusion of a test run. For instance, if a simulator detects
+ a memory access violation or a divide by zero error, this notice
+ might go unseen in all the output scrolling past in a terminal.
-These can be recursively nested, the included files can include other files.
+* The built-in preprocessing tools _can_ be overridden with
+ non-GCC equivalents. However, this is highly impractical to do
+ as preprocessing features are quite dependent on the
+ idiosyncrasies and features of the GCC toolchain.
-To import config files, either provide an array of files to import, or use hashes to set imports. The former is useful if you do not anticipate needing to replace a given file for different configurations (project: or options:). If you need to replace/remove imports based on different configuration files, use the hashed version. The two methods cannot be mixed in the same .yml.
+#### Example Test Compiler Tooling
-Example [:import] YAML blurb using array
+Resulting compiler command line construction from preceding example
+`:tools` YAML blurb…
-```yaml
-:import:
- - path/to/config.yml
- - path/to/another/config.yml
+```shell
+> compiler -I"/usr/include” -I”project/tests”
+ -I"project/tests/support” -I”project/source” -I”project/include”
+ -DTEST -DLONG_NAMES -network-license -optimize-level 4 arg-foo
+ arg-bar arg-baz -c project/source/source.c -o
+ build/tests/out/source.o
```
-Example [:import] YAML blurb using hashes
-```yaml
-:import:
- :configA: path/to/config.yml
- :configB: path/to/another/config.yml
-```
+Notes on compiler tooling example:
+- `arg-foo arg-bar arg-baz` is a fabricated example string collected from
+ `$stdout` as a result of shell execution of `args.exe`.
+- The `-c` and `-o` arguments are fabricated examples simulating a single
+ compilation step for a test; `${1}` & `${2}` are single files.
-Ceedling sets values for a subset of CMock settings. All CMock
-options are available to be set, but only those options set by
-Ceedling in an automated fashion are documented below. See CMock
-documentation.
+#### Example Test Linker Tooling
-**cmock**: configure CMock's code generation options and set symbols used to modify CMock's compiled features
-Ceedling sets values for a subset of CMock settings. All CMock options are available to be set, but only those options set by Ceedling in an automated fashion are documented below. See CMock documentation.
+Resulting linker command line construction from preceding example
+`:tools` YAML blurb…
-* `enforce_strict_ordering`:
+```shell
+> \programs\acme\bin\linker.exe thing.o unity.o
+ test_thing_runner.o test_thing.o mock_foo.o mock_bar.o -lfoo-lib
+ -lbar-lib -o build\tests\out\test_thing.exe
+```
- Tests fail if expected call order is not same as source order
+Notes on linker tooling example:
- **Default**: TRUE
+- In this scenario `${1}` is an array of all the object files needed to
+ link a test fixture executable.
-* `mock_path`:
+#### Example Test Fixture Tooling
- Path for generated mocks
+Resulting test fixture command line construction from preceding example
+`:tools` YAML blurb…
- **Default**: /tests/mocks
+```shell
+> tools\bin\acme_simulator.exe -mem large -f "build\tests\out\test_thing.bin 2>&1”
+```
-* `defines`:
+Notes on test fixture tooling example:
- List of conditional compilation symbols used to configure CMock's
- compiled features. See CMock documentation to understand available
- options. No symbols must be set unless defaults are inappropriate for
- your specific environment. All symbols are used only by Ceedling to
- compile CMock C code; contents of [:defines] are ignored by CMock's
- Ruby code when instantiated.
+1. `:executable` could have simply been `${1}` if we were compiling
+ and running native executables instead of cross compiling. That is,
+ if the output of the linker runs on the host system, then the test
+ fixture _is_ `${1}`.
+1. We’re using `$stderr` redirection to allow us to capture simulator error
+ messages to `$stdout` for display at the run's conclusion.
- **Default**: `[]` (empty)
+### Ceedling tool arguments addition shortcut
-* `verbosity`:
+Sometimes Ceedling’s default tool defininitions are _this close_ to being just
+what you need. But, darn, you need one extra argument on the command line, and
+you'd love to not override an entire tool definition to tweak it.
- If not set, defaults to Ceedling's verbosity level
+We got you. Now, this little feature only allows you to add arguments to the
+end of a tool command line. Not the beginning. And, you can’t remove arguments
+with this hack.
-* `plugins`:
+Further, this little feature is a blanket application across all uses of a
+tool. If you need fine-grained control of command line flags in build steps per
+test executable, please see the [`:flags` configuration documentation][flags].
- If [:project][:use_exceptions] is enabled, the internal plugins list is pre-populated with 'cexception'.
+To use this shortcut, simply add a configuration section to your project file
+at the top-level, `:tools_` ↳ `:arguments`. See the list of
+tool names at the beginning of the `:tools` documentation to identify the named
+options. Plugins can also include their own tool definitions that can be
+modified with this same hack.
- Whether or not you have included [:cmock][:plugins] in your
- configuration file, Ceedling automatically adds 'cexception' to the
- plugin list if exceptions are enabled. To add to the list Ceedling
- provides CMock, simply add [:cmock][:plugins] to your configuration
- and specify your desired additional plugins.
+This example YAML:
- Each of the plugins have their own additional documentation.
+```yaml
+:tools_test_compiler:
+ :arguments:
+ - --flag # Add `--flag` to the end of all test C file compilation
+```
+...will produce this command line:
-* `includes`:
+```shell
+ > gcc --flag
+```
- If [:cmock][:unity_helper] set, pre-populated with unity_helper file
- name (no path).
+## `:plugins` Ceedling extensions
- The [:cmock][:includes] list works identically to the plugins list
- above with regard to adding additional files to be inserted within
- mocks as #include statements.
+See the section below dedicated to plugins for more information. This section
+pertains to enabling plugins in your project configuration.
+Ceedling includes a number of built-in plugins. See the collection within
+the project at [plugins/][ceedling-plugins] or the [documentation section below](#ceedling-plugins)
+dedicated to Ceedling’s plugins. Each built-in plugin subdirectory includes
+thorough documentation covering its capabilities and configuration options.
-The last four settings above are directly tied to other Ceedling
-settings; hence, why they are listed and explained here. The
-first setting above, [:enforce_strict_ordering], defaults
-to FALSE within CMock. It is set to TRUE by default in Ceedling
-as our way of encouraging you to use strict ordering. It's a teeny
-bit more expensive in terms of code generated, test execution
-time, and complication in deciphering test failures. However,
-it's good practice. And, of course, you can always disable it
-by overriding the value in the Ceedling YAML configuration file.
+_Note_: Many users find that the handy-dandy [Command Hooks plugin][command-hooks]
+is often enough to meet their needs. This plugin allows you to connect your own
+scripts and command line tools to Ceedling build steps.
+[custom-plugins]: PluginDevelopmentGuide.md
+[ceedling-plugins]: ../plugins/
+[command-hooks]: ../plugins/command_hooks/
-**cexception**: configure symbols used to modify CException's compiled features
+* `:load_paths`:
-* `defines`:
+ Base paths to search for plugin subdirectories or extra Ruby functionality.
- List of conditional compilation symbols used to configure CException's
- features in its source and header files. See CException documentation
- to understand available options. No symbols must be set unless the
- defaults are inappropriate for your specific environment.
+ Ceedling maintains the Ruby load path for its built-in plugins. This list of
+ paths allows you to add your own directories for custom plugins or simpler
+ Ruby files referenced by your Ceedling configuration options elsewhere.
**Default**: `[]` (empty)
+* `:enabled`:
-**unity**: configure symbols used to modify Unity's compiled features
+ List of plugins to be used - a plugin's name is identical to the
+ subdirectory that contains it.
-* `defines`:
+ **Default**: `[]` (empty)
- List of conditional compilation symbols used to configure Unity's
- features in its source and header files. See Unity documentation to
- understand available options. No symbols must be set unless the
- defaults are inappropriate for your specific environment. Most Unity
- defines can be easily configured through the YAML file.
+Plugins can provide a variety of added functionality to Ceedling. In
+general use, it's assumed that at least one reporting plugin will be
+used to format test results (usually `report_tests_pretty_stdout`).
- **Default**: `[]` (empty)
+If no reporting plugins are specified, Ceedling will print to `$stdout` the
+(quite readable) raw test results from all test fixtures executed.
-Example [:unity] YAML blurbs
-```yaml
-:unity: #itty bitty processor & toolchain with limited test execution options
- :defines:
- - UNITY_INT_WIDTH=16 #16 bit processor without support for 32 bit instructions
- - UNITY_EXCLUDE_FLOAT #no floating point unit
+### Example `:plugins` YAML blurb
-:unity: #great big gorilla processor that grunts and scratches
- :defines:
- - UNITY_SUPPORT_64 #big memory, big counters, big registers
- - UNITY_LINE_TYPE=\"unsigned int\" #apparently we're using really long test files,
- - UNITY_COUNTER_TYPE=\"unsigned int\" #and we've got a ton of test cases in those test files
- - UNITY_FLOAT_TYPE=\"double\" #you betcha
-```
-
-
-Notes on Unity configuration:
-
-* **Verification** - Ceedling does no verification of your configuration
- values. In a properly configured setup, your Unity configuration
- values are processed, collected together with any test define symbols
- you specify elsewhere, and then passed to your toolchain during test
- compilation. Unity's conditional compilation statements, your
- toolchain's preprocessor, and/or your toolchain's compiler will
- complain appropriately if your specified configuration values are
- incorrect, incomplete, or incompatible.
-
-* **Routing $stdout** - Unity defaults to using `putchar()` in C's
- standard library to display test results. For more exotic environments
- than a desktop with a terminal (e.g. running tests directly on a
- non-PC target), you have options. For example, you could create a
- routine that transmits a character via RS232 or USB. Once you have
- that routine, you can replace `putchar()` calls in Unity by overriding
- the function-like macro `UNITY_OUTPUT_CHAR`. Consult your toolchain
- and shell documentation. Eventhough this can also be defined in the YAML file
- most shell environments do not handle parentheses as command line arguments
- very well. To still be able to add this functionality all necessary
- options can be defined in the `unity_config.h`. Unity needs to be told to look for
- the `unity_config.h` in the YAML file, though.
-
-Example [:unity] YAML blurbs
```yaml
-:unity:
- :defines:
- - UNITY_INCLUDE_CONFIG_H
-```
+:plugins:
+ :load_paths:
+ - project/tools/ceedling/plugins # Home to your collection of plugin directories.
+ - project/support # Home to some ruby code your custom plugins share.
+ :enabled:
+ - report_tests_pretty_stdout # Nice test results at your command line.
+ - our_custom_code_metrics_report # You created a plugin to scan all code to collect
+ # line counts and complexity metrics. Its name is a
+ # subdirectory beneath the first `:load_path` entry.
-Example unity_config.h
```
-#ifndef UNITY_CONFIG_H
-#define UNITY_CONFIG_H
-#include "uart_output.h" //Helper library for your custom environment
+
-#define UNITY_INT_WIDTH 16
-#define UNITY_OUTPUT_START() uart_init(F_CPU, BAUD) //Helperfunction to init UART
-#define UNITY_OUTPUT_CHAR(a) uart_putchar(a) //Helperfunction to forward char via UART
-#define UNITY_OUTPUT_COMPLETE() uart_complete() //Helperfunction to inform that test has ended
+# Which Ceedling
-#endif
-```
+In certain scenarios you may need to run a different version of Ceedling.
+Typically, Ceedling developers need this ability. But, it could come in
+handy in certain advanced Continuous Integration build scenarios or some
+sort of version behavior comparison.
+It’s not uncommon in Ceedling development work to have the last production
+gem installed while modifying the application code in a locally cloned
+repository. Or, you may be bouncing between local versions of Ceedling to
+troubleshoot changes.
-**tools**: a means for representing command line tools for use under
-Ceedling's automation framework
+Which Ceedling handling gives you options on what gets run.
-Ceedling requires a variety of tools to work its magic. By default,
-the GNU toolchain (`gcc`, `cpp`, `as`) are configured and ready for
-use with no additions to the project configuration YAML file.
-However, as most work will require a project-specific toolchain,
-Ceedling provides a generic means for specifying / overriding
-tools.
+## Which Ceedling background
-* `test_compiler`:
+Ceedling is usually packaged and installed as a Ruby Gem. This gem ends
+up installed in an appropriate place by the `gem` package installer.
+Inside the gem installation is the entire Ceedling project. The `ceedling`
+command line launcher lives in `bin/` while the Ceedling application lives
+in `lib/`. The code in `/bin` manages lots of startup details and base
+configuration. Ultimately, it then launches the main application code from
+`lib/`.
- Compiler for test & source-under-test code
+The features and conventions controlling _which ceedling_ dictate which
+application code the `ceedling` command line handler launches.
- - `${1}`: input source
- - `${2}`: output object
- - `${3}`: optional output list
- - `${4}`: optional output dependencies file
+_Note:_ If you are a developer working on the code in Ceedling’s `bin/`
+and want to run it while a gem is installed, you must take the additional
+step of specifying the path to the `ceedling` launcher in your file system.
- **Default**: `gcc`
+In Unix-like systems, this will look like:
+`> my/ceedling/changes/bin/ceedling `.
-* `test_linker`:
+On Windows systems, you may need to run:
+`> ruby my\ceedling\changes\bin\ceedling `.
- Linker to generate test fixture executables
+## Which Ceedling options and precedence
- - `${1}`: input objects
- - `${2}`: output binary
- - `${3}`: optional output map
- - `${4}`: optional library list
- - `${5}`: optional library path list
+When Ceedling starts up, it evaluates a handful of conditions to determine
+which Ceedling location to launch.
- **Default**: `gcc`
+The following are evaluated in order:
-* `test_fixture`:
+1. Environment variable `WHICH_CEEDLING`. If this environment variable is
+ set, its value is used.
+1. Configuration entry `:project` ↳ `:which_ceedling`. If this is set,
+ its value is used.
+1. The path `vendor/ceedling`. If this path exists in your working
+ directory — typically because of a `--local` vendored installation at
+ project creation — its contents are used to launch Ceedling.
+1. If none of the above exist, the `ceedling` launcher defaults to using
+ the `lib/` directory next to the `bin/` directory from which the
+ `ceedling` launcher is running. In the typical case this is the default
+ gem installation.
- Executable test fixture
+_Note:_ Configuration entry (2) does not make sense in some scenarios.
+When running `ceedling new`, `ceedling examples`, or `ceedling example`
+there is no project file to read. Similarly, `ceedling upgrade` does not
+load a project file; it merely works with the directory structure and
+contets of a project. In these cases, the environment variable is your
+only option to set which Ceedling to launch.
- - `${1}`: simulator as executable with`${1}` as input binary file argument or native test executable
+## Which Ceedling settings
- **Default**: `${1}`
+The environment variable and configuration entry for _Which Ceedling_ can
+contain two values:
-* `test_includes_preprocessor`:
+1. The value `gem` indicates that the command line `ceedling` launcher
+ should run the application packaged alongside it in `lib/` (these
+ paths are typically found in the gem installation location).
+1. A relative or absolute path in your file system. Such a path should
+ point to the top-level directory that contains Ceedling’s `bin/` and
+ `lib/` sub-directories.
- Extractor of #include statements
+
- - `${1}`: input source file
+# Build Directive Macros
- **Default**: `cpp`
+## Overview of Build Directive Macros
-* `test_file_preprocessor`:
+Ceedling supports a small number of build directive macros. At present,
+these macros are only for use in test files.
- Preprocessor of test files (macros, conditional compilation statements)
- - `${1}`: input source file
- - `${2}`: preprocessed output source file
+By placing these macros in your test files, you may control aspects of an
+individual test executable's build from within the test file itself.
- **Default**: `gcc`
+These macros are actually defined in Unity, but they evaluate to empty
+strings. That is, the macros do nothing. But, by placing them in your
+test files they communicate instructions to Ceedling when scanned at
+the beginning of a test build.
-* `test_file_preprocessor_directives`:
+## `TEST_SOURCE_FILE()`
- Preprocessor of test files to expand only conditional compilation statements,
- handle directives, but do not expand macros
+### `TEST_SOURCE_FILE()` Purpose
- - `${1}`: input source file
- - `${2}`: not-fully preprocessed output source file
+The `TEST_SOURCE_FILE()` build directive allows the simple injection of
+a specific source file into a test executable's build.
- **Default**: `gcc`
+The Ceedling convention of compiling and linking any C file that
+corresponds in name to an `#include`d header file does not always work.
+The alternative of `#include`ing a C source file directly is ugly and can
+cause various build problems with duplicated symbols, etc.
-* `test_dependencies_generator`:
+`TEST_SOURCE_FILE()` is also likely the best method for adding an assembly
+file to the build of a given test executable — if assembly support is
+enabled for test builds.
- Discovers deep dependencies of source & test (for incremental builds)
+### `TEST_SOURCE_FILE()` Usage
- - `${1}`: input source file
- - `${2}`: compiled object filepath
- - `${3}`: output dependencies file
+The argument for the `TEST_SOURCE_FILE()` build directive macro is a
+single filename or filepath as a string enclosed in quotation marks. Use
+forward slashes for path separators. The filename or filepath must be
+present within Ceedling’s source file collection.
- **Default**: `gcc`
+To understand your source file collection:
-* `release_compiler`:
+- See the documentation for project file configuration section [`:paths`](#project-paths-configuration).
+- Dump a listing your project’s source files with the command line task
+ `ceedling files:source`.
- Compiler for release source code
+Multiple uses of `TEST_SOURCE_FILE()` are perfectly fine. You’ll likely
+want one per line within your test file.
- - `${1}`: input source
- - `${2}`: output object
- - `${3}`: optional output list
- - `${4}`: optional output dependencies file
+### `TEST_SOURCE_FILE()` Example
- **Default**: `gcc`
+```c
+// Test file test_mycode.c
+#include "unity.h"
+#include "somefile.h"
-* `release_assembler`:
+// There is no file.h in this project to trigger Ceedling’s convention.
+// Compile file.c and link into test_mycode executable.
+TEST_SOURCE_FILE("foo/bar/file.c")
- Assembler for release assembly code
+void setUp(void) {
+ // Do some set up
+}
- - `${1}`: input assembly source file
- - `${2}`: output object file
+// ...
+```
- **Default**: `as`
+## `TEST_INCLUDE_PATH()`
-* `release_linker`:
+### `TEST_INCLUDE_PATH()` Purpose
- Linker for release source code
+The `TEST_INCLUDE_PATH()` build directive allows a header search path to
+be injected into the build of an individual test executable.
- - `${1}`: input objects
- - `${2}`: output binary
- - `${3}`: optional output map
- - `${4}`: optional library list
- - `${5}`: optional library path list
+### `TEST_INCLUDE_PATH()` Usage
- **Default**: `gcc`
+`TEST_INCLUDE_PATH()` entries in your test file are only an additive customization.
+The path will be added to the base / common path list specified by
+`:paths` ↳ `:include` in the project file. If no list is specified in the project
+file, `TEST_INCLUDE_PATH()` entries will comprise the entire header search path list.
-* `release_dependencies_generator`:
+Unless you have a pretty funky C project, generally, at least one search path entry
+is necessary for every test executable. That path can come from a `:paths` ↳ `:include`
+entry in your project configuration or by using `TEST_INCLUDE_PATH()` in your test
+file. Please see [Configuring Your Header File Search Paths][header-file-search-paths]
+for an overview of Ceedling’s conventions on header file search paths.
- Discovers deep dependencies of source files (for incremental builds)
+The argument for the `TEST_INCLUDE_PATH()` build directive macro is a single
+filepath as a string enclosed in quotation marks. Use forward slashes for
+path separators.
- - `${1}`: input source file
- - `${2}`: compiled object filepath
- - `${3}`: output dependencies file
+At present, a limitation of the `TEST_INCLUDE_PATH()` build directive macro is that
+paths are relative to the working directory from which you are executing `ceedling`.
+A change to your working directory could require updates to the path arguments of
+all instances of `TEST_INCLUDE_PATH()`.
- **Default**: `gcc`
+Multiple uses of `TEST_INCLUDE_PATH()` are perfectly fine. You’ll likely want one
+per line within your test file.
+[header-file-search-paths]: #configuring-your-header-file-search-paths
-A Ceedling tool has a handful of configurable elements:
+### `TEST_INCLUDE_PATH()` Example
-1. [:executable] - Command line executable (required)
+```c
+// Test file test_mycode.c
+#include "unity.h"
+#include "somefile.h"
-2. [:arguments] - List of command line arguments
- and substitutions (required)
+// Add the following to the compiler's -I search paths used to
+// compile all components comprising the test_mycode executable.
+TEST_INCLUDE_PATH("foo/bar/")
+TEST_INCLUDE_PATH("/usr/local/include/baz/")
-3. [:name] - Simple name (e.g. "nickname") of tool beyond its
- executable name (if not explicitly set then Ceedling will
- form a name from the tool's YAML entry name)
+void setUp(void) {
+ // Do some set up
+}
-4. [:stderr_redirect] - Control of capturing $stderr messages
- {:none, :auto, :win, :unix, :tcsh}.
- Defaults to :none if unspecified; create a custom entry by
- specifying a simple string instead of any of the available
- symbols.
+// ...
+```
-5. [:background_exec] - Control execution as background process
- {:none, :auto, :win, :unix}.
- Defaults to :none if unspecified.
+
-6. [:optional] - By default a tool is required for operation, which
- means tests will be aborted if the tool is not present. However,
- you can set this to `TRUE` if it's not needed for testing.
+# Ceedling Plugins
+Ceedling includes a number of plugins. See the collection of built-in [plugins/][ceedling-plugins]
+or consult the list with summaries and links to documentation in the subsection
+that follows. Each plugin subdirectory includes full documentation of its
+capabilities and configuration options.
-Tool Element Runtime Substitution
----------------------------------
+To enable built-in plugins or your own custom plugins, see the documentation for
+the `:plugins` section in Ceedling project configuation options.
-To accomplish useful work on multiple files, a configured tool will most
-often require that some number of its arguments or even the executable
-itself change for each run. Consequently, every tool's argument list and
-executable field possess two means for substitution at runtime. Ceedling
-provides two kinds of inline Ruby execution and a notation for
-populating elements with dynamically gathered values within the build
-environment.
+Many users find that the handy-dandy [Command Hooks plugin][command-hooks]
+is often enough to meet their needs. This plugin allows you to connect your own
+scripts and tools to Ceedling build steps.
-Tool Element Runtime Substitution: Inline Ruby Execution
---------------------------------------------------------
+As mentioned, you can create your own plugins. See the [guide][custom-plugins]
+for how to create custom plugins.
-In-line Ruby execution works similarly to that demonstrated for the
-[:environment] section except that substitution occurs as the tool is
-executed and not at the time the configuration file is first scanned.
+[//]: # (Links in this section already defined above)
-* `#{...}`:
+## Ceedling’s built-in plugins, a directory
- Ruby string substitution pattern wherein the containing string is
- expanded to include the string generated by Ruby code between the
- braces. Multiple instances of this expansion can occur within a single
- tool element entry string. Note that if this string substitution
- pattern occurs at the very beginning of a string in the YAML
- configuration the entire string should be enclosed in quotes (see the
- [:environment] section for further explanation on this point).
+### Ceedling plugin `report_tests_pretty_stdout`
-* `{...} `:
+[This plugin][report_tests_pretty_stdout] is meant to tbe the default for
+printing test results to the console. Without it, readable test results are
+still produced but are not nicely formatted and summarized.
- If an entire tool element string is enclosed with braces, it signifies
- that Ceedling should execute the Ruby code contained within those
- braces. Say you have a collection of paths on disk and some of those
- paths include spaces. Further suppose that a single tool that must use
- those paths requires those spaces to be escaped, but all other uses of
- those paths requires the paths to remain unchanged. You could use this
- Ceedling feature to insert Ruby code that iterates those paths and
- escapes those spaces in the array as used by the tool of this example.
+Plugin output includes a well-formatted list of summary statistics, ignored and
+failed tests, and any extraneous output (e.g. `printf()` statements or
+simulator memory errors) collected from executing the test fixtures.
-Tool Element Runtime Substitution: Notational Substitution
-----------------------------------------------------------
+Alternatives to this plugin are:
+
+ * `report_tests_ide_stdout`
+ * `report_tests_gtestlike_stdout`
-A Ceedling tool's other form of dynamic substitution relies on a '$'
-notation. These '$' operators can exist anywhere in a string and can be
-decorated in any way needed. To use a literal '$', escape it as '\\$'.
+Both of the above write to the console test results with a format that is useful
+to IDEs generally in the case of the former, and GTest-aware reporting tools in
+the case of the latter.
-* `$`:
+[report_tests_pretty_stdout]: ../plugins/report_tests_pretty_stdout
- Simple substitution for value(s) globally available within the runtime
- (most often a string or an array).
+### Ceedling plugin `report_tests_ide_stdout`
-* `${#}`:
+[This plugin][report_tests_ide_stdout] prints to the console test results
+formatted similarly to `report_tests_pretty_stdout` with one key difference.
+This plugin's output is formatted such that an IDE executing Ceedling tasks can
+recognize file paths and line numbers in test failures, etc.
- When a Ceedling tool's command line is expanded from its configured
- representation and used within Ceedling Ruby code, certain calls to
- that tool will be made with a parameter list of substitution values.
- Each numbered substitution corresponds to a position in a parameter
- list. Ceedling Ruby code expects that configured compiler and linker
- tools will contain ${1} and ${2} replacement arguments. In the case of
- a compiler ${1} will be a C code file path, and ${2} will be the file
- path of the resulting object file. For a linker ${1} will be an array
- of object files to link, and ${2} will be the resulting binary
- executable. For an executable test fixture ${1} is either the binary
- executable itself (when using a local toolchain such as gcc) or a
- binary input file given to a simulator in its arguments.
+This plugin's formatting is often recognized in an IDE's build window and
+automatically linked for file navigation. With such output, you can select a
+test result in your IDE's execution window and jump to the failure (or ignored
+test) in your test file (more on using [IDEs] with Ceedling, Unity, and
+CMock).
+If enabled, this plugin should be used in place of
+`report_tests_pretty_stdout`.
-Example [:tools] YAML blurbs
+[report_tests_ide_stdout]: ../plugins/report_tests_ide_stdout
-```yaml
-:tools:
- :test_compiler:
- :executable: compiler #exists in system search path
- :name: 'acme test compiler'
- :arguments:
- - -I"$": COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE #expands to -I search paths
- - -I"$": COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR #expands to -I search paths
- - -D$: COLLECTION_DEFINES_TEST_AND_VENDOR #expands to all -D defined symbols
- - --network-license #simple command line argument
- - -optimize-level 4 #simple command line argument
- - "#{`args.exe -m acme.prj`}" #in-line ruby sub to shell out & build string of arguments
- - -c ${1} #source code input file (Ruby method call param list sub)
- - -o ${2} #object file output (Ruby method call param list sub)
- :test_linker:
- :executable: /programs/acme/bin/linker.exe #absolute file path
- :name: 'acme test linker'
- :arguments:
- - ${1} #list of object files to link (Ruby method call param list sub)
- - -l$-lib: #inline yaml array substitution to link in foo-lib and bar-lib
- - foo
- - bar
- - -o ${2} #executable file output (Ruby method call param list sub)
- :test_fixture:
- :executable: tools/bin/acme_simulator.exe #relative file path to command line simulator
- :name: 'acme test fixture'
- :stderr_redirect: :win #inform Ceedling what model of $stderr capture to use
- :arguments:
- - -mem large #simple command line argument
- - -f "${1}" #binary executable input file to simulator (Ruby method call param list sub)
-```
+[IDEs]: https://www.throwtheswitch.org/ide
-Resulting command line constructions from preceding example [:tools] YAML blurbs
+### Ceedling plugin `report_tests_teamcity_stdout`
- > compiler -I"/usr/include” -I”project/tests”
- -I"project/tests/support” -I”project/source” -I”project/include”
- -DTEST -DLONG_NAMES -network-license -optimize-level 4 arg-foo
- arg-bar arg-baz -c project/source/source.c -o
- build/tests/out/source.o
+[TeamCity] is one of the original Continuous Integration server products.
-[notes: (1.) "arg-foo arg-bar arg-baz" is a fabricated example
-string collected from $stdout as a result of shell execution
-of args.exe
-(2.) the -c and -o arguments are
-fabricated examples simulating a single compilation step for
-a test; ${1} & ${2} are single files]
+[This plugin][report_tests_teamcity_stdout] processes test results into TeamCity
+service messages printed to the console. TeamCity's service messages are unique
+to the product and allow the CI server to extract build steps, test results,
+and more from software builds if present.
- > \programs\acme\bin\linker.exe thing.o unity.o
- test_thing_runner.o test_thing.o mock_foo.o mock_bar.o -lfoo-lib
- -lbar-lib -o build\tests\out\test_thing.exe
+The output of this plugin is useful in actual CI builds but is unhelpful in
+local developer builds. See the plugin's documentation for options to enable
+this plugin only in CI builds and not in local builds.
-[note: in this scenario ${1} is an array of all the object files
-needed to link a test fixture executable]
+[TeamCity]: https://jetbrains.com/teamcity
+[report_tests_teamcity_stdout]: ../plugins/report_tests_teamcity_stdout
- > tools\bin\acme_simulator.exe -mem large -f "build\tests\out\test_thing.bin 2>&1”
+### Ceedling plugin `report_tests_gtestlike_stdout`
-[note: (1.) :executable could have simply been ${1} - if we were compiling
-and running native executables instead of cross compiling (2.) we're using
-$stderr redirection to allow us to capture simulator error messages to
-$stdout for display at the run's conclusion]
+[This plugin][report_tests_gtestlike_stdout] collects test results and prints
+them to the console in a format that mimics [Google Test's output][gtest-sample-output].
+Google Test output is both human readable and recognized
+by a variety of reporting tools, IDEs, and Continuous Integration servers.
+If enabled, this plugin should be used in place of
+`report_tests_pretty_stdout`.
-Notes:
+[gtest-sample-output]:
+https://subscription.packtpub.com/book/programming/9781800208988/11/ch11lvl1sec31/controlling-output-with-google-test
+[report_tests_gtestlike_stdout]: ../plugins/report_tests_gtestlike_stdout
-* The upper case names are Ruby global constants that Ceedling
- builds
+### Ceedling plugin `command_hooks`
-* "COLLECTION_" indicates that Ceedling did some work to assemble
- the list. For instance, expanding path globs, combining multiple
- path globs into a convenient summation, etc.
+[This plugin][command-hooks] provides a simple means for connecting Ceedling’s build events to
+Ceedling tool entries you define in your project configuration (see `:tools`
+documentation). In this way you can easily connect your own scripts or command
+line utilities to build steps without creating an entire custom plugin.
-* At present, $stderr redirection is primarily used to capture
- errors from test fixtures so that they can be displayed at the
- conclusion of a test run. For instance, if a simulator detects
- a memory access violation or a divide by zero error, this notice
- might go unseen in all the output scrolling past in a terminal.
+[//]: # (Links defined in a previous section)
-* The preprocessing tools can each be overridden with non-gcc
- equivalents. However, this is an advanced feature not yet
- documented and requires that the replacement toolchain conform
- to the same conventions used by gcc.
+### Ceedling plugin `module_generator`
-**Ceedling Collection Used in Compilation**:
+A pattern emerges in day-to-day unit testing, especially in the practice of
+Test- Driven Development. Again and again, one needs a triplet of a source
+file, header file, and test file — scaffolded in such a way that they refer to
+one another.
-* `COLLECTION_PATHS_TEST`:
+[This plugin][module_generator] allows you to save precious minutes by creating
+these templated files for you with convenient command line tasks.
- All test paths
+[module_generator]: ../plugins/module_generator
-* `COLLECTION_PATHS_SOURCE`:
+### Ceedling plugin `fff`
- All source paths
+The Fake Function Framework, [FFF], is an alternative approach to [test doubles][test-doubles]
+than that used by CMock.
-* `COLLECTION_PATHS_INCLUDE`:
+[This plugin][FFF-plugin] replaces Ceedling generation of CMock-based mocks and
+stubs in your tests with FFF-generated fake functions instead.
- All include paths
+[//]: # (FFF links are defined up in an introductory section explaining CMock)
-* `COLLECTION_PATHS_SUPPORT`:
+### Ceedling plugin `beep`
- All test support paths
+[This plugin][beep] provides a simple audio notice when a test build completes suite
+execution or fails due to a build error. It is intended to support developers
+running time-consuming test suites locally (i.e. in the background).
-* `COLLECTION_PATHS_SOURCE_AND_INCLUDE`:
+The plugin provides a variety of options for emitting audio notificiations on
+various desktop platforms.
- All source and include paths
+[beep]: ../plugins/beep
-* `COLLECTION_PATHS_SOURCE_INCLUDE_VENDOR`:
+### Ceedling plugin `bullseye`
- All source and include paths + applicable vendor paths (e.g.
- CException's source path if exceptions enabled)
+[This plugin][bullseye-plugin] adds additional Ceedling tasks to execute tests
+with code coverage instrumentation provided by the commercial code coverage
+tool provided by [Bullseye]. The Bullseye tool provides visualization and report
+generation from the coverage results produced by an instrumented test suite.
-* `COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE`:
+[bullseye]: http://www.bullseye.com
+[bullseye-plugin]: ../plugins/bullseye
- All test toolchain include paths
+### Ceedling plugin `gcov`
-* `COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE`:
+[This plugin][gcov-plugin] adds additional Ceedling tasks to execute tests with GNU code
+coverage instrumentation. Coverage reports of various sorts can be generated
+from the coverage results produced by an instrumented test suite.
- All test, source, and include paths
+This plugin manages the use of up to three coverage reporting tools. The GNU
+[gcov] tool provides simple coverage statitics to the console as well as to the
+other supported reporting tools. Optional Python-based [GCovr] and .Net-based
+[ReportGenerator] produce fancy coverage reports in XML, JSON, HTML, etc.
+formats.
-* `COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR`:
+[gcov-plugin]: ../plugins/gcov
+[gcov]: http://gcc.gnu.org/onlinedocs/gcc/Gcov.html
+[GCovr]: https://www.gcovr.com/
+[ReportGenerator]: https://reportgenerator.io
- All test, source, include, and applicable vendor paths (e.g. Unity's
- source path plus CMock and CException's source paths if mocks and
- exceptions are enabled)
+### Ceedling plugin `report_tests_log_factory`
-* `COLLECTION_PATHS_RELEASE_TOOLCHAIN_INCLUDE`:
+[This plugin][report_tests_log_factory] produces any or all of three useful test
+suite reports in JSON, JUnit, or CppUnit format. It further provides a
+mechanism for users to create their own custom reports with a small amount of
+custom Ruby rather than a full plugin.
- All release toolchain include paths
+[report_tests_log_factory]: ../plugins/report_tests_log_factory
-* `COLLECTION_DEFINES_TEST_AND_VENDOR`:
+### Ceedling plugin `report_build_warnings_log`
- All symbols specified in [:defines][:test] + symbols defined for
- enabled vendor tools - e.g. [:unity][:defines], [:cmock][:defines],
- and [:cexception][:defines]
+[This plugin][report_build_warnings_log] scans the output of build tools for console
+warning notices and produces a simple text file that collects all such warning
+messages.
-* `COLLECTION_DEFINES_RELEASE_AND_VENDOR`:
+[report_build_warnings_log]: ../plugins/report_build_warnings_log
- All symbols specified in [:defines][:release] plus symbols defined by
-[:cexception][:defines] if exceptions are enabled
+### Ceedling plugin `report_tests_raw_output_log`
+[This plugin][report_tests_raw_output_log] captures extraneous console output
+generated by test executables — typically for debugging — to log files named
+after the test executables.
-Notes:
+[report_tests_raw_output_log]: ../plugins/report_tests_raw_output_log
-* Other collections exist within Ceedling. However, they are
- only useful for advanced features not yet documented.
+### Ceedling plugin `subprojects`
-* Wherever multiple path lists are combined for use Ceedling prioritizes
- path groups as follows: test paths, support paths, source paths, include
- paths.
- This can be useful, for instance, in certain testing scenarios
- where we desire Ceedling or the compiler to find a stand-in header file
- before the actual source header file of the same name.
+[This plugin][subprojects] supports subproject release builds of static
+libraries. It manages differing sets of compiler flags and linker flags that
+fit the needs of different library builds.
+[subprojects]: ../plugins/subprojects
-**plugins**: Ceedling extensions
+### Ceedling plugin `dependencies`
-* `load_paths`:
+[This plugin][dependencies] manages release build dependencies including
+fetching those dependencies and calling a given dependenc's build process.
+Ultimately, this plugin generates the components needed by your Ceedling
+release build target.
- Base paths to search for plugin subdirectories or extra ruby functionalit
+[dependencies]: ../plugins/dependencies
- **Default**: `[]` (empty)
+### Ceedling plugin `compile_commands_json_db`
-* `enabled`:
+[This plugin][compile_commands_json_db] create a [JSON Compilation Database][json-compilation-database].
+This file is useful to [any code editor or IDE][lsp-tools] that implements
+syntax highlighting, etc. by way of the LLVM project's [`clangd`][clangd]
+Language Server Protocol conformant language server.
- List of plugins to be used - a plugin's name is identical to the
- subdirectory that contains it (and the name of certain files within
- that subdirectory)
+[compile_commands_json_db]: ../plugins/compile_commands_json_db
+[lsp-tools]: https://microsoft.github.io/language-server-protocol/implementors/tools/
+[clangd]: https://clangd.llvm.org
+[json-compilation-database]: https://clang.llvm.org/docs/JSONCompilationDatabase.html
- **Default**: `[]` (empty)
+
+# Global Collections
-Plugins can provide a variety of added functionality to Ceedling. In
-general use, it's assumed that at least one reporting plugin will be
-used to format test results. However, if no reporting plugins are
-specified, Ceedling will print to `$stdout` the (quite readable) raw
-test results from all test fixtures executed.
+Collections are Ruby arrays and Rake FileLists (that act like
+arrays). Ceedling did work to populate and assemble these by
+processing the project file, using internal knowledge,
+expanding path globs, etc. at startup.
-Example [:plugins] YAML blurb
+Collections are globally available Ruby constants. These
+constants are documented below. Collections are also available
+via accessors on the `Configurator` object (same names but all
+lower case methods).
-```yaml
-:plugins:
- :load_paths:
- - project/tools/ceedling/plugins #home to your collection of plugin directories
- - project/support #maybe home to some ruby code your custom plugins share
- :enabled:
- - stdout_pretty_tests_report #nice test results at your command line
- - our_custom_code_metrics_report #maybe you needed line count and complexity metrics, so you
- #created a plugin to scan all your code and collect that info
-```
+Global collections are typically used in Rakefiles, plugins,
+and Ruby scripts where the contents tend to be especially
+handy for crafting custom functionality.
-* `stdout_pretty_tests_report`:
+Once upon a time collections were a core component of Ceedling.
+As the tool has grown in sophistication and as many of its
+features now operate per test executable, the utility of and
+number of collections has dwindled. Previously, nearly all
+Ceedling actions happened in bulk and with the same
+collections used for all tasks. This is no longer true.
- Prints to $stdout a well-formatted list of ignored and failed tests,
- final test counts, and any extraneous output (e.g. printf statements
- or simulator memory errors) collected from executing the test
- fixtures. Meant to be used with runs at the command line.
+* `COLLECTION_PROJECT_OPTIONS`:
-* `stdout_ide_tests_report`:
+ All project option files with path found in the configured
+ options paths having the configured YAML file extension.
- Prints to $stdout simple test results formatted such that an IDE
- executing test-related Rake tasks can recognize file paths and line
- numbers in test failures, etc. Thus, you can click a test result in
- your IDE's execution window and jump to the failure (or ignored test)
- in your test file (obviously meant to be used with an [IDE like
- Eclipse][ide], etc).
+* `COLLECTION_ALL_TESTS`:
- [ide]: http://throwtheswitch.org/white-papers/using-with-ides.html
+ All files with path found in the configured test paths
+ having the configured source file extension.
-* `xml_tests_report`:
+* `COLLECTION_ALL_ASSEMBLY`:
- Creates an XML file of test results in the xUnit format (handy for
- Continuous Integration build servers or as input to other reporting
- tools). Produces a file report.xml in /artifacts/tests.
+ All files with path found in the configured source and
+ test support paths having the configured assembly file
+ extension.
-* `bullseye`:
+* `COLLECTION_ALL_SOURCE`:
- Adds additional Rake tasks to execute tests with the commercial code
- coverage tool provided by [Bullseye][]. See readme.txt inside the bullseye
- plugin directory for configuration and use instructions. Note:
- Bullseye only works with certain compilers and linkers (healthy list
- of supported toolchains though).
+ All files with path found in the configured source paths
+ having the configured source file extension.
- [bullseye]: http://www.bullseye.com
+* `COLLECTION_ALL_HEADERS`:
-* `gcov`:
+ All files with path found in the configured include,
+ support, and test paths having the configured header file
+ extension.
- Adds additional Rake tasks to execute tests with the GNU code coverage
- tool [gcov][]. See readme.txt inside the gcov directory for configuration
- and use instructions. Only works with GNU compiler and linker.
+* `COLLECTION_ALL_SUPPORT`:
- [gcov]: http://gcc.gnu.org/onlinedocs/gcc/Gcov.html
+ All files with path found in the configured test support
+ paths having the configured source file extension.
-* `warnings_report`:
+* `COLLECTION_PATHS_INCLUDE`:
- Scans compiler and linker `$stdout / $stderr` output for the word
- 'warning' (case insensitive). All code warnings (or tool warnings) are
- logged to a file warnings.log in the appropriate `/artifacts` directory (e.g. test/ for test tasks, `release/` for a
- release build, or even `bullseye/` for bullseye runs).
+ All configured include paths.
-Module Generator
-========================
-Ceedling includes a plugin called module_generator that will create a source, header and test file for you.
-There are several possibilities to configure this plugin through your project.yml to suit your project's needs.
+* `COLLECTION_PATHS_SOURCE`:
-Directory Structure
--------------------------------------------
+ All configured source paths.
-The default configuration for directory/project structure is:
-```yaml
-:module_generator:
- :project_root: ./
- :source_root: src/
- :test_root: test/
-```
-You can change these variables in your project.yml file to comply with your project's directory structure.
+* `COLLECTION_PATHS_SUPPORT`:
-If you call `ceedling module:create`, it will create three files:
-1. A source file in the source_root
-2. A header file in the source_root
-3. A test file in the test_root
+ All configured support paths.
-If you want your header file to be in another location,
-you can specify the ':inc_root:" in your project.yml file:
-```yaml
-:module_generator:
- :inc_root: inc/
-```
-The module_generator will then create the header file in your defined ':inc_root:'.
-By default, ':inc_root:' is not defined so the module_generator will use the source_root.
-
-Sometimes, your project can't be divided into a single src, inc, and test folder. You have several directories
-with sources/..., something like this for example:
-
- - myDriver
- - src
- - inc
- - test
- - myOtherDriver
- - src
- - inc
- - test
- - ...
-
-Don't worry, you don't have to manually create the source/header/test files.
-The module_generator can accept a path to create a source_root/inc_root/test_root folder with your files:
-`ceedling module:create[:]`
-
-F.e., applied to the above project structure:
-`ceedling module:create[myOtherDriver:driver]`
-This will make the module_generator run in the subdirectory 'myOtherDriver' and generate the module files
-for you in that directory. So, this command will generate the following files:
-1. A source file 'driver.c' in /myOtherDriver/
-2. A header file 'driver.h' in /myOtherDriver/ (or if specified)
-3. A test file 'test_driver.c' in /myOtherDriver/
-
-Naming
--------------------------------------------
-By default, the module_generator will generate your files in lowercase.
-`ceedling module:create[mydriver]` and `ceedling module:create[myDriver]`(note the uppercase) will generate the same files:
-1. mydriver.c
-2. mydriver.h
-3. test_mydriver.c
-
-You can configure the module_generator to use a differect naming mechanism through the project.yml:
-```yaml
-:module_generator:
- :naming: "camel"
-```
-There are other possibilities as well (bumpy, camel, snake, caps).
-Refer to the unity module generator for more info (the unity module generator is used under the hood by module_generator).
+* `COLLECTION_PATHS_TEST`:
+ All configured test paths.
-Boilerplate header
--------------------------------------------
-There are two ways of adding a boilerplate header comment to your generated files:
-* With a defined string in the project.yml file:
+* `COLLECTION_PATHS_SOURCE_AND_INCLUDE`:
-```yaml
-:module_generator:
- :boilerplates:
- :src: '/* This is Boilerplate code. */'
-```
+ All configured source and include paths.
-Using the command **ceedling module:create[foo]** it creates the source module as follows:
+* `COLLECTION_PATHS_SOURCE_INCLUDE_VENDOR`:
-```c
-/* This is Boilerplate code. */
-#include "foo.h"
-```
+ All configured source and include paths plus applicable
+ vendor paths (Unity's source path plus CMock and
+ CException's source paths if mocks and exceptions are
+ enabled).
-It would be the same for **:tst:** and **:inc:** adding its respective options.
+* `COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE`:
-* Defining an external file with boileplate code:
+ All configured test, support, source, and include paths.
-```yml
-:module_generator:
- :boilerplate_files:
- :src: '\src_boilerplate.txt'
- :inc: '\inc_boilerplate.txt'
- :tst: '\tst_boilerplate.txt'
-```
+* `COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR`:
-For whatever file names in whichever folder you desire.
+ All test, support, source, include, and applicable
+ vendor paths (Unity's source path plus CMock and
+ CException's source paths if mocks and exceptions are
+ enabled).
+* `COLLECTION_PATHS_RELEASE_TOOLCHAIN_INCLUDE`:
-Advanced Topics (Coming)
-========================
+ All configured release toolchain include paths.
-Modifying Your Configuration without Modifying Your Project File: Option Files & User Files
--------------------------------------------------------------------------------------------
+* `COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE`:
-Modifying your project file without modifying your project file
+ All configured test toolchain include paths.
-Debugging and/or printf()
--------------------------
+* `COLLECTION_PATHS_VENDOR`:
-When you gotta get your hands dirty...
+ Unity's source path plus CMock and CException's source
+ paths if mocks and exceptions are enabled.
-Ceedling Plays Nice with Others - Using Ceedling for Tests Alongside Another Release Build Setup
-------------------------------------------------------------------------------------------------
+* `COLLECTION_VENDOR_FRAMEWORK_SOURCES`:
-You've got options.
+ Unity plus CMock, and CException's .c filenames (without
+ paths) if mocks and exceptions are enabled.
-Adding Handy Rake Tasks for Your Project (without Fancy Pants Custom Plugins)
------------------------------------------------------------------------------
+* `COLLECTION_RELEASE_BUILD_INPUT`:
-Add a file `rakefile.rb` at the root of your project that loads Ceedling. This
-differs whether you are using the gem version or a local Ceedling version.
+ * All files with path found in the configured source
+ paths having the configured source file extension.
+ * If exceptions are enabled, the source files for
+ CException.
+ * If assembly support is enabled, all assembly files
+ found in the configured paths having the configured
+ assembly file extension.
-Gem Version:
-```ruby
-require('ceedling')
-Ceedling.load_project
-```
+* `COLLECTION_EXISTING_TEST_BUILD_INPUT`:
-Local Ceedling Version (assuming local ceedling is in `vendor/ceedling`):
-```ruby
-PROJECT_CEEDLING_ROOT = "vendor/ceedling"
-load "#{PROJECT_CEEDLING_ROOT}/lib/ceedling.rb"
-Ceedling.load_project
-```
+ * All files with path found in the configured source
+ paths having the configured source file extension.
+ * All files with path found in the configured test
+ paths having the configured source file extension.
+ * Unity's source files.
+ * If exceptions are enabled, the source files for
+ CException.
+ * If mocks are enabled, the C source files for CMock.
+ * If assembly support is enabled, all assembly files
+ found in the configured paths having the configured
+ assembly file extension.
-Now you simply add your rake task to the file e.g.:
-```ruby
-desc "Print hello world in sh" # Only tasks with description are listed by ceedling -T
-task :hello_world do
- sh "echo Hello World!"
-end
-```
+ This collection does not include .c files generated by
+ Ceedling and its supporting frameworks at build time
+ (e.g. test runners and mocks). Further, this collection
+ does not include source files added to a test
+ executable's build list with the `TEST_SOURCE_FILE()`
+ build directive macro.
-The task can now be called with: `ceedling hello_world`
+* `COLLECTION_RELEASE_ARTIFACT_EXTRA_LINK_OBJECTS`:
-Working with Non-Desktop Testing Environments
----------------------------------------------
+ If exceptions are enabled, CException's .c filenames
+ (without paths) remapped to configured object file
+ extension.
-For those crazy platforms lacking command line simulators and for which
-cross-compiling on the desktop just ain't gonna get it done.
+* `COLLECTION_TEST_FIXTURE_EXTRA_LINK_OBJECTS`:
-Creating Custom Plugins
------------------------
+ All test support source filenames (without paths)
+ remapped to configured object file extension.
-Oh boy. This is going to take some explaining.
+
diff --git a/docs/Changelog.md b/docs/Changelog.md
new file mode 100644
index 000000000..1846da98d
--- /dev/null
+++ b/docs/Changelog.md
@@ -0,0 +1,331 @@
+# 🌱 Ceedling Changelog
+
+This format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+This changelog is complemented by two other documents:
+
+1. 🔊 **[Release Notes](ReleaseNotes.md)** for announcements, education, acknowledgements, and known issues.
+1. 💔 **[Breaking Changes](BreakingChanges.md)** for a list of impacts to existing Ceedling projects.
+
+---
+
+# [1.0.0 pre-release] — 2024-05-31
+
+## 🌟 Added
+
+### Parallel execution of build steps
+
+As was explained in the _[Highlights](#-Highlights)_, Ceedling can now run its internal tasks in parallel and take full advantage of your build system’s resources. Even lacking various optimizations (see _[Known Issues](#-Known-Issues)_) builds are now often quite speedy.
+
+Enabling this speedup requires either or both of two simple configuration settings. See Ceedling’s [documentation](CeedlingPacket.md) for `:project` ↳ `:compile_threads` and `:project` ↳ `:test_threads`.
+
+### A proper command line
+
+Ceedling now offers a full command line interface with rich help, useful order-independent option flags, and more.
+
+The existing `new`, `upgrade`, `example`, and `exampples` commands remain but have been improved. For those commands that support it, you may now specify the project file to load (see new, related mixins feature discussed elsewhere), log file to write to, exit code handling behavior, and more from the command line.
+
+Try `ceedling help` and then `ceedling help ` to get started.
+
+See the _[Release Notes](ReleaseNotes.md)_ and _[CeedlingPacket](CeedlingPacket.md)_ for more on the new and improved command line.
+
+### `TEST_INCLUDE_PATH(...)` & `TEST_SOURCE_FILE(...)`
+
+Issue [#743](https://github.com/ThrowTheSwitch/Ceedling/issues/743)
+
+Using what we are calling build directive macros, you can now provide Ceedling certain configuration details from inside a test file.
+
+See the [documentation](CeedlingPacket.md) discussion on include paths, Ceedling conventions, and these macros to understand all the details.
+
+_Note:_ Ceedling is not yet capable of preserving build directive macros through preprocessing of test files. If, for example, you wrap these macros in
+ conditional compilation preprocessing statements, they will not work as you expect.
+
+#### `TEST_INCLUDE_PATH(...)`
+
+In short, `TEST_INCLUDE_PATH()` allows you to add a header file search path to the build of the test executable in which it is found. This can mean much shorter compilation command lines and good flexibility for complicated projects.
+
+#### `TEST_SOURCE_FILE(...)`
+
+In short, `TEST_SOURCE_FILE()` allows you to be explicit as to which source C files should be compiled and linked into a test executable. Sometimes Ceedling’s convention for matching source files with test files by way of `#include`d header files does not meet the need. This solves the problems of those scenarios.
+
+### Mixins for modifying your configuration
+
+Thorough documentation on Mixins can be found in _[CeedlingPacket](CeedlingPacket.md)_.
+
+### Additional options for loading a base configuration from a project file
+
+Once upon a time, you could load a project configuration in just two simple ways — _project.yml_ in your working directory and an environment variable pointing to a different file. Those days are over.
+
+You may now:
+
+* Load your project configuration from a filepath provided at the command line.
+* Load your project configuration from an environment variable hoding a filepath.
+* Load your project configuration from the default _project.yml_ in your working directory.
+* Modify your configuration with Mixins loaded from your project file, environment variables, and/or from the command line.
+
+All the options for loading and modifying a project configuration are thoroughly documented in _[CeedlingPacket](CeedlingPacket.md))_.
+
+### Broader crash detection in test suites and new backtrace abilities
+
+Previously Ceedling had a limited ability to detect and report segmentation faults and primarily on Unix-like platforms. This has been expanded and improved to crash detection more broadly. Invalid memory accesses, stack overflows, heap errors, and branching problems can all lead to crashed test executables. Ceedling is now able to detect these across platforms and report on them appropriately.
+
+See _[CeedlingPacket](CeedlingPacket.md))_ for the new `:project` ↳ `:use_backtrace` feature to control how much detail is extracted from a crashed test executable to help you find the cause.
+
+### More better `:flags` handling
+
+Issue [#43](https://github.com/ThrowTheSwitch/Ceedling/issues/43)
+
+Each test executable is now built as a mini project. Using improved `:flags` handling and an updated section format within Ceedling’s project configuration, you have much better options for specifying flags presented to the various tools within your build, particulary within test builds.
+
+### More better `:defines` handling
+
+Each test executable is now built as a mini project. Using improved `:defines` handling and an updated section format within Ceedling’s project configuration, you have much better options for specifying symbols used in your builds' compilation steps, particulary within test builds.
+
+One powerful new feature is the ability to test the same source file built differently for different tests. Imagine a source file has three different conditional compilation sections. You can now write unit tests for each of those sections without complicated gymnastics to cause your test suite to build and run properly.
+
+### Inline Ruby string expansion for `:flags` and `:defines` configuration entries
+
+Inline Ruby string expansion has been, well, expanded for use in `:flags` and `:defines` entries to complement existing such functionality in `:environment`, `:paths`, `:tools`, etc.
+
+The previously distributed documentation for inline Ruby string expansion has been collected into a single subsection within the project file documentation and improved.
+
+### `report_tests_log_factory` plugin
+
+This new plugin consolidates a handful of previously discrete report gernation plugins into a single plugin that also enables low-code, custom, end-user created reports.
+
+The output of these prior plugins are now simply configuration options for this new plugin:
+
+1. `junit_tests_report`
+1. `json_tests_report`
+1. `xml_tests_report`
+
+This new plugin also includes the option to generate an HTML report (see next section).
+
+### HTML tests report
+
+A community member submitted an [HTML report generation plugin](https://github.com/ThrowTheSwitch/Ceedling/pull/756/) that was not officially released before 0.32. It has been absorbed into the new `report_tests_log_factory` plugin (see previous section).
+
+### Improved Segfault Handling
+
+Segmentation faults are now reported as failures instead of an error with as much detail as possible. See the project configuration file documentation on `:project` ↳ `:use_backtrace` for more!
+
+### Pretty logging output
+
+Ceedling logging now optionally includes emoji and nice Unicode characters. Ceedling will attempt to determine if your platform supports it. You can use the environment variable `CEEDLING_DECORATORS` to force the feature on or off. See the documentation for logging decorators in _[CeedlingPacket](CeedlingPacket.md)_.
+
+### Vendored license files
+
+The application commands `ceedling new` and `ceedling upgrade` at the command line provide project creation and management functions. Optionally, these commands can vendor tools and libraries locally alongside your project. These vendoring options now include license files along with the source of the vendored tools and libraries.
+
+## 💪 Fixed
+
+### `:paths` and `:files` handling bug fixes and clarification
+
+Most project configurations are relatively simple, and Ceedling’s features for collecting paths worked fine enough. However, bugs and ambiguities lurked. Further, insufficient validation left users resorting to old fashioned trial-and-error troubleshooting.
+
+Much glorious filepath and pathfile handling now abounds:
+
+* The purpose and use of `:paths` and `:files` has been clarified in both code and documentation. `:paths` are directory-oriented while `:files` are filepath-oriented.
+* [Documentation](CeedlingPacket.md) is now accurate and complete.
+* Path handling edge cases have been properly resolved (`./foo/bar` is the same as `foo/bar` but was not always processed as such).
+* Matching globs were advertised in the documentation (erroneously, incidentally) but lacked full programmatic support.
+* Ceedling now tells you if your matching patterns don't work. Unfortunately, all Ceedling can determine is if a particular pattern yielded 0 results.
+
+### Bug fixes for command line build tasks `files:header` and `files:support`
+
+Longstanding bugs produced duplicate and sometimes incorrect lists of header files. Similarly, support file lists were not properly expanded from globs. Both of these problems have been fixed. The `files:header` command line task has replaced the `files:include` task.
+
+### Dashed filename handling bug fix
+
+Issue [#780](https://github.com/ThrowTheSwitch/Ceedling/issues/780)
+
+In certain combinations of Ceedling features, a dash in a C filename could cause Ceedling to exit with an exception. This has been fixed.
+
+### Source filename extension handling bug fix
+
+Issue [#110](https://github.com/ThrowTheSwitch/Ceedling/issues/110)
+
+Ceedling has long had the ability to configure a source filename extension other than `.c` (`:extension` ↳ `:source`). However, in most circumstances this ability would lead to broken builds. Regardless of user-provided source files and filename extenion settings, Ceedling’s supporting frameworks — Unity, CMock, and CException — all have `.c` file components. Ceedling also generates mocks and test runners with `.c` filename extensions regardless of any filename extension setting. Changing the source filename extension would cause Ceedling to miss its own core source files. This has been fixed.
+
+### Bug fixes for `gcov` plugin
+
+The most commonly reported bugs have been fixed:
+
+* `nil` references
+* Exit code issues with recent releases of `gcov`
+* Empty coverage results and related build failures
+
+### Bug fixes for `beep` plugin
+
+A handful of small bugs in using shell `echo` with the ASCII bell character have been fixed.
+
+### Which Ceedling handling includes new environment variable `WHICH_CEEDLING`
+
+A previously semi-documented feature allowed you to point to a version of Ceedling on disk to run from within your project file, `:project` ↳ `:which_ceedling`.
+
+This feature is primarily of use to Ceedling developers but can be useful in other specialized scenarios. See the documentation in _[CeedlingPacket](CeedlingPacket.md))_ for full deatils as this is an advanced feature.
+
+The existing feature has been improved with logging and validation as well as proper documentation. An environment variable `WHICH_CEEDLING` is now also supported. If set, this variable supersedes any other settings. In the case of `ceedling new` and `ceedling upgrade`, it is the only way to change which Ceedling is in use as a project file either does not exist for the former or is not loaded for the latter.
+
+## ⚠️ Changed
+
+### Preprocessing improvements
+
+Issues [#806](https://github.com/ThrowTheSwitch/Ceedling/issues/806) + [#796](https://github.com/ThrowTheSwitch/Ceedling/issues/796)
+
+Preprocessing refers to expanding macros and other related code file text manipulations often needed in sophisticated projects before key test suite generation steps. Without (optional) preprocessing, generating test funners from test files and generating mocks from header files lead to all manner of build shenanigans.
+
+The preprocessing needed by Ceedling for sophisticated projects has always been a difficult feature to implement. The most significant reason is simply that there is no readily available cross-platform C code preprocessing tool that provides Ceedling everything it needs to do its job. Even gcc’s `cpp` preprocessor tool comes up short. Over time Ceedling’s attempt at preprocessing grew more brittle and complicated as community contribturs attempted to fix it or cause it to work properly with other new features.
+
+This release of Ceedling stripped the feature back to basics and largely rewrote it within the context of the new build pipeline. Complicated regular expressions and Ruby-generated temporary files have been eliminated. Instead, Ceedling now blends two reports from gcc' `cpp` tool and complements this with additional context. In addition, preprocessing now occurs at the right moments in the overall build pipeline.
+
+While this new approach is not 100% foolproof, it is far more robust and far simpler than previous attempts. Other new Ceedling features should be able to address shortcomings in edge cases.
+
+### Project file environment variable name change `CEEDLING_MAIN_PROJECT_FILE` ➡️ `CEEDLING_PROJECT_FILE`
+
+Options and support for loading a project configuration have expanded significantly, mostly notably with the addition of Mixins.
+
+The environment variable option for pointing Ceedling to a project file other than _project.yml_ in your working directory has been renamed `CEEDLING_MAIN_PROJECT_FILE` ➡️ `CEEDLING_PROJECT_FILE`.
+
+Documentation on Mixins and the new options for loading a project configuration are thoroughly documented in _[CeedlingPacket](CeedlingPacket.md))_.
+
+### Plugin system improvements
+
+1. The plugin subsystem has incorporated logging to trace plugin activities at high verbosity levels.
+1. Additional events have been added for test preprocessing steps (the popular and useful [`command_hooks` plugin](plugins/command_hooks/README.md) has been updated accordingly).
+1. Built-in plugins have been updated for thread-safety as Ceedling is now able to execute builds with multiple threads.
+
+### Logging improvements
+
+Logging messages are more useful. A variety of logging messages have been added throughout Ceedling builds. Message labels (e.g. `ERROR:`) are now applied automatically). Exception handling is now centralized and significantly cleans up exception messages (backtraces are available with debug verbosity).
+
+### Exit code options for test suite failures
+
+Be default Ceedling terminates with an exit code of `1` when a build succeeds but unit tests fail.
+
+A previously undocumented project configuration option `:graceful_fail` could force a Ceedling exit code of `0` upon test failures.
+
+This configuration option has moved but is now [documented](CeedlingPacket.md). It is also available as a new command line argument (`--graceful-fail`).
+
+```yaml
+:test_build:
+ :graceful_fail: TRUE
+```
+
+### Improved Segfault Handling in Test Suites
+
+Segmentation faults are now reported as failures instead of an error with as much detail as possible. See the project configuration file documentation discussing the `:project` ↳ `:use_backtrace` option for more!
+
+### Altered local documentation file directory structure
+
+The application commands `ceedling new` and `ceedling upgrade` at the command line provide options for local copies of documentation when creating or upgrading a project. Previous versions of Ceedling used a flat file structure for the _docs/_ directory. Ceedling now uses subdirectories to organize plugin and tool documentation within the _docs/_ directory for clearer organization and preserving original filenames.
+
+### JUnit, XML & JSON test report plugins consolidation
+
+The three previously discrete plugins listed below have been consolidated into a single new plugin, `report_tests_log_factory`:
+
+1. `junit_tests_report`
+1. `json_tests_report`
+1. `xml_tests_report`
+
+`report_tests_log_factory` is able to generate all 3 reports of the plugins it replaces, a new HTML report, and custom report formats with a small amount of user-written Ruby code (i.e. not an entire Ceedling plugun). See its [documentation](../plugins/report_tests_log_factory) for more.
+
+The report format of the previously independent `xml_tests_report` plugin has been renamed from _XML_ in all instances to _CppUnit_ as this is the specific test reporting format the former plugin and new `report_tests_log_factory` plugin outputs.
+
+In some circumstances, JUnit report generation would yield an exception in its routines for reorganizing test results (Issues [#829](https://github.com/ThrowTheSwitch/Ceedling/issues/829) & [#833](https://github.com/ThrowTheSwitch/Ceedling/issues/833)). The true source of the nil test results entries has likely been fixed but protections have also been added in JUnit report generation as well.
+
+### Improvements and changes for `gcov` plugin
+
+1. Documentation has been significantly updated including a _Troubleshooting_ for common issues.
+1. Compilation with coverage now only occurs for the source files under test and no longer for all C files (i.e. coverage for unity.c, mocks, and test files that is meaningless noise has been eliminated).
+1. Coverage summaries printed to the console after `gcov:` test task runs now only concern the source files exercised instead of all source files. A final coverage tally has been restored.
+1. Coverage summaries can now be disabled.
+1. Coverage reports are now automatically generated after `gcov:` test tasks are executed. This behvaior can be disabled with a new configuration option. When enabled, a separate task is made available to trigger report generation.
+1. To maintain consistency, repports generated by `gcovr` and `reportgenerator` are written to subdirectories named for the respective tools benath the `gcov/` artifacts path.
+
+See the [gcov plugin’s documentation](plugins/gcov/README.md).
+
+### Improvements for `compile_commands_json_db` plugin
+
+1. The plugin creates a compilation database that distinguishes the same code file compiled multiple times with different configurations as part of the new test suite build structure. It has been updated to work with other Ceedling changes.
+1. Documentation has been greatly revised.
+
+### Improvements for `beep` plugin
+
+1. Additional sound tools — `:tput`, `:beep`, and `:say` — have been added for more platform sound output options and fun.
+1. Documentation has been greatly revised.
+1. The plugin more properly uses looging and system shell calls.
+
+## 👋 Removed
+
+### `verbosity` and `log` command line tasks
+
+These command line features were implemented using Rake. That is, they were Rake tasks, not command line switches, and they were subject to the peculiarities of Rake tasks. Specifically, order mattered — these tasks had to precede build tasks they were to affect — and `verbosity` required a non-standard parameter convention for numeric values.
+
+These command line tasks no longer exist. They are now proper command line flags. These are most useful (and, in the case of logging, only availble) with Ceedling’s new `build` command line argument. The `build` command takes a list of build & plugin tasks to run. It is now complmented by `--verbosity`, `--log`, and `--logfile` flags. See the detailed help at `ceedling help build` for these.
+
+The `build` keyword is optional. That is, omitting it is allowed and operates largely equivalent to the historical Ceedling command line.
+
+The previous command line of `ceedling verbosity[4] test:all release` or `ceedling verbosity:obnoxious test:all release` can now be any of the following:
+
+* `ceedling test:all release --verbosity=obnoxious`
+* `ceedling test:all release -v 4`
+* `ceedling --verbosity=obnoxious test:all release`
+* `ceedling -v 4 test:all release`
+
+Note that in the above list Ceedling is actually executing as though `ceedling build ` were entered at the command line. It is entirely acceptable to use the full form. The above list is provided as its form is the simplest to enter and consistent with previous versions of Ceedling.
+
+### `options:` tasks
+
+Options files were a simple but limited way to merge configuration with your base configuration from the command line. This feature has been superseded by Ceedling Mixins.
+
+### Test suite smart rebuilds
+
+All “smart” rebuild features built around Rake no longer exist. That is, incremental test suite builds for only changed files are no longer possible. Any test build is a full rebuild of its components (the speed increase due to parallel build tasks more than makes up for this).
+
+These project configuration options related to smart builds are no longer recognized:
+ - `:use_deep_dependencies`
+ - `:generate_deep_dependencies`
+ - `:auto_link_deep_dependencies`
+
+In future revisions of Ceedling, smart rebuilds will be brought back (without relying on Rake) and without a list of possibly conflicting configuation options to control related features.
+
+Note that release builds do retain a fair amount of smart rebuild capabilities. Release builds continue to rely on Rake (for now).
+
+### Preprocessor support for Unity’s `TEST_CASE()` and `TEST_RANGE()`
+
+Unity’s features `TEST_CASE()` and `TEST_RANGE()` continue to work but only when `:use_test_preprocessor` is disabled. The previous project configuration option `:use_preprocessor_directives` that preserved them when preprocessing is enabled is no longer recognized.
+
+`TEST_CASE()` and `TEST_RANGE()` are macros that disappear when the preprocessor digests a test file. After preprocessing, they no longer exist in the test file that is compiled.
+
+In future revisions of Ceedling, support for `TEST_CASE()` and `TEST_RANGE()` when preprocessing is enabled will be brought back (very likely without a dedicated configuration option — hopefully, we’ll get it to just work™️).
+
+### Removed background task execution
+
+Background task execution for tool configurations (`:background_exec`) has been deprecated. This option was one of Ceedling’s earliest features attempting to speed up builds within the constraints of relying on Rake. This feature has rarely, if ever, been used in practice, and other, better options exist to manage any scenario that might motivate a background task.
+
+### Removed `colour_report` plugin
+
+Colored build output and test results in your terminal is glorious. Long ago the `colour_report` plugin provided this. It was a simple plugin that hooked into Ceedling in a somewhat messy way. Its approach to coloring output was also fairly brittle. It long ago stopped coloring build output as intended. It has been removed.
+
+Ceedling’s logging will eventually be updated to rely on a proper logging library. This will provide a number of important features along with greater speed and stability for the tool as a whole. This will also be the opportunity to add robust terminal text coloring support.
+
+### Bullseye code coverage plugin temporarily disabled
+
+The Gcov plugin has been updated and improved, but its proprietary counterpart, the [Bullseye](https://www.bullseye.com) plugin, is not presently functional. The needed fixes and updates require a software license that we do not (yet) have.
+
+### Gcov plugin’s support for deprecated features removed
+
+The configuration format for the `gcovr` utility changed when support for the `reportgenerator` utility was added. A format that accomodated a more uniform and common layout was adopted. However, support for the older, deprecated `gcovr`-only configuration was maintained. This support for the deprecated `gcovr` configuration format has been removed.
+
+Please consult the [gcov plugin’s documentation](plugins/gcov/README.md) to update any old-style `gcovr` configurations.
+
+### Gcov plugin’s `:abort_on_uncovered` option temporarily removed
+
+Like Ceedling’s preprocessing features, the Gcov plugin had grown in features and complexity over time. The plugin had become difficult to maintain and some of its features had become user unfriendly at best and misleading at worst.
+
+The Gcov plugin’s `:abort_on_uncovered` option plus the related `:uncovered_ignore_list` option were not preserved in this release. They will be brought back after some noodling on how to make these features user friendly again.
+
+### Undocumented environment variable `CEEDLING_USER_PROJECT_FILE` support removed
+
+A previously undocumented feature for merging a second configuration via environment variable `CEEDLING_USER_PROJECT_FILE` has been removed. This feature has been superseded by the new Mixins functionality.
diff --git a/docs/PluginDevelopmentGuide.md b/docs/PluginDevelopmentGuide.md
new file mode 100644
index 000000000..95edf01c0
--- /dev/null
+++ b/docs/PluginDevelopmentGuide.md
@@ -0,0 +1,742 @@
+# Developing Plugins for Ceedling
+
+This guide walks you through the process of creating custom plugins for
+[Ceedling](https://github.com/ThrowTheSwitch/Ceedling).
+
+It is assumed that the reader has a working installation of Ceedling and some
+basic usage experience, *i.e.* project creation/configuration and running tasks.
+
+Some experience with Ruby and Rake will be helpful but not absolutely required.
+You can learn the basics as you go — often by looking at other, existing
+Ceedling plugins or by simply searching for code examples online.
+
+## Contents
+
+* [Custom Plugins Overview](#custom-plugins-overview)
+* [Plugin Conventions & Architecture](#plugin-conventions--architecture)
+ 1. [Configuration Plugin](#plugin-option-1-configuration)
+ 1. [Programmatic `Plugin` subclass](#plugin-option-2-plugin-subclass)
+ 1. [Rake Tasks Plugin](#plugin-option-3-rake-tasks)
+
+## Development Roadmap & Notes
+
+_February 19, 2024_
+
+(See Ceedling's _[release notes](ReleaseNotes.md)_ for more.)
+
+* Ceedling 0.32 marks the beginning of moving all of Ceedling away from relying
+ on Rake. New, Rake-based plugins should not be developed. Rake dependencies
+ among built-in plugins will be refactored as the transition occurs.
+* Ceedling's entire plugin architecture will be overhauled in future releases.
+ The current structure is too dependent on Rake and provides both too little
+ and too much access to Ceedling's core.
+* Certain aspects of Ceedling's plugin structure have developed organically.
+ Consistency, coherence, and usability may not be high — particularly for
+ build step hook argument hashes and test results data structures used in
+ programmatic plugins.
+* Because of iterating on Ceedling's core design and features, documentation
+ here may not always be perfectly up to date.
+
+---
+
+# Custom Plugins Overview
+
+Ceedling plugins extend Ceedling without modifying its core code. They are
+implemented in YAML and the Ruby programming language and are loaded by
+Ceedling at runtime.
+
+Plugins provide the ability to customize the behavior of Ceedling at various
+stages of a build — preprocessing, compiling, linking, building, testing, and
+reporting.
+
+See _[CeedlingPacket]_ for basic details of operation (`:plugins` configuration
+section) and for a [directory of built-in plugins][plugins-directory].
+
+[CeedlingPacket]: CeedlingPacket.md
+[plugins-directory]: CeedlingPacket.md#ceedlings-built-in-plugins-a-directory
+
+# Plugin Conventions & Architecture
+
+Plugins are enabled and configured from within a Ceedling project's YAML
+configuration file (`:plugins` section).
+
+Conventions & requirements:
+
+* Plugin configuration names, the containing directory names, and filenames
+ must:
+ * All match (i.e. identical names)
+ * Be snake_case (lowercase with connecting underscores).
+* Plugins must be organized in a containing directory (the name of the plugin
+ as used in the project configuration `:plugins` ↳ `:enabled` list is its
+ containing directory name).
+* A plugin's containing directory must be located in a Ruby load path. Load
+ paths may be added to a Ceedling project using the `:plugins` ↳ `:load_paths`
+ list.
+* Rake plugins place their Rakefiles in the root of thecontaining plugin
+ directory.
+* Programmatic plugins must contain either or both `config/` and `lib/`
+ subdirectories within their containing directories.
+* Configuration plugins must place their files within a `config/` subdirectory
+ within the plugin's containing directory.
+
+Ceedling provides 3 options to customize its behavior through a plugin. Each
+strategy is implemented with source files conforming to location and naming
+conventions. These approaches can be combined.
+
+1. Configuration (YAML & Ruby)
+1. `Plugin` subclass (Ruby)
+1. Rake tasks (Ruby)
+
+# Plugin Option 1: Configuration
+
+The configuration option, surprisingly enough, provides Ceedling configuration
+values. Configuration plugin values can supplement or override project
+configuration values.
+
+Not long after Ceedling plugins were developed the `option:` feature was added
+to Ceedling to merge in secondary configuration files. This feature is
+typically a better way to manage nultiple configurations and in many ways
+supersedes a configuration plugin.
+
+That said, a configuration plugin is more capable than the `option:` feature and
+can be appropriate in some circumstances. Further, Ceedling's configuration
+pluging abilities are often a great way to provide configuration to
+programmatic `Plugin` subclasses (Ceedling plugins options #2).
+
+## Three flavors of configuration plugins exist
+
+1. **YAML defaults.** The data of a simple YAML file is incorporated into
+ Ceedling's configuration defaults during startup.
+1. **Programmatic (Ruby) defaults.** Ruby code creates a configuration hash
+ that Ceedling incorporates into its configuration defaults during startup.
+ This provides the greatest flexibility in creating configuration values.
+1. **YAML configurations.** The data of a simple YAML file is incorporated into
+ Ceedling's configuration much like Ceedling's actual configuration file.
+
+## Example configuration plugin layout
+
+Project configuration file:
+
+```yaml
+:plugins:
+ :load_paths:
+ - support/plugins
+ :enabled:
+ - zoom_zap
+```
+
+Ceedling project directory sturcture:
+
+(Third flavor of configuration plugin shown.)
+
+```
+project/
+├── project.yml
+└── support/
+ └── plugins/
+ └── zoom_zap/
+ └── config/
+ └── zoom_zap.yml
+```
+
+## Ceedling configuration build & use
+
+Configuration is developed at startup in this order:
+
+1. Ceedling loads its own defaults
+1. Any plugin defaults are inserted, if they do not already exist
+1. Any plugin configuration is merged
+1. Project file configuration is merged
+
+Merging means that any existing configuration is replaced. If no such
+key/value pairs already exist, they are inserted into the configuration.
+
+A plugin may further implement its own code to use custom configuration added to
+the Ceedling project file. In such cases, it's typically wise to make use of a
+plugin's option for defining default values. Configuration handling code is
+greatly simplified if strucures and values are guaranteed to exist in some
+form.
+
+## Configuration Plugin Flavors
+
+### Configuration Plugin Flvaor A: YAML Defaults
+
+Naming and location convention: `/config/defaults.yml`
+
+Configuration values are defined inside a YAML file just as the Ceedling project
+configuration file.
+
+Keys and values are defined in Ceedling's “base” configuration along with all
+default values Ceedling loads at startup. If a particular key/value pair is
+already set at the time the plugin attempts to set it, it will not be
+redefined.
+
+YAML values are static apart from Ceedling's ability to perform string
+substitution at configuration load time (see _[CeedlingPacket]_ for more).
+Programmatic Ruby defaults (next section) are more flexible but more
+complicated.
+
+```yaml
+# Any valid YAML is appropriate
+:key:
+ :value:
+```
+
+### Configuration Plugin Flvaor B: Programmatic (Ruby) Defaults
+
+Naming and location convention: `/config/defaults_.rb`
+
+Configuration values are defined in a Ruby hash returned by a “naked” function
+`get_default_config()` in a Ruby file. The Ruby file is loaded and evaluated at
+Ceedling startup. It can contain anything allowed in a Ruby script file but
+must contain the accessor function. The returned hash's top-level keys will
+live in Ceedling's configuration at the same level in the configuration
+hierarchy as a Ceedling project file's top-level keys ('top-level' refers to
+the left-most keys in the YAML, not to how “high” the keys are towards the top
+of the file).
+
+Keys and values are defined in Ceedling's “base” configuration along with all
+default values Ceedling loads at startup. If a particular key/value pair is
+already set at the time the plugin attempts to set it, it will not be
+redefined.
+
+This configuration option is more flexible than that documented in the previous
+section as full Ruby execution is possible in creating the defaults hash.
+
+### Configuration Plugin Flvaor C: YAML Values
+
+Naming and location convention: `/config/.yml`
+
+Configuration values are defined inside a YAML file just as the Ceedling project
+configuration file.
+
+Keys and values are defined in Ceedling's “base” configuration along with all
+default values Ceedling loads at startup. If a particular key/value pair is
+already set at the time the plugin attempts to set it, it will not be
+redefined.
+
+YAML values are static apart from Ceedling's ability to perform string
+substitution at configuration load time (see _[CeedlingPacket]_ for more).
+Programmatic Ruby defaults (next section) are more flexible but more
+complicated.
+
+```yaml
+# Any valid YAML is appropriate
+:key:
+ :value:
+```
+
+# Plugin Option 2: `Plugin` Subclass
+
+Naming and location conventions:
+
+* `/lib/.rb`
+* The plugin's class name must be the camelized version (a.k.a. “bumpy case")
+ of the plugin filename — `whiz_bang.rb` ➡️ `WhizBang`.
+
+This plugin option allows full programmatic ability connceted to any of a number
+of predefined Ceedling build steps.
+
+The contents of `.rb` must implement a class that subclasses
+`Plugin`, Ceedling's plugin base class.
+
+## Example `Plugin` subclass
+
+An incomplete `Plugin` subclass follows to illustate the basics.
+
+```ruby
+# whiz_bang/lib/whiz_bang.rb
+require 'ceedling/plugin'
+
+class WhizBang < Plugin
+ def setup
+ # ...
+ end
+
+ # Build step hook
+ def pre_test(test)
+ # ...
+ end
+
+ # Build step hook
+ def post_test(test)
+ # ...
+ end
+end
+```
+
+## Example programmatic plugin layout
+
+Project configuration file:
+
+```yaml
+:plugins:
+ :load_paths:
+ - support/plugins
+ :enabled:
+ - whiz_bang
+```
+
+Ceedling project directory sturcture:
+
+```
+project/
+├── project.yml
+└── support/
+ └── plugins/
+ └── whiz_bang/
+ └── lib/
+ └── whiz_bang.rb
+```
+
+It is possible and often convenient to add more `.rb` files to the containing
+`lib/` directory to allow good organization of plugin code. No Ceedling
+conventions exist for these supplemental code files. Only standard Ruby
+constaints exists for these filenames and content.
+
+## `Plugin` instance variables
+
+Each `Plugin` sublcass has access to the following instance variables:
+
+* `@name`
+* `@ceedling`
+
+`@name` is self explanatory. `@ceedling` is a hash containing every object
+within the Ceedling application; its keys are the filenames of the objects
+minus file extension.
+
+Objects commonly used in plugins include:
+
+* `@ceedling[:configurator]` — Project configuration
+* `@ceedling[:streaminator]` — Logging
+* `@ceedling[:reportinator]` — String formatting for logging
+* `@ceedling[:file_wrapper]` — File operations
+* `@ceedling[:plugin_reportinator]` — Various needs including gathering test
+ results
+
+## `Plugin` method `setup()`
+
+If your plugin defines this method, it will be called during plugin creation at
+Ceedling startup. It is effectively your constructor for your custom `Plugin`
+subclass.
+
+## `Plugin` hook methods `pre_` and `post_` conventions & concerns
+
+### Multi-threaded protections
+
+Because Ceedling can run build operations in multiple threads, build step hook
+handliers must be thread safe. Practically speaking, this generally requires
+a `Mutex` object `synchronize()`d around any code that writes to or reads from
+a common data structure instantiated within a plugin.
+
+A common example is collecting test results filepaths from the
+`post_test_fixture_execute()` hook. A hash or array accumulating these
+filepaths as text executables complete their runs must have appropriate
+threading protections.
+
+### Command line tool shell results
+
+Pre and post build step hooks are often called on either side of a command line
+tool operation. If a command line tool is executed for a build step (e.g. test
+compilation), the `arg_hash` will be the same for the pre and post hooks with
+one difference.
+
+In the `post_` hook, the `arg_hash` parameter will contain a `shell_result` key
+whose associated value is itself a hash with the following contents:
+
+```ruby
+{
+ :output => "", # String holding any $stdout / redirected $stderr output
+ :status => , # Ruby object of type Process::Status
+ :exit_code => , # Command line exit code (extracted from :status object)
+ :time => # Seconds elapsed for shell operation
+}
+```
+
+## `Plugin` hook methods `pre_mock_preprocess(arg_hash)` and `post_mock_preprocess(arg_hash)`
+
+These methods are called before and after execution of preprocessing for header
+files to be mocked (see [CeedlingPacket] to understand preprocessing). If a
+project does not enable preprocessing or a build does not include tests, these
+are not called. This pair of methods is called a number of times equal to the
+number of mocks in a test build.
+
+The argument `arg_hash` follows the structure below:
+
+```ruby
+arg_hash = {
+ # Filepath of header file to be preprocessed on its way to being mocked
+ :header_file => "",
+ # Filepath of processed header file
+ :preprocessed_header_file => "",
+ # Filepath of tests C file the mock will be used by
+ :test => "",
+ # List of flags to be provided to `cpp` GNU preprocessor tool
+ :flags => [],
+ # List of search paths to be provided to `cpp` GNU preprocessor tool
+ :include_paths => [],
+ # List of compilation symbols to be provided to `cpp` GNU preprocessor tool
+ :defines => []
+}
+```
+
+## `Plugin` hook methods `pre_test_preprocess(arg_hash)` and `post_test_preprocess(arg_hash)`
+
+These methods are called before and after execution of test file preprocessing
+(see [CeedlingPacket] to understand preprocessing). If a project does not
+enable preprocessing or a build does not include tests, these are not called.
+This pair of methods is called a number of times equal to the number of test
+files in a test build.
+
+The argument `arg_hash` follows the structure below:
+
+```ruby
+arg_hash = {
+ # Filepath of C test file to be preprocessed on its way to being used to generate runner
+ :test_file => "",
+ # Filepath of processed tests file
+ :preprocessed_test_file => "",
+ # Filepath of tests C file the mock will be used by
+ :test => "",
+ # List of flags to be provided to `cpp` GNU preprocessor tool
+ :flags => [],
+ # List of search paths to be provided to `cpp` GNU preprocessor tool
+ :include_paths => [],
+ # List of compilation symbols to be provided to `cpp` GNU preprocessor tool
+ :defines => []
+}
+```
+
+## `Plugin` hook methods `pre_mock_generate(arg_hash)` and `post_mock_generate(arg_hash)`
+
+These methods are called before and after mock generation. If a project does not
+enable mocks or a build does not include tests, these are not called. This pair
+of methods is called a number of times equal to the number of mocks in a test
+build.
+
+The argument `arg_hash` follows the structure below:
+
+```ruby
+arg_hash = {
+ # Filepath of the header file being mocked.
+ :header_file => "",
+ # Additional context passed by the calling function.
+ # Ceedling passes the :test symbol by default while plugins may provide another
+ :context => :,
+ # Filepath of the tests C file that references the requested mock
+ :test => "",
+ # Filepath of the generated mock C code.
+ :output_path => ""
+}
+```
+
+## `Plugin` hook methods `pre_runner_generate(arg_hash)` and `post_runner_generate(arg_hash)`
+
+These methods are called before and after execution of test runner generation. A
+test runner includes all the necessary C scaffolding (and `main()` entry point)
+to call the test cases defined in a test file when a test executable runs. If a
+build does not include tests, these are not called. This pair of methods is
+called a number of times equal to the number of test files in a test build.
+
+The argument `arg_hash` follows the structure below:
+
+```ruby
+arg_hash = {
+ # Additional context passed by the calling function.
+ # Ceedling passes the :test symbol by default while plugins may provide another
+ :context => :,
+ # Filepath of the tests C file.
+ :test_file => "",
+ # Filepath of the test file to be processed (if preprocessing enabled, this is not the same as :test_file).
+ :input_file => "",
+ # Filepath of the generated tests runner file.
+ :runner_file => ""
+}
+```
+
+## `Plugin` hook methods `pre_compile_execute(arg_hash)` and `post_compile_execute(arg_hash)`
+
+These methods are called before and after source file compilation. These are
+called in both test and release builds. This pair of methods is called a number
+of times equal to the number of C files in a test or release build.
+
+The argument `arg_hash` follows the structure below:
+
+```ruby
+arg_hash = {
+ :tool => {
+ # Hash holding compiler tool elements (see CeedlingPacket)
+ },
+ # Symbol of the operation being performed, e.g. :compile, :assemble or :link
+ :operation => :,
+ # Additional context passed by the calling function.
+ # Ceedling provides :test or :release by default while plugins may provide another.
+ :context => :,
+ # Filepath of the input C file
+ :source => "",
+ # Filepath of the output object file
+ :object => "",
+ # List of flags to be provided to compiler tool
+ :flags => [],
+ # List of search paths to be provided to compiler tool
+ :search_paths => [],
+ # List of compilation symbols to be provided to compiler tool
+ :defines => [],
+ # Filepath of the listing file, e.g. .lst file
+ :list => "",
+ # Filepath of the dependencies file, e.g. .d file
+ :dependencies => ""
+}
+```
+
+## `Plugin` hook methods `pre_link_execute(arg_hash)` and `post_link_execute(arg_hash)`
+
+These methods are called before and after linking an executable. These are
+called in both test and release builds. These are called for each test
+executable and each release artifact. This pair of methods is called a number
+of times equal to the number of test files in a test build or release artifacts
+in a release build.
+
+The argument `arg_hash` follows the structure below:
+
+```ruby
+arg_hash = {
+ # Hash holding linker tool properties.
+ :tool => {
+ # Hash holding compiler tool elements (see CeedlingPacket)
+ },
+ # Additional context passed by the calling function.
+ # Ceedling provides :test or :release by default while plugins may provide another.
+ :context => :,
+ # List of object files paths being linked, e.g. .o files
+ :objects => [],
+ # List of flags to be provided to linker tool
+ :flags => [],
+ # Filepath of the output file, e.g. .out file
+ :executable => "",
+ # Filepath of the map file, e.g. .map file
+ :map => "",
+ # List of libraries to link, e.g. those passed to the (GNU) linker with -l
+ :libraries => [],
+ # List of libraries paths, e.g. the ones passed to the (GNU) linker with -L
+ :libpaths => []
+}
+```
+
+## `Plugin` hook methods `pre_test_fixture_execute(arg_hash)` and `post_test_fixture_execute(arg_hash)`
+
+These methods are called before and after running a test executable. If a build
+does not include tests, these are not called. This pair of methods is called
+for each test executable in a build (each test file is ultimately built into a
+test executable).
+
+The argument `arg_hash` follows the structure below:
+
+```ruby
+arg_hash = {
+ # Hash holding execution tool properties.
+ :tool => {
+ # Hash holding compiler tool elements (see CeedlingPacket)
+ },
+ # Additional context passed by the calling function.
+ # Ceedling provides :test or :release by default while plugins may provide another.
+ :context => :,
+ # Name of the test file minus path and extension (`test/TestIness.c` -> 'TestIness')
+ :test_name => "",
+ # Filepath of original tests C file that became the test executable
+ :test_filepath => "",
+ # Path to the tests executable file, e.g. .out file
+ :executable => "",
+ # Path to the tests result file, e.g. .pass/.fail file
+ :result_file => ""
+}
+```
+
+## `Plugin` hook methods `pre_test(test)` and `post_test(test)`
+
+These methods are called before and after performing all steps needed to run a
+test file — i.e. configure, preprocess, compile, link, run, get results, etc.
+This pair of methods is called for each test file in a test build.
+
+The argument `test` corresponds to the path of the test C file being processed.
+
+## `Plugin` hook methods `pre_release()` and `post_release()`
+
+These methods are called before and after performing all steps needed to run the
+release task — i.e. configure, preprocess, compile, link, etc.
+
+## `Plugin` hook methods `pre_build` and `post_build`
+
+These methods are called before and after executing any ceedling task — e.g:
+test, release, coverage, etc.
+
+## `Plugin` hook methods `post_error()`
+
+This method is called at the conclusion of a Ceedling build that encounters any
+error that halts the build process. To be clear, a test build with failing test
+cases is not a build error.
+
+## `Plugin` hook methods `summary()`
+
+This method is called when invoking the summary task, `ceedling summary`. This
+method facilitates logging the results of the last build without running the
+previous build again.
+
+## Validating a plugin’s tools
+
+By default, Ceedling validates configured tools at startup according to a
+simple setting within the tool definition. This works just fine for default
+core tools and options. However, in the case of plugins, tools may not be even
+relevant to a plugin's operation depending on its configurable options. It's
+a bit silly for a tool not needed by your project to fail validation if
+Ceedling can't find it in your `$PATH`. Similarly, it's irresponsible to skip
+validating a tool just because it may not be needed.
+
+Ceedling provides optional, programmatic tool validation for these cases.
+`@ceedling]:tool_validator].validate()` can be forced to ignore a tool's
+`required:` setting to validate it. In such a scenario, a plugin should
+configure its own tools as `:optional => true` but forcibly validate them at
+plugin startup if the plugin's configuration options require said tool.
+
+An example from the `gcov` plugin illustrates this.
+
+```ruby
+# Validate gcov summary tool if coverage summaries are enabled (summaries rely on the `gcov` tool)
+if summaries_enabled?( @project_config )
+ @ceedling[:tool_validator].validate(
+ tool: TOOLS_GCOV_SUMMARY, # Tool defintion as Ruby hash
+ boom: true # Ignore optional status (raise exception if invalid)
+ )
+end
+```
+
+The tool `TOOLS_GCOV_SUMMARY` is defined with a Ruby hash in the plugin code.
+It is configured with `:optional => true`. At plugin startup, configuration
+options determine if the tool is needed. It is forcibly validated if the plugin
+configuration requires it.
+
+## Collecting test results from within `Plugin` subclass
+
+Some testing-specific plugins need access to test results to do their work. A
+utility method is available for this purpose.
+
+`@ceedling[:plugin_reportinator].assemble_test_results()`
+
+This method takes as an argument a list of results filepaths. These typically
+correspond directly to the collection of test files Ceedling processed in a
+given test build. It's common for this list of filepaths to be assembled from
+the `post_test_fixture_execute` build step execution hook.
+
+The data that `assemble_test_results()` returns hss a structure as follows. In
+this example, actual results from a single, real test file are presented as
+hash/array Ruby code with comments and with some edits to reduce line length.
+
+```ruby
+{
+ # Associates each test executable (i.e. test file) with an execution run time
+ :times => {
+ "test/TestUsartModel.c" => 0.21196400001645088
+ },
+
+ # List of succeeding test cases, grouped by test file.
+ :successes => [
+ {
+ :source => {:file => "test/TestUsartModel.c", :dirname => "test", :basename => "TestUsartModel.c"},
+ :collection => [
+ # If Unity is configured to do so, it will output execution run time for each test case.
+ # Ceedling creates a zero entry if the Unity option is not enabled.
+ {:test => "testCase1", :line => 17, :message => "", :unity_test_time => 0.0},
+ {:test => "testCase2", :line => 31, :message => "", :unity_test_time => 0.0}
+ ]
+ }
+ ],
+
+ # List of failing test cases, grouped by test file.
+ :failures => [
+ {
+ :source => {:file => "test/TestUsartModel.c", :dirname => "test", :basename => "TestUsartModel.c"},
+ :collection => [
+ {:test => "testCase3", :line => 25, :message => "", :unity_test_time => 0.0}
+ ]
+ }
+ ],
+
+ # List of ignored test cases, grouped by test file.
+ :ignores => [
+ {
+ :source => {:file => "test/TestUsartModel.c", :dirname => "test", :basename => "TestUsartModel.c"},
+ :collection => [
+ {:test => "testCase4", :line => 39, :message => "", :unity_test_time => 0.0}
+ ]
+ }
+ ],
+
+ # List of strings printed to $stdout, grouped by test file.
+ :stdout => [
+ {
+ :source => {:file => "test/TestUsartModel.c", :dirname => "test", :basename => "TestUsartModel.c"},
+ # Calls to print to $stdout are outside Unity's scope, preventing attaching test file line numbers
+ :collection => [
+ "<$stdout string (e.g. printf() call)>"
+ ]
+ }
+ ],
+
+ # Test suite run stats
+ :counts => {
+ :total => 4,
+ :passed => 2,
+ :failed => 1,
+ :ignored => 1,
+ :stdout => 1},
+
+ # The sum of all test file execution run times
+ :total_time => 0.21196400001645088
+}
+```
+
+# Plugin Option 3: Rake Tasks
+
+This plugin type adds custom Rake tasks to your project that can be run with `ceedling `.
+
+Naming and location conventions: `/.rake`
+
+## Example Rake task
+
+```ruby
+# Only tasks with description are listed by `ceedling -T`
+desc "Print hello world to console"
+task :hello_world do
+ sh "echo Hello World!"
+end
+```
+
+Resulting, example command line:
+
+```shell
+ > ceedling hello_world
+ > Hello World!
+```
+
+## Example Rake plugin layout
+
+Project configuration file:
+
+```yaml
+:plugins:
+ :load_paths:
+ - support/plugins
+ :enabled:
+ - hello_world
+```
+
+Ceedling project directory sturcture:
+
+```
+project/
+├── project.yml
+└── support/
+ └── plugins/
+ └── hello_world/
+ └── hello_world.rake
+```
\ No newline at end of file
diff --git a/docs/ReleaseNotes.md b/docs/ReleaseNotes.md
new file mode 100644
index 000000000..62e6e4687
--- /dev/null
+++ b/docs/ReleaseNotes.md
@@ -0,0 +1,219 @@
+# 🌱 Ceedling Release Notes
+
+These release notes are complemented by two other documents:
+
+1. 🪵 **[Changelog](Changelog.md)** for a structured list of additions, fixes, changes, and removals.
+1. 💔 **[Breaking Changes](BreakingChanges.md)** for a list of impacts to existing Ceedling projects.
+
+---
+
+# 1.0.0 pre-release — May 24, 2024
+
+## 🏴☠️ Avast, Breaking Changes, Ye Scallywags!
+
+**_Ahoy!_** There be plenty o’ **[breaking changes](BreakingChanges.md)** ahead, mateys! Arrr…
+
+## 👀 Highlights
+
+This Ceedling release is probably the most significant since the project was first [posted to SourceForge in 2009][sourceforge]. See the [Changelog](Changelog.md) for all the details.
+
+Ceedling now runs in Ruby 3. Builds can now run much faster than previous versions because of parallelized tasks. For test suites, header file search paths, code defines, and tool run flags are now customizable per test executable.
+
+### Special Thank-You's
+
+A **HUGE** Thanks to the ThrowTheSwitch.org community, for continuing to use, critique, and contribute to these tools. We're making the C world a better place and we appreciate all of you!
+
+We'd like to make some quick shout-outs to some especially helpful contributions. THANK YOU!
+
+ -- Mark VanderVoord & Machael Karlesky, ThrowTheSwitch.org Project Maintainers
+
+#### Sponsors of Ceedling 1.0
+
+ - [ThingamaByte, LLC](https://thingamabyte.com) - For continuing to nurture these projects and community with so much of their time.
+ - [Kamstrup, A/S](https://kamstrup.com) - For sponsoring and testing Ceedling's new parallel build/test support
+ - [Fraunhofer Institute for Integrated Systems and Device Technology IISB](https://iisb.fraunhofer.de) - For also sponsoring and testing Ceedling's new parallel build/test support
+ - [Peter Membrey](https://github.com/pmembrey) - For sponsoring, helping to plan, and validating Ceedling's new dependencies plugin
+
+#### Major Code/Doc Contributors
+
+These individuals contributed significant features, bugfixes, and improvements. This list was generated by git, so if you feel you should be here, you're probably right. Please let us know and we're very sorry to have missed you!
+
+ - Dom Postorivo
+ - Cezary Gapinski
+ - Aaron Schlicht
+ - Tim Bates
+ - Patrick Junger
+ - Austin Glaser
+ - Łukasz Żegliński
+ - Anthony Male
+ - Peter Membrey
+ - Laurens Miers
+ - Alejandro Rosso
+ - Tony Webb
+ - Greg Williams
+ - John Van Enk
+ - Job Vranish
+
+#### Major Forum Contributors
+
+These individuals have been a huge help in answering questions and guiding newcomers on our forums. Thanks for making this a welcoming community!
+
+ - @Letme
+ - @dpostorivo
+ - @swaldhoer
+ - @CezaryGapinski
+ - @aschlicht
+
+#### Also, thanks for your contributions!
+
+Hiroaki Yamazoe, Lucas Becker, Rafael Campos Las Heras, Scott Vokes, Zane D. Purvis, Данила Вальковец, Aaron Schlicht,
+Carl-Oskar Larsson, Javier Tiá, Jesper L. Nielsen, MrRedKite, Nik Krause, Rasmus Melchior Jacobsen, serjche,
+Andrei Korigodskii, Eder Clayton, Felipe Balbi, Hannes Bachl, Mike D. Lowis, Peter Horn, Rasmus Uth Pedersen,
+Simon Grossenbacher, AlexandreMarkus, André Draszik, Aurelien CHAPPUIS, Austin Yates, Ben Wainwright,
+Christopher Cichiwskyj, Crt Mori, Horacio Mijail Antón Quiles, John Hutchinson, Joseph Gaudet, Julien Peyregne, KGons,
+Kalle Møller, Peter Kempter, Luca Cavalli, Maksim Chichikalov, Marcelo Jo, Matt Chernosky, Niall Cooling,
+Olivier C. Larocque, Patrick Little, Richard Eklycke, Serjche, Spencer Russell, Stavros Vagionitis, Steven Huang,
+Toby Mole, Tom Hotston, Yuanqing Liu, afacotti, ccarrizosa, diachini, Steven Willard
+
+### Big Deal Highlights 🏅
+
+#### Ruby3
+
+Ceedling now runs in Ruby3. This latest version of Ceedling is _not_ backwards compatible with earlier versions of Ruby.
+
+#### Way faster execution with parallel build steps
+
+Previously, Ceedling builds were depth-first and limited to a single line of execution. This limitation was an artifact of how Ceedling was architected and relying on general purpose Rake for the build pipeline. Rake does, in fact, support multi-threaded builds, but, Ceedling was unable to take advantage of this. As such, builds were limited to a single line of execution no matter how many CPU resources were available.
+
+Ceedling 1.0.0 introduces a new build pipeline that batches build steps breadth-first. This means all test preprocessor steps, all compilation steps, all linking steps, etc. can benefit from concurrent and parallel execution. This speedup applies to both test suite and release builds.
+
+#### Per-test-executable configurations
+
+In previous versions of Ceedling each test executable was built with essentially the same global configuration. In the case of `#define`s and tool command line flags, individual files could be handled differently, but configuring Ceedling for doing so for all the files in any one test executable was tedious and error prone.
+
+Now Ceedling builds each test executable as a mini project where header file search paths, compilation `#define` symbols, and tool flags can be specified per test executable. That is, each file that ultimately comprises a test executable is handled with the same configuration as the other files that make up that test executable.
+
+Now you can have tests with quite different configurations and behaviors. Two tests need different mocks of the same header file? No problem. You want to test the same source file two different ways? We got you.
+
+The following new features (discussed in later sections) contribute to this new ability:
+
+- `TEST_INCLUDE_PATH(...)`. This build directive macro can be used within a test file to tell Ceedling which header search paths should be used during compilation. These paths are only used for compiling the files that comprise that test executable.
+- `:defines` handling. `#define`s are now specified for the compilation of all modules comprising a test executable. Matching is only against test file names but now includes wildcard and regular expression options.
+- `:flags` handling. Flags (e.g. `-std=c99`) are now specified for the build steps — preprocessing, compilation, and linking — of all modules comprising a test executable. Matching is only against test file names and now includes more sensible and robust wildcard and regular expression options.
+
+#### Mixins for configuration variations
+
+Ever wanted to smoosh in some extra configuration selectively? Let’s say you have different build scenarios and you'd like to run different variations of your project for them. Maybe you have core configuration that is common to all those scenarios. Previous versions of Ceedling included a handful of features that partially met these sorts of needs.
+
+All such features have been superseded by _Mixins_. Mixins are simply additional YAML that gets merged into you base project configuration. However, Mixins provide several key improvements over previous features:
+
+1. Mixins can be as little or as much configuration as you want. You could push all your configuration into mixins with a base project file including nothing but a `:mixins` section.
+1. Mixins can be specified in your project configuration, via environment variables, and from the command line. A clear order of precedence controls the order of merging. Any conflicts or duplicates are automatically resolved.
+1. Logging makes clear what proejct file and mixins are loaded and merged at startup.
+1. Like built-in plugins, Ceedling will soon come with built-in mixins available for common build scenarios.
+
+#### A proper command line
+
+Until this release, Ceedling depended on Rake for most of its command line handling. Rake’s task conventions provide poor command line handling abilities. The core problems with Rake command line handling include:
+
+1. Only brief, limited help statements.
+1. No optional flags to modify a task — verbosity, logging, etc. were their own tasks.
+1. Complex/limited parameterization (e.g. `verbosity[3]` instead of `--verbosity normal`).
+1. Tasks are order-dependent. So, for example, `test:all verbosity[5]` changes verbosity after the tests are run.
+
+Ceedling now offers a full command line interface with rich help, useful order-independent option flags, and more.
+
+The existing `new`, `upgrade`, `example`, and `exampples` commands remain but have been improved. For those commands that support it, you may now specify the project file to load (see new, related mixins feature discussed elsewhere), log file to write to, exit code handling behavior, and more from the command line.
+
+Try `ceedling help` and then `ceedling help ` to get started.
+
+**_Important Notes on the New Command Line:_**
+
+* The new and improved features for running build tasks — loading a project file, merging mixins, verbosity handling, etc. — are documented within the application command `build` keyword. A build command line such as the following is now possible: `ceedling test:all --verbosity obnoxious --logfile my/path/build.log`. Run `ceedling help build` to learn more and definitely see the next bullet point as well.
+* The `build` keyword is assumed by Ceedling. That is, it’s optional. `ceedling test:all` is the same as `ceedling build test:all`. The `build` keyword handling tells the Ceedling application to execute the named build task dynamically generated from your project configuration.
+* In the transition to remove Rake from Ceedling, two categories of command line interactions now exist. Note this distinction in the `help` headings.
+ 1. **Application Commands** — `help`, `build`, `new`, `upgrade`, `environment`, `examples`, `example`, `dumpconfig`, and `version`. These have full help via `ceedling help ` and a variety of useful command line switches that conform to typical command line conventions.
+ 1. **Build & Plugin Tasks** — Operations dynamically generated from your project configuration. These have only summary help (listed in `ceedling help`) and work just as they previously did. Common command line tasks including `ceedling test:all` and `ceedling release` are in this category.
+
+### Medium Deal Highlights 🥈
+
+#### `TEST_SOURCE_FILE(...)`
+
+In previous versions of Ceedling, a new, undocumented build directive feature was introduced. Adding a call to the macro `TEST_FILE(...)` with a C file’s name added that C file to the compilation and linking list for a test executable.
+
+This approach was helpful when relying on a Ceedling convention was problematic. Specifically, `#include`ing a header file would cause any correspondingly named source file to be added to the build list for a test executable. This convention could cause problems if, for example, the header file defined symbols that complicated test compilation or behavior. Similarly, if a source file did not have a corresponding header file of the same name, sometimes the only option was to `#include` it directly; this was ugly and problematic in its own way.
+
+The previously undocumented build directive macro `TEST_FILE(...)` has been renamed to `TEST_SOURCE_FILE(...)` and is now [documented](CeedlingPacket.md).
+
+#### Preprocessing improvements
+
+Ceedling has been around for a number of years and has had the benefit of many contributors over that time. Preprocessing (expanding macros in test files and header files to be mocked) is quite tricky to get right but is essential for big, complicated test suites. Over Ceedling’s long life various patches and incremental improvements have evolved in such a way that preprocessing had become quite complicated and often did the wrong thing. Much of this has been fixed and improved in this release (some of it has been temporarily removed).
+
+#### Crash Handling
+
+Previously, if a test executable ran into a segmentation fault (usually caused by memory issues in the code), the entire test executable would report nothing and an error would be reported. This behavior has been expanded to handle any crash condition and further improved. Optionally, a crashed test executable can be automatically rerun for each test case individually to narrow down which test caused the problem. Each crash is reported with its own failure. If `gdb` is properly installed and configured the specific line that caused the crash can be reported.
+
+See _[CeedlingPacket](CeedlingPacket.md))_ for the new `:project` ↳ `:use_backtrace` feature to control how much detail is extracted from a crashed test executable to help you find the cause.
+
+#### Documentation
+
+The Ceedling user guide, _[CeedlingPacket](CeedlingPacket.md)_, has been significantly revised and expanded. We will expand it further in future releases and eventually break it up into multiple documents or migrate it to a full documentation management system.
+
+Many of the plugins have received documentation updates as well.
+
+There’s more to be done, but Ceedling’s documentation is more complete and accurate than it’s ever been.
+
+### Small Deal Highlights 🥉
+
+- Effort has been invested across the project to improve error messages, exception handling, and exit code processing. Noisy backtraces have been relegated to the verbosity level of DEBUG as intended.
+- Logical ambiguity and functional bugs within `:paths` and `:files` configuration handling have been resolved along with updated documentation.
+- A variety of small improvements and fixes have been made throughout the plugin system and to many plugins.
+- The historically unwieldy `verbosity` command line task now comes in two flavors. The original recipe numeric parameterized version (e.g. `[4]`) exist as is. The new extra crispy recipe includes — funny enough — verbose task names `verbosity:silent`, `verbosity:errors`, `verbosity:complain`, `verbosity:normal`, `verbosity:obnoxious`, `verbosity:debug`.
+- This release marks the beginning of the end for Rake as a backbone of Ceedling. Over many years it has become clear that Rake’s design assumptions hamper building the sorts of features Ceedling’s users want, Rake’s command line structure creates a messy user experience for a full application built around it, and Rake’s quirks cause maintenance challenges. Particularly for test suites, much of Ceedling’s (invisible) dependence on Rake has been removed in this release. Much more remains to be done, including replicating some of the abilities Rake offers.
+- This is the first ever release of Ceedling with proper release notes. Hello, there! Release notes will be a regular part of future Ceedling updates. If you haven't noticed already, this edition of the notes are detailed and quite lengthy. This is entirely due to how extensive the changes are in the 1.0.0 release. Future releases will have far shorter notes.
+- The `fake_function_framework` plugin has been renamed simply `fff`.
+- Optional Unicode and emoji decorators have been added for your output stream enjoyment. See the documentation for logging decorators in _[CeedlingPacket](CeedlingPacket.md)_.
+
+## 🚨 Important Changes in Behavior to Be Aware Of
+
+- **Test suite build order 🔢.** Ceedling no longer builds each test executable one at a time. From the tasks you provide at the command line, Ceedling now collects up and batches all preprocessing steps, all mock generation, all test runner generation, all compilation, etc. Previously you would see each of these done for a single test executable and then repeated for the next executable and so on. Now, each build step happens to completion for all specified tests before moving on to the next build step.
+- **Logging output order 🔢.** When multi-threaded builds are enabled, logging output may not be what you expect. Progress statements may be all batched together or interleaved in ways that are misleading. The steps are happening in the correct order. How you are informed of them may be somewhat out of order.
+- **Files generated multiple times 🔀.** Now that each test is essentially a self-contained mini-project, some output may be generated multiple times. For instance, if the same mock is required by multiple tests, it will be generated multiple times. The same holds for compilation of source files into object files. A coming version of Ceedling will concentrate on optimizations to reuse any output that is truly identical across tests.
+- **Test suite plugin runs 🏃🏻.** Because build steps are run to completion across all the tests you specify at the command line (e.g. all the mocks for your tests are generated at one time) you may need to adjust how you depend on build steps.
+
+Together, these changes may cause you to think that Ceedling is running steps out of order or duplicating work. While bugs are always possible, more than likely, the output you see and the build ordering is expected.
+
+## 🩼 Known Issues
+
+1. The new internal pipeline that allows builds to be parallelized and configured per-test-executable can mean a fair amount of duplication of steps. A header file may be mocked identically multiple times. The same source file may be compiled identically multiple times. The speed gains due to parallelization more than make up for this. Future releases will concentrate on optimizing away duplication of build steps.
+1. While header file search paths are now customizable per executable, this currently only applies to the search paths the compiler uses. Distinguishing test files or header files of the same name in different directories for test runner and mock generation respectively continues to rely on educated guesses in Ceedling code.
+1. Any path for a C file specified with `TEST_SOURCE_FILE(...)` is in relation to **_project root_** — that is, from where you execute `ceedling` at the command line. If you move source files or change your directory structure, many of your `TEST_SOURCE_FILE(...)` calls may need to be updated. A more flexible and dynamic approach to path handling will come in a future update.
+
+## 📚 Background Knowledge
+
+### Parallel execution of build steps
+
+You may have heard that Ruby is actually only single-threaded or may know of its Global Interpreter Lock (GIL) that prevents parallel execution. To oversimplify a complicated subject, the Ruby implementations most commonly used to run Ceedling afford concurrency and true parallelism speedups but only in certain circumstances. It so happens that these circumstances are precisely the workload that Ceedling manages.
+
+“Mainstream” Ruby implementations — not JRuby, for example — offer the following that Ceedling takes advantage of:
+
+#### Native thread context switching on I/O operations
+
+Since version 1.9, Ruby supports native threads and not only green threads. However, native threads are limited by the GIL to executing one at a time regardless of the number of cores in your processor. But, the GIL is “relaxed” for I/O operations.
+
+When a native thread blocks for I/O, Ruby allows the OS scheduler to context switch to a thread ready to execute. This is the original benefit of threads when they were first developed back when CPUs contained a single core and multi-processor systems were rare and special. Ceedling does a fair amount of file and standard stream I/O in its pure Ruby code. Thus, when multiple threads are enabled in the proejct configuration file, execution can speed up for these operations.
+
+#### Process spawning
+
+Ruby’s process spawning abilities have always mapped directly to OS capabilities. When a processor has multiple cores available, the OS tends to spread multiple child processes across those cores in true parallel execution.
+
+Much of Ceedling’s workload is executing a tool — such as a compiler — in a child process. With multiple threads enabled, each thread can spawn a child process for a build tool used by a build step. These child processes can be spread across multiple cores in true parallel execution.
+
+## 📣 Shoutouts
+
+Thank yous and acknowledgments:
+
+- …
+
+
+[sourceforge]: https://sourceforge.net/projects/ceedling/ "Ceedling’s public debut"
\ No newline at end of file
diff --git a/examples/blinky/project.yml b/examples/blinky/project.yml
deleted file mode 100644
index 75f453d3d..000000000
--- a/examples/blinky/project.yml
+++ /dev/null
@@ -1,101 +0,0 @@
----
-
-# Notes:
-# This is a fully tested project that demonstrates the use
-# of a timer ISR to blink the on board LED of an Arduino UNO
-:project:
- :use_exceptions: FALSE
- :use_test_preprocessor: TRUE
- :use_auxiliary_dependencies: TRUE
- :build_root: build
- :release_build: TRUE
- :test_file_prefix: test_
- :which_ceedling: gem
- :ceedling_version: '?'
-
-#You'll have to specify these
-:environment:
- - :mcu: atmega328p
- - :f_cpu: 16000000UL
- - :serial_port: COM8 #change this to the serial port you are using!!!
- - :objcopy: avr-objcopy
- # Uncomment these lines if you are using windows and don't have these tools in your path
- # - :path:
- # - C:\mingw\bin
- # - C:\WinAVR-20100110\bin
- # - C:\WinAVR-20100110\utils\bin
- # - #{ENV['PATH']}
-
-:extension:
- :executable: .bin
-
-:release_build:
- :output: blinky
-
-:paths:
- :test:
- - +:test/**
- - -:test/support
- :source:
- - src/**
- :support:
- - test/support
-
-:defines:
- # in order to add common defines:
- # 1) remove the trailing [] from the :common: section
- # 2) add entries to the :common: section (e.g. :test: has TEST defined)
- :common: &common_defines []
- :test:
- - *common_defines
- - TEST
- - UNITY_OUTPUT_COLOR #this is just here to make sure it gets removed by ceedling
- :test_preprocess:
- - *common_defines
- - TEST
-
-:tools:
- :release_compiler:
- :executable: avr-gcc
- :arguments:
- - ${1}
- - -DTARGET
- - -DF_CPU=#{ENV['F_CPU']}
- - -mmcu=#{ENV['MCU']}
- - -Iinclude/
- - -Wall
- - -Os
- - -c
- - -o ${2}
- :release_linker:
- :executable: avr-gcc
- :arguments:
- - -mmcu=#{ENV['MCU']}
- - ${1}
- - -o ${2}.bin
-
-:cmock:
- :mock_prefix: mock_
- :when_no_prototypes: :warn
- :enforce_strict_ordering: TRUE
- :plugins:
- - :ignore
- :treat_as:
- uint8: HEX8
- uint16: HEX16
- uint32: UINT32
- int8: INT8
- bool: UINT8
-
-#:tools:
-# Ceedling defaults to using gcc for compiling, linking, etc.
-# As [:tools] is blank, gcc will be used (so long as it's in your system path)
-# See documentation to configure a given toolchain for use
-
-:plugins:
- :load_paths:
- - "#{Ceedling.load_path}"
- :enabled:
- - stdout_pretty_tests_report
- - module_generator
-...
diff --git a/examples/blinky/rakefile.rb b/examples/blinky/rakefile.rb
deleted file mode 100644
index 37b0fe701..000000000
--- a/examples/blinky/rakefile.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-require "ceedling"
-Ceedling.load_project
-
-task :default => %w[ test:all release ]
-
-# Dummy task to ensure that the SERIAL_PORT environment variable is set.
-# It can be set on the command line as follows:
-# $ rake SERIAL_PORT=[serial port name]
-task :serial_port do
- unless ENV['SERIAL_PORT']
- raise "SERIAL_PORT is not defined in the environment!"
- end
-end
-
-desc "Convert the output binary to a hex file for programming to the Arduino"
-task :convert => :release do
- bin_file = "build/release/#{RELEASE_BUILD_OUTPUT}.bin"
- hex_file = "build/release/#{RELEASE_BUILD_OUTPUT}.hex"
- cmd = "#{ENV['OBJCOPY']} -O ihex -R .eeprom #{bin_file} #{hex_file}"
- puts cmd
- sh cmd
-end
-
-desc "Program the Arduino over the serial port."
-task :program => [:convert, :serial_port] do
- hex_file = "build/release/#{RELEASE_BUILD_OUTPUT}.hex"
- cmd = "avrdude -F -V -c arduino -p #{ENV['MCU']} -P #{ENV['SERIAL_PORT']} -b 115200 -U flash:w:#{hex_file}"
- puts cmd
- sh cmd
-end
diff --git a/examples/blinky/src/BlinkTask.c b/examples/blinky/src/BlinkTask.c
deleted file mode 100644
index 7ab3e686e..000000000
--- a/examples/blinky/src/BlinkTask.c
+++ /dev/null
@@ -1,21 +0,0 @@
-// #include
-
-#include "BlinkTask.h"
-
-#ifdef TEST
- #define LOOP
- #include "stub_io.h"
-#else
- #include
- #include
- #define LOOP while(1)
-#endif // TEST
-
-
-
-void BlinkTask(void)
-{
- /* toggle the LED */
- PORTB ^= _BV(PORTB5);
-
-}
diff --git a/examples/blinky/src/BlinkTask.h b/examples/blinky/src/BlinkTask.h
deleted file mode 100644
index d9887810f..000000000
--- a/examples/blinky/src/BlinkTask.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef BlinkTask_H
-#define BlinkTask_H
-
-void BlinkTask(void);
-
-#endif
diff --git a/examples/blinky/src/Configure.c b/examples/blinky/src/Configure.c
deleted file mode 100644
index 11e506baa..000000000
--- a/examples/blinky/src/Configure.c
+++ /dev/null
@@ -1,36 +0,0 @@
-#include "Configure.h"
-#include "main.h"
-#ifdef TEST
- #include "stub_io.h"
- #include "stub_interrupt.h"
-#else
- #include
- #include
-#endif // TEST
-
-/* setup timer 0 to divide bus clock by 64.
- This results in a 1.024ms overflow interrupt
-16000000/64
- 250000
-
-0.000 004s *256
-0.001024
-*/
-void Configure(void)
-{
- /* disable interrupts */
- cli();
-
- /* Configure TIMER0 to use the CLK/64 prescaler. */
- TCCR0B = _BV(CS00) | _BV(CS01);
-
- /* enable the TIMER0 overflow interrupt */
- TIMSK0 = _BV(TOIE0);
-
- /* confiure PB5 as an output. */
- DDRB |= _BV(DDB5);
-
- /* enable interrupts. */
- sei();
-}
-
diff --git a/examples/blinky/src/Configure.h b/examples/blinky/src/Configure.h
deleted file mode 100644
index 2399d39d5..000000000
--- a/examples/blinky/src/Configure.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef Configure_H
-#define Configure_H
-
-void Configure(void);
-
-#endif // Configure_H
diff --git a/examples/blinky/src/main.c b/examples/blinky/src/main.c
deleted file mode 100644
index 6533aaae6..000000000
--- a/examples/blinky/src/main.c
+++ /dev/null
@@ -1,51 +0,0 @@
-// #include
-
-#include "main.h"
-#include "BlinkTask.h"
-#include "Configure.h"
-
-//Most OS's frown upon direct memory access.
-//So we'll have to use fake registers during testing.
-#ifdef TEST
- #define LOOP
- #include "stub_io.h"
- #include "stub_interrupt.h"
-#else
- #include
- #include
- #define LOOP while(1)
- //The target will need a main.
- //Our test runner will provide it's own and call AppMain()
- int main(void)
- {
- return AppMain();
- }
-#endif // TEST
-
-int AppMain(void)
-{
- Configure();
-
- LOOP
- {
- if(BlinkTaskReady==0x01)
- {
- BlinkTaskReady = 0x00;
- BlinkTask();
- }
- }
- return 0;
-}
-
-ISR(TIMER0_OVF_vect)
-{
- /* toggle every thousand ticks */
- if (tick >= 1000)
- {
- /* signal our periodic task. */
- BlinkTaskReady = 0x01;
- /* reset the tick */
- tick = 0;
- }
- tick++;
-}
diff --git a/examples/blinky/src/main.h b/examples/blinky/src/main.h
deleted file mode 100644
index 17c176c52..000000000
--- a/examples/blinky/src/main.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef __MAIN_H__
-#define __MAIN_H__
-
-int main(void);
-int AppMain(void);
-volatile int BlinkTaskReady;
-int tick;
-
-#endif /* __MAIN_H__ */
diff --git a/examples/blinky/test/support/stub_interrupt.h b/examples/blinky/test/support/stub_interrupt.h
deleted file mode 100644
index 7410f2149..000000000
--- a/examples/blinky/test/support/stub_interrupt.h
+++ /dev/null
@@ -1,347 +0,0 @@
-/* Copyright (c) 2002,2005,2007 Marek Michalkiewicz
- Copyright (c) 2007, Dean Camera
-
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
-
- * Neither the name of the copyright holders nor the names of
- contributors may be used to endorse or promote products derived
- from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- POSSIBILITY OF SUCH DAMAGE. */
-
-/* $Id: interrupt.h,v 1.25.2.1 2008/01/05 06:33:11 dmix Exp $ */
-
-#ifndef _AVR_INTERRUPT_H_
-#define _AVR_INTERRUPT_H_
-
-// #include
-#include "stub_io.h"
-#if !defined(__DOXYGEN__) && !defined(__STRINGIFY)
-/* Auxiliary macro for ISR_ALIAS(). */
-#define __STRINGIFY(x) #x
-#endif /* !defined(__DOXYGEN__) */
-
-/**
-\file
-\@{
-*/
-
-
-/** \name Global manipulation of the interrupt flag
-
- The global interrupt flag is maintained in the I bit of the status
- register (SREG).
-*/
-
-// #if defined(__DOXYGEN__)
-/** \def sei()
- \ingroup avr_interrupts
-
- \code #include \endcode
-
- Enables interrupts by setting the global interrupt mask. This function
- actually compiles into a single line of assembly, so there is no function
- call overhead. */
-// #define sei()
-void sei(void); // Redefine the macro as a function so that it can be mocked by CMock
-// #else /* !DOXYGEN */
-// # define sei() __asm__ __volatile__ ("sei" ::)
-// #endif /* DOXYGEN */
-
-// #if defined(__DOXYGEN__)
-/** \def cli()
- \ingroup avr_interrupts
-
- \code #include \endcode
-
- Disables all interrupts by clearing the global interrupt mask. This function
- actually compiles into a single line of assembly, so there is no function
- call overhead. */
-// #define cli()
-void cli(void); // Redefine the macro as a function so that it can be mocked by CMock
-// #else /* !DOXYGEN */
-// # define cli() __asm__ __volatile__ ("cli" ::)
-// #endif /* DOXYGEN */
-
-
-/** \name Macros for writing interrupt handler functions */
-
-
-// #if defined(__DOXYGEN__)
-/** \def ISR(vector [, attributes])
- \ingroup avr_interrupts
-
- \code #include \endcode
-
- Introduces an interrupt handler function (interrupt service
- routine) that runs with global interrupts initially disabled
- by default with no attributes specified.
-
- The attributes are optional and alter the behaviour and resultant
- generated code of the interrupt routine. Multiple attributes may
- be used for a single function, with a space seperating each
- attribute.
-
- Valid attributes are ISR_BLOCK, ISR_NOBLOCK, ISR_NAKED and
- ISR_ALIASOF(vect).
-
- \c vector must be one of the interrupt vector names that are
- valid for the particular MCU type.
-*/
-// # define ISR(vector, [attributes])
-#define ISR void ISR
-// #else /* real code */
-
-// #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) || (__GNUC__ > 4)
-// # define __INTR_ATTRS used, externally_visible
-// #else /* GCC < 4.1 */
-// # define __INTR_ATTRS used
-// #endif
-
-// #ifdef __cplusplus
-// # define ISR(vector, ...) \
-// extern "C" void vector (void) __attribute__ ((signal,__INTR_ATTRS)) __VA_ARGS__; \
-// void vector (void)
-// #else
-// # define ISR(vector, ...) \
-// void vector (void) __attribute__ ((signal,__INTR_ATTRS)) __VA_ARGS__; \
-// void vector (void)
-// #endif
-
-// #endif /* DOXYGEN */
-
-#if defined(__DOXYGEN__)
-/** \def SIGNAL(vector)
- \ingroup avr_interrupts
-
- \code #include \endcode
-
- Introduces an interrupt handler function that runs with global interrupts
- initially disabled.
-
- This is the same as the ISR macro without optional attributes.
- \deprecated Do not use SIGNAL() in new code. Use ISR() instead.
-*/
-# define SIGNAL(vector)
-#else /* real code */
-
-#ifdef __cplusplus
-# define SIGNAL(vector) \
- extern "C" void vector(void) __attribute__ ((signal, __INTR_ATTRS)); \
- void vector (void)
-#else
-# define SIGNAL(vector) \
- void vector (void) __attribute__ ((signal, __INTR_ATTRS)); \
- void vector (void)
-#endif
-
-#endif /* DOXYGEN */
-
-#if defined(__DOXYGEN__)
-/** \def EMPTY_INTERRUPT(vector)
- \ingroup avr_interrupts
-
- \code #include \endcode
-
- Defines an empty interrupt handler function. This will not generate
- any prolog or epilog code and will only return from the ISR. Do not
- define a function body as this will define it for you.
- Example:
- \code EMPTY_INTERRUPT(ADC_vect);\endcode */
-# define EMPTY_INTERRUPT(vector)
-#else /* real code */
-
-#ifdef __cplusplus
-# define EMPTY_INTERRUPT(vector) \
- extern "C" void vector(void) __attribute__ ((signal,naked,__INTR_ATTRS)); \
- void vector (void) { __asm__ __volatile__ ("reti" ::); }
-#else
-# define EMPTY_INTERRUPT(vector) \
- void vector (void) __attribute__ ((signal,naked,__INTR_ATTRS)); \
- void vector (void) { __asm__ __volatile__ ("reti" ::); }
-#endif
-
-#endif /* DOXYGEN */
-
-#if defined(__DOXYGEN__)
-/** \def ISR_ALIAS(vector, target_vector)
- \ingroup avr_interrupts
-
- \code #include \endcode
-
- Aliases a given vector to another one in the same manner as the
- ISR_ALIASOF attribute for the ISR() macro. Unlike the ISR_ALIASOF
- attribute macro however, this is compatible for all versions of
- GCC rather than just GCC version 4.2 onwards.
-
- \note This macro creates a trampoline function for the aliased
- macro. This will result in a two cycle penalty for the aliased
- vector compared to the ISR the vector is aliased to, due to the
- JMP/RJMP opcode used.
-
- \deprecated
- For new code, the use of ISR(..., ISR_ALIASOF(...)) is
- recommended.
-
- Example:
- \code
- ISR(INT0_vect)
- {
- PORTB = 42;
- }
-
- ISR_ALIAS(INT1_vect, INT0_vect);
- \endcode
-*/
-# define ISR_ALIAS(vector, target_vector)
-#else /* real code */
-
-#ifdef __cplusplus
-# if defined(__AVR_MEGA__) && __AVR_MEGA__
-# define ISR_ALIAS(vector, tgt) extern "C" void vector (void) \
- __attribute__((signal, naked, __INTR_ATTRS)); \
- void vector (void) { asm volatile ("jmp " __STRINGIFY(tgt) ::); }
-# else /* !__AVR_MEGA */
-# define ISR_ALIAS(vector, tgt) extern "C" void vector (void) \
- __attribute__((signal, naked, __INTR_ATTRS)); \
- void vector (void) { asm volatile ("rjmp " __STRINGIFY(tgt) ::); }
-# endif /* __AVR_MEGA__ */
-#else /* !__cplusplus */
-# if defined(__AVR_MEGA__) && __AVR_MEGA__
-# define ISR_ALIAS(vector, tgt) void vector (void) \
- __attribute__((signal, naked, __INTR_ATTRS)); \
- void vector (void) { asm volatile ("jmp " __STRINGIFY(tgt) ::); }
-# else /* !__AVR_MEGA */
-# define ISR_ALIAS(vector, tgt) void vector (void) \
- __attribute__((signal, naked, __INTR_ATTRS)); \
- void vector (void) { asm volatile ("rjmp " __STRINGIFY(tgt) ::); }
-# endif /* __AVR_MEGA__ */
-#endif /* __cplusplus */
-
-#endif /* DOXYGEN */
-
-#if defined(__DOXYGEN__)
-/** \def reti()
- \ingroup avr_interrupts
-
- \code #include \endcode
-
- Returns from an interrupt routine, enabling global interrupts. This should
- be the last command executed before leaving an ISR defined with the ISR_NAKED
- attribute.
-
- This macro actually compiles into a single line of assembly, so there is
- no function call overhead.
-*/
-# define reti()
-#else /* !DOXYGEN */
-# define reti() __asm__ __volatile__ ("reti" ::)
-#endif /* DOXYGEN */
-
-#if defined(__DOXYGEN__)
-/** \def BADISR_vect
- \ingroup avr_interrupts
-
- \code #include \endcode
-
- This is a vector which is aliased to __vector_default, the vector
- executed when an ISR fires with no accompanying ISR handler. This
- may be used along with the ISR() macro to create a catch-all for
- undefined but used ISRs for debugging purposes.
-*/
-# define BADISR_vect
-#else /* !DOXYGEN */
-# define BADISR_vect __vector_default
-#endif /* DOXYGEN */
-
-/** \name ISR attributes */
-
-#if defined(__DOXYGEN__)
-/** \def ISR_BLOCK
- \ingroup avr_interrupts
-
- \code# include \endcode
-
- Identical to an ISR with no attributes specified. Global
- interrupts are initially disabled by the AVR hardware when
- entering the ISR, without the compiler modifying this state.
-
- Use this attribute in the attributes parameter of the ISR macro.
-*/
-# define ISR_BLOCK
-
-/** \def ISR_NOBLOCK
- \ingroup avr_interrupts
-
- \code# include \endcode
-
- ISR runs with global interrupts initially enabled. The interrupt
- enable flag is activated by the compiler as early as possible
- within the ISR to ensure minimal processing delay for nested
- interrupts.
-
- This may be used to create nested ISRs, however care should be
- taken to avoid stack overflows, or to avoid infinitely entering
- the ISR for those cases where the AVR hardware does not clear the
- respective interrupt flag before entering the ISR.
-
- Use this attribute in the attributes parameter of the ISR macro.
-*/
-# define ISR_NOBLOCK
-
-/** \def ISR_NAKED
- \ingroup avr_interrupts
-
- \code# include \endcode
-
- ISR is created with no prologue or epilogue code. The user code is
- responsible for preservation of the machine state including the
- SREG register, as well as placing a reti() at the end of the
- interrupt routine.
-
- Use this attribute in the attributes parameter of the ISR macro.
-*/
-# define ISR_NAKED
-
-/** \def ISR_ALIASOF(target_vector)
- \ingroup avr_interrupts
-
- \code#include \endcode
-
- The ISR is linked to another ISR, specified by the vect parameter.
- This is compatible with GCC 4.2 and greater only.
-
- Use this attribute in the attributes parameter of the ISR macro.
-*/
-# define ISR_ALIASOF(target_vector)
-#else /* !DOXYGEN */
-# define ISR_BLOCK
-# define ISR_NOBLOCK __attribute__((interrupt))
-# define ISR_NAKED __attribute__((naked))
-# define ISR_ALIASOF(v) __attribute__((alias(__STRINGIFY(v))))
-#endif /* DOXYGEN */
-
-/* \@} */
-
-#endif
diff --git a/examples/blinky/test/support/stub_io.h b/examples/blinky/test/support/stub_io.h
deleted file mode 100644
index e9646daf6..000000000
--- a/examples/blinky/test/support/stub_io.h
+++ /dev/null
@@ -1,421 +0,0 @@
-/* Copyright (c) 2002,2003,2005,2006,2007 Marek Michalkiewicz, Joerg Wunsch
- Copyright (c) 2007 Eric B. Weddington
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
-
- * Neither the name of the copyright holders nor the names of
- contributors may be used to endorse or promote products derived
- from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- POSSIBILITY OF SUCH DAMAGE. */
-
-/* $Id: io.h,v 1.52.2.28 2009/12/20 17:02:53 arcanum Exp $ */
-
-/** \file */
-/** \defgroup avr_io : AVR device-specific IO definitions
- \code #include \endcode
-
- This header file includes the apropriate IO definitions for the
- device that has been specified by the -mmcu= compiler
- command-line switch. This is done by diverting to the appropriate
- file <avr/io XXXX .h> which should
- never be included directly. Some register names common to all
- AVR devices are defined directly within <avr/common.h> ,
- which is included in <avr/io.h> ,
- but most of the details come from the respective include file.
-
- Note that this file always includes the following files:
- \code
- #include
- #include
- #include
- #include
- \endcode
- See \ref avr_sfr for more details about that header file.
-
- Included are definitions of the IO register set and their
- respective bit values as specified in the Atmel documentation.
- Note that inconsistencies in naming conventions,
- so even identical functions sometimes get different names on
- different devices.
-
- Also included are the specific names useable for interrupt
- function definitions as documented
- \ref avr_signames "here".
-
- Finally, the following macros are defined:
-
- - \b RAMEND
-
- The last on-chip RAM address.
-
- - \b XRAMEND
-
- The last possible RAM location that is addressable. This is equal to
- RAMEND for devices that do not allow for external RAM. For devices
- that allow external RAM, this will be larger than RAMEND.
-
- - \b E2END
-
- The last EEPROM address.
-
- - \b FLASHEND
-
- The last byte address in the Flash program space.
-
- - \b SPM_PAGESIZE
-
- For devices with bootloader support, the flash pagesize
- (in bytes) to be used for the \c SPM instruction.
- - \b E2PAGESIZE
-
- The size of the EEPROM page.
-
-*/
-
-#ifndef _AVR_IO_H_
-#define _AVR_IO_H_
-
-// #include
-#include "stub_sfr_defs.h"
-// #if defined (__AVR_AT94K__)
-// # include
-// #elif defined (__AVR_AT43USB320__)
-// # include
-// #elif defined (__AVR_AT43USB355__)
-// # include
-// #elif defined (__AVR_AT76C711__)
-// # include
-// #elif defined (__AVR_AT86RF401__)
-// # include
-// #elif defined (__AVR_AT90PWM1__)
-// # include
-// #elif defined (__AVR_AT90PWM2__)
-// # include
-// #elif defined (__AVR_AT90PWM2B__)
-// # include
-// #elif defined (__AVR_AT90PWM3__)
-// # include
-// #elif defined (__AVR_AT90PWM3B__)
-// # include
-// #elif defined (__AVR_AT90PWM216__)
-// # include
-// #elif defined (__AVR_AT90PWM316__)
-// # include
-// #elif defined (__AVR_AT90PWM81__)
-// # include
-// #elif defined (__AVR_ATmega8U2__)
-// # include
-// #elif defined (__AVR_ATmega16M1__)
-// # include
-// #elif defined (__AVR_ATmega16U2__)
-// # include
-// #elif defined (__AVR_ATmega16U4__)
-// # include
-// #elif defined (__AVR_ATmega32C1__)
-// # include
-// #elif defined (__AVR_ATmega32M1__)
-// # include
-// #elif defined (__AVR_ATmega32U2__)
-// # include
-// #elif defined (__AVR_ATmega32U4__)
-// # include
-// #elif defined (__AVR_ATmega32U6__)
-// # include
-// #elif defined (__AVR_ATmega64C1__)
-// # include
-// #elif defined (__AVR_ATmega64M1__)
-// # include
-// #elif defined (__AVR_ATmega128__)
-// # include
-// #elif defined (__AVR_ATmega1280__)
-// # include
-// #elif defined (__AVR_ATmega1281__)
-// # include
-// #elif defined (__AVR_ATmega1284P__)
-// # include
-// #elif defined (__AVR_ATmega128RFA1__)
-// # include
-// #elif defined (__AVR_ATmega2560__)
-// # include
-// #elif defined (__AVR_ATmega2561__)
-// # include
-// #elif defined (__AVR_AT90CAN32__)
-// # include
-// #elif defined (__AVR_AT90CAN64__)
-// # include
-// #elif defined (__AVR_AT90CAN128__)
-// # include
-// #elif defined (__AVR_AT90USB82__)
-// # include
-// #elif defined (__AVR_AT90USB162__)
-// # include
-// #elif defined (__AVR_AT90USB646__)
-// # include
-// #elif defined (__AVR_AT90USB647__)
-// # include
-// #elif defined (__AVR_AT90USB1286__)
-// # include
-// #elif defined (__AVR_AT90USB1287__)
-// # include
-// #elif defined (__AVR_ATmega64__)
-// # include
-// #elif defined (__AVR_ATmega640__)
-// # include
-// #elif defined (__AVR_ATmega644__) || defined (__AVR_ATmega644A__)
-// # include
-// #elif defined (__AVR_ATmega644P__)
-// # include
-// #elif defined (__AVR_ATmega644PA__)
-// # include
-// #elif defined (__AVR_ATmega645__) || defined (__AVR_ATmega645A__) || defined (__AVR_ATmega645P__)
-// # include
-// #elif defined (__AVR_ATmega6450__) || defined (__AVR_ATmega6450A__) || defined (__AVR_ATmega6450P__)
-// # include
-// #elif defined (__AVR_ATmega649__) || defined (__AVR_ATmega649A__)
-// # include
-// #elif defined (__AVR_ATmega6490__) || defined (__AVR_ATmega6490A__) || defined (__AVR_ATmega6490P__)
-// # include
-// #elif defined (__AVR_ATmega649P__)
-// # include
-// #elif defined (__AVR_ATmega64HVE__)
-// # include
-// #elif defined (__AVR_ATmega103__)
-// # include
-// #elif defined (__AVR_ATmega32__)
-// # include
-// #elif defined (__AVR_ATmega323__)
-// # include
-// #elif defined (__AVR_ATmega324P__) || defined (__AVR_ATmega324A__)
-// # include
-// #elif defined (__AVR_ATmega324PA__)
-// # include
-// #elif defined (__AVR_ATmega325__)
-// # include
-// #elif defined (__AVR_ATmega325P__)
-// # include
-// #elif defined (__AVR_ATmega3250__)
-// # include
-// #elif defined (__AVR_ATmega3250P__)
-// # include
-// #elif defined (__AVR_ATmega328P__) || defined (__AVR_ATmega328__)
-// # include
-# include
-// #elif defined (__AVR_ATmega329__)
-// # include
-// #elif defined (__AVR_ATmega329P__) || defined (__AVR_ATmega329PA__)
-// # include
-// #elif defined (__AVR_ATmega3290__)
-// # include
-// #elif defined (__AVR_ATmega3290P__)
-// # include