Просмотр исходного кода

- add ceedling/cmock/unity as testing framework and support
- unified makefile project for the whole repos
- new separate project for tests

hathach 13 лет назад
Родитель
Сommit
bc735bbe22
100 измененных файлов с 6827 добавлено и 292 удалено
  1. 1 288
      .cproject
  2. 0 4
      .project
  3. 199 0
      tests/.cproject
  4. 82 0
      tests/.project
  5. 78 0
      tests/project.yml
  6. 4 0
      tests/rakefile.rb
  7. 56 0
      tests/test/test_hid_host.c
  8. BIN
      tests/vendor/ceedling/docs/CExceptionSummary.pdf
  9. BIN
      tests/vendor/ceedling/docs/CMock Summary.pdf
  10. BIN
      tests/vendor/ceedling/docs/CeedlingPacket.pdf
  11. BIN
      tests/vendor/ceedling/docs/Unity Summary.pdf
  12. 27 0
      tests/vendor/ceedling/lib/build_invoker_utils.rb
  13. 42 0
      tests/vendor/ceedling/lib/cacheinator.rb
  14. 12 0
      tests/vendor/ceedling/lib/cacheinator_helper.rb
  15. 27 0
      tests/vendor/ceedling/lib/ceedling.rb
  16. 16 0
      tests/vendor/ceedling/lib/ceedling/version.rb
  17. 16 0
      tests/vendor/ceedling/lib/ceedling/version.rb.erb
  18. 15 0
      tests/vendor/ceedling/lib/cmock_builder.rb
  19. 329 0
      tests/vendor/ceedling/lib/configurator.rb
  20. 437 0
      tests/vendor/ceedling/lib/configurator_builder.rb
  21. 124 0
      tests/vendor/ceedling/lib/configurator_plugins.rb
  22. 124 0
      tests/vendor/ceedling/lib/configurator_setup.rb
  23. 184 0
      tests/vendor/ceedling/lib/configurator_validator.rb
  24. 91 0
      tests/vendor/ceedling/lib/constants.rb
  25. 380 0
      tests/vendor/ceedling/lib/defaults.rb
  26. 92 0
      tests/vendor/ceedling/lib/dependinator.rb
  27. 9 0
      tests/vendor/ceedling/lib/erb_wrapper.rb
  28. 132 0
      tests/vendor/ceedling/lib/file_finder.rb
  29. 54 0
      tests/vendor/ceedling/lib/file_finder_helper.rb
  30. 189 0
      tests/vendor/ceedling/lib/file_path_utils.rb
  31. 69 0
      tests/vendor/ceedling/lib/file_system_utils.rb
  32. 10 0
      tests/vendor/ceedling/lib/file_system_wrapper.rb
  33. 79 0
      tests/vendor/ceedling/lib/file_wrapper.rb
  34. 54 0
      tests/vendor/ceedling/lib/flaginator.rb
  35. 164 0
      tests/vendor/ceedling/lib/generator.rb
  36. 40 0
      tests/vendor/ceedling/lib/generator_helper.rb
  37. 89 0
      tests/vendor/ceedling/lib/generator_test_results.rb
  38. 62 0
      tests/vendor/ceedling/lib/generator_test_results_sanity_checker.rb
  39. 63 0
      tests/vendor/ceedling/lib/generator_test_runner.rb
  40. 31 0
      tests/vendor/ceedling/lib/loginator.rb
  41. 46 0
      tests/vendor/ceedling/lib/makefile.rb
  42. 307 0
      tests/vendor/ceedling/lib/objects.yml
  43. 19 0
      tests/vendor/ceedling/lib/par_map.rb
  44. 80 0
      tests/vendor/ceedling/lib/plugin.rb
  45. 53 0
      tests/vendor/ceedling/lib/plugin_builder.rb
  46. 107 0
      tests/vendor/ceedling/lib/plugin_manager.rb
  47. 19 0
      tests/vendor/ceedling/lib/plugin_manager_helper.rb
  48. 75 0
      tests/vendor/ceedling/lib/plugin_reportinator.rb
  49. 52 0
      tests/vendor/ceedling/lib/plugin_reportinator_helper.rb
  50. 43 0
      tests/vendor/ceedling/lib/preprocessinator.rb
  51. 30 0
      tests/vendor/ceedling/lib/preprocessinator_extractor.rb
  52. 21 0
      tests/vendor/ceedling/lib/preprocessinator_file_handler.rb
  53. 46 0
      tests/vendor/ceedling/lib/preprocessinator_helper.rb
  54. 55 0
      tests/vendor/ceedling/lib/preprocessinator_includes_handler.rb
  55. 38 0
      tests/vendor/ceedling/lib/project_config_manager.rb
  56. 64 0
      tests/vendor/ceedling/lib/project_file_loader.rb
  57. 17 0
      tests/vendor/ceedling/lib/rake_utils.rb
  58. 33 0
      tests/vendor/ceedling/lib/rake_wrapper.rb
  59. 74 0
      tests/vendor/ceedling/lib/rakefile.rb
  60. 58 0
      tests/vendor/ceedling/lib/release_invoker.rb
  61. 16 0
      tests/vendor/ceedling/lib/release_invoker_helper.rb
  62. 9 0
      tests/vendor/ceedling/lib/reportinator.rb
  63. 9 0
      tests/vendor/ceedling/lib/rules_cmock.rake
  64. 26 0
      tests/vendor/ceedling/lib/rules_preprocess.rake
  65. 79 0
      tests/vendor/ceedling/lib/rules_release.rake
  66. 15 0
      tests/vendor/ceedling/lib/rules_release_deep_dependencies.rake
  67. 59 0
      tests/vendor/ceedling/lib/rules_tests.rake
  68. 15 0
      tests/vendor/ceedling/lib/rules_tests_deep_dependencies.rake
  69. 51 0
      tests/vendor/ceedling/lib/setupinator.rb
  70. 20 0
      tests/vendor/ceedling/lib/stream_wrapper.rb
  71. 41 0
      tests/vendor/ceedling/lib/streaminator.rb
  72. 15 0
      tests/vendor/ceedling/lib/streaminator_helper.rb
  73. 32 0
      tests/vendor/ceedling/lib/system_utils.rb
  74. 76 0
      tests/vendor/ceedling/lib/system_wrapper.rb
  75. 38 0
      tests/vendor/ceedling/lib/target_loader.rb
  76. 89 0
      tests/vendor/ceedling/lib/task_invoker.rb
  77. 104 0
      tests/vendor/ceedling/lib/tasks_base.rake
  78. 91 0
      tests/vendor/ceedling/lib/tasks_filesystem.rake
  79. 28 0
      tests/vendor/ceedling/lib/tasks_release.rake
  80. 9 0
      tests/vendor/ceedling/lib/tasks_release_deep_dependencies.rake
  81. 52 0
      tests/vendor/ceedling/lib/tasks_tests.rake
  82. 9 0
      tests/vendor/ceedling/lib/tasks_tests_deep_dependencies.rake
  83. 36 0
      tests/vendor/ceedling/lib/tasks_vendor.rake
  84. 81 0
      tests/vendor/ceedling/lib/test_includes_extractor.rb
  85. 97 0
      tests/vendor/ceedling/lib/test_invoker.rb
  86. 28 0
      tests/vendor/ceedling/lib/test_invoker_helper.rb
  87. 212 0
      tests/vendor/ceedling/lib/tool_executor.rb
  88. 115 0
      tests/vendor/ceedling/lib/tool_executor_helper.rb
  89. 10 0
      tests/vendor/ceedling/lib/verbosinator.rb
  90. 16 0
      tests/vendor/ceedling/lib/yaml_wrapper.rb
  91. 15 0
      tests/vendor/ceedling/plugins/bullseye/assets/template.erb
  92. 162 0
      tests/vendor/ceedling/plugins/bullseye/bullseye.rake
  93. 53 0
      tests/vendor/ceedling/plugins/bullseye/config/defaults.yml
  94. 172 0
      tests/vendor/ceedling/plugins/bullseye/lib/bullseye.rb
  95. 0 0
      tests/vendor/ceedling/plugins/bullseye/readme.txt
  96. 34 0
      tests/vendor/ceedling/plugins/gcov/defaults.yml
  97. 152 0
      tests/vendor/ceedling/plugins/gcov/gcov.rake
  98. 128 0
      tests/vendor/ceedling/plugins/gcov/gcov.rb
  99. 0 0
      tests/vendor/ceedling/plugins/gcov/readme.txt
  100. 15 0
      tests/vendor/ceedling/plugins/gcov/template.erb

+ 1 - 288
.cproject

@@ -269,294 +269,7 @@
 				</scannerConfigBuildInfo>
 			</storageModule>
 			<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets">
-				<buildTargets>
-					<target name="test local" path="tests/tests" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
-						<buildCommand>rake.bat</buildCommand>
-						<buildTarget>test:all</buildTarget>
-						<stopOnError>true</stopOnError>
-						<useDefaultCommand>false</useDefaultCommand>
-						<runAllBuilders>true</runAllBuilders>
-					</target>
-					<target name="test clean" path="tests/tests" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
-						<buildCommand>rake.bat</buildCommand>
-						<buildArguments/>
-						<buildTarget>clean</buildTarget>
-						<stopOnError>true</stopOnError>
-						<useDefaultCommand>false</useDefaultCommand>
-						<runAllBuilders>true</runAllBuilders>
-					</target>
-				</buildTargets>
-			</storageModule>
-		</cconfiguration>
-		<cconfiguration id="com.crt.advproject.toolchain.exe.debug.1281391870.909223166">
-			<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="com.crt.advproject.toolchain.exe.debug.1281391870.909223166" moduleId="org.eclipse.cdt.core.settings" name="test">
-				<externalSettings/>
-				<extensions>
-					<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
-					<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
-					<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
-					<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
-					<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
-					<extension id="org.eclipse.cdt.core.VCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
-				</extensions>
-			</storageModule>
-			<storageModule moduleId="cdtBuildSystem" version="4.0.0">
-				<configuration artifactName="${ProjName}" buildProperties="" description="" id="com.crt.advproject.toolchain.exe.debug.1281391870.909223166" name="test" parent="org.eclipse.cdt.build.core.emptycfg">
-					<folderInfo id="com.crt.advproject.toolchain.exe.debug.1281391870.909223166." name="/" resourcePath="">
-						<toolChain id="org.eclipse.cdt.build.core.prefbase.toolchain.437810642" name="No ToolChain" superClass="org.eclipse.cdt.build.core.prefbase.toolchain">
-							<targetPlatform id="org.eclipse.cdt.build.core.prefbase.toolchain.437810642.512375678" name=""/>
-							<builder buildPath="${workspace_loc:/tinyusb/test}" id="org.eclipse.cdt.build.core.settings.default.builder.225884486" name="Gnu Make Builder.test" superClass="org.eclipse.cdt.build.core.settings.default.builder"/>
-							<tool id="org.eclipse.cdt.build.core.settings.holder.libs.48714889" name="holder for library settings" superClass="org.eclipse.cdt.build.core.settings.holder.libs"/>
-						</toolChain>
-					</folderInfo>
-				</configuration>
-			</storageModule>
-			<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
-			<storageModule moduleId="org.eclipse.cdt.core.language.mapping"/>
-			<storageModule moduleId="scannerConfiguration">
-				<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
-				<profile id="com.crt.advproject.GCCManagedMakePerProjectProfileCPP">
-					<buildOutputProvider>
-						<openAction enabled="false" filePath=""/>
-						<parser enabled="false"/>
-					</buildOutputProvider>
-					<scannerInfoProvider id="com.crt.advproject.specsFile">
-						<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="arm-none-eabi-c++" useDefault="true"/>
-						<parser enabled="true"/>
-					</scannerInfoProvider>
-				</profile>
-				<profile id="com.crt.advproject.GCCManagedMakePerProjectProfile">
-					<buildOutputProvider>
-						<openAction enabled="false" filePath=""/>
-						<parser enabled="false"/>
-					</buildOutputProvider>
-					<scannerInfoProvider id="com.crt.advproject.specsFile">
-						<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file} " command="arm-none-eabi-gcc" useDefault="true"/>
-						<parser enabled="true"/>
-					</scannerInfoProvider>
-				</profile>
-				<profile id="com.crt.advproject.GASManagedMakePerProjectProfile">
-					<buildOutputProvider>
-						<openAction enabled="false" filePath=""/>
-						<parser enabled="false"/>
-					</buildOutputProvider>
-					<scannerInfoProvider id="com.crt.advproject.specsFile">
-						<runAction arguments="-x assembler-with-cpp -E -P -v -dD ${plugin_state_location}/${specs_file}" command="arm-none-eabi-gcc" useDefault="true"/>
-						<parser enabled="true"/>
-					</scannerInfoProvider>
-				</profile>
-				<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
-					<buildOutputProvider>
-						<openAction enabled="true" filePath=""/>
-						<parser enabled="true"/>
-					</buildOutputProvider>
-					<scannerInfoProvider id="specsFile">
-						<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
-						<parser enabled="true"/>
-					</scannerInfoProvider>
-				</profile>
-				<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
-					<buildOutputProvider>
-						<openAction enabled="true" filePath=""/>
-						<parser enabled="true"/>
-					</buildOutputProvider>
-					<scannerInfoProvider id="makefileGenerator">
-						<runAction arguments="-E -P -v -dD" command="" useDefault="true"/>
-						<parser enabled="true"/>
-					</scannerInfoProvider>
-				</profile>
-				<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
-					<buildOutputProvider>
-						<openAction enabled="true" filePath=""/>
-						<parser enabled="true"/>
-					</buildOutputProvider>
-					<scannerInfoProvider id="specsFile">
-						<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
-						<parser enabled="true"/>
-					</scannerInfoProvider>
-				</profile>
-				<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP">
-					<buildOutputProvider>
-						<openAction enabled="true" filePath=""/>
-						<parser enabled="true"/>
-					</buildOutputProvider>
-					<scannerInfoProvider id="specsFile">
-						<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
-						<parser enabled="true"/>
-					</scannerInfoProvider>
-				</profile>
-				<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC">
-					<buildOutputProvider>
-						<openAction enabled="true" filePath=""/>
-						<parser enabled="true"/>
-					</buildOutputProvider>
-					<scannerInfoProvider id="specsFile">
-						<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
-						<parser enabled="true"/>
-					</scannerInfoProvider>
-				</profile>
-				<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
-					<buildOutputProvider>
-						<openAction enabled="true" filePath=""/>
-						<parser enabled="true"/>
-					</buildOutputProvider>
-					<scannerInfoProvider id="specsFile">
-						<runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/${specs_file}&quot;'" command="sh" useDefault="true"/>
-						<parser enabled="true"/>
-					</scannerInfoProvider>
-				</profile>
-				<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
-					<buildOutputProvider>
-						<openAction enabled="true" filePath=""/>
-						<parser enabled="true"/>
-					</buildOutputProvider>
-					<scannerInfoProvider id="specsFile">
-						<runAction arguments="-c 'g++ -E -P -v -dD &quot;${plugin_state_location}/specs.cpp&quot;'" command="sh" useDefault="true"/>
-						<parser enabled="true"/>
-					</scannerInfoProvider>
-				</profile>
-				<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
-					<buildOutputProvider>
-						<openAction enabled="true" filePath=""/>
-						<parser enabled="true"/>
-					</buildOutputProvider>
-					<scannerInfoProvider id="specsFile">
-						<runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/specs.c&quot;'" command="sh" useDefault="true"/>
-						<parser enabled="true"/>
-					</scannerInfoProvider>
-				</profile>
-				<scannerConfigBuildInfo instanceId="com.crt.advproject.toolchain.exe.debug.1281391870.909223166;com.crt.advproject.toolchain.exe.debug.1281391870.909223166.;cdt.managedbuild.tool.gnu.c.compiler.mingw.base.1123347799;cdt.managedbuild.tool.gnu.c.compiler.input.1476468867">
-					<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC"/>
-					<profile id="com.crt.advproject.GCCManagedMakePerProjectProfileCPP">
-						<buildOutputProvider>
-							<openAction enabled="false" filePath=""/>
-							<parser enabled="false"/>
-						</buildOutputProvider>
-						<scannerInfoProvider id="com.crt.advproject.specsFile">
-							<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="arm-none-eabi-c++" useDefault="true"/>
-							<parser enabled="true"/>
-						</scannerInfoProvider>
-					</profile>
-					<profile id="com.crt.advproject.GCCManagedMakePerProjectProfile">
-						<buildOutputProvider>
-							<openAction enabled="false" filePath=""/>
-							<parser enabled="false"/>
-						</buildOutputProvider>
-						<scannerInfoProvider id="com.crt.advproject.specsFile">
-							<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file} " command="arm-none-eabi-gcc" useDefault="true"/>
-							<parser enabled="true"/>
-						</scannerInfoProvider>
-					</profile>
-					<profile id="com.crt.advproject.GASManagedMakePerProjectProfile">
-						<buildOutputProvider>
-							<openAction enabled="false" filePath=""/>
-							<parser enabled="false"/>
-						</buildOutputProvider>
-						<scannerInfoProvider id="com.crt.advproject.specsFile">
-							<runAction arguments="-x assembler-with-cpp -E -P -v -dD ${plugin_state_location}/${specs_file}" command="arm-none-eabi-gcc" useDefault="true"/>
-							<parser enabled="true"/>
-						</scannerInfoProvider>
-					</profile>
-					<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
-						<buildOutputProvider>
-							<openAction enabled="true" filePath=""/>
-							<parser enabled="true"/>
-						</buildOutputProvider>
-						<scannerInfoProvider id="specsFile">
-							<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
-							<parser enabled="true"/>
-						</scannerInfoProvider>
-					</profile>
-					<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
-						<buildOutputProvider>
-							<openAction enabled="true" filePath=""/>
-							<parser enabled="true"/>
-						</buildOutputProvider>
-						<scannerInfoProvider id="makefileGenerator">
-							<runAction arguments="-E -P -v -dD" command="" useDefault="true"/>
-							<parser enabled="true"/>
-						</scannerInfoProvider>
-					</profile>
-					<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
-						<buildOutputProvider>
-							<openAction enabled="true" filePath=""/>
-							<parser enabled="true"/>
-						</buildOutputProvider>
-						<scannerInfoProvider id="specsFile">
-							<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
-							<parser enabled="true"/>
-						</scannerInfoProvider>
-					</profile>
-					<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP">
-						<buildOutputProvider>
-							<openAction enabled="true" filePath=""/>
-							<parser enabled="true"/>
-						</buildOutputProvider>
-						<scannerInfoProvider id="specsFile">
-							<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
-							<parser enabled="true"/>
-						</scannerInfoProvider>
-					</profile>
-					<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC">
-						<buildOutputProvider>
-							<openAction enabled="true" filePath=""/>
-							<parser enabled="true"/>
-						</buildOutputProvider>
-						<scannerInfoProvider id="specsFile">
-							<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
-							<parser enabled="true"/>
-						</scannerInfoProvider>
-					</profile>
-					<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
-						<buildOutputProvider>
-							<openAction enabled="true" filePath=""/>
-							<parser enabled="true"/>
-						</buildOutputProvider>
-						<scannerInfoProvider id="specsFile">
-							<runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/${specs_file}&quot;'" command="sh" useDefault="true"/>
-							<parser enabled="true"/>
-						</scannerInfoProvider>
-					</profile>
-					<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
-						<buildOutputProvider>
-							<openAction enabled="true" filePath=""/>
-							<parser enabled="true"/>
-						</buildOutputProvider>
-						<scannerInfoProvider id="specsFile">
-							<runAction arguments="-c 'g++ -E -P -v -dD &quot;${plugin_state_location}/specs.cpp&quot;'" command="sh" useDefault="true"/>
-							<parser enabled="true"/>
-						</scannerInfoProvider>
-					</profile>
-					<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
-						<buildOutputProvider>
-							<openAction enabled="true" filePath=""/>
-							<parser enabled="true"/>
-						</buildOutputProvider>
-						<scannerInfoProvider id="specsFile">
-							<runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/specs.c&quot;'" command="sh" useDefault="true"/>
-							<parser enabled="true"/>
-						</scannerInfoProvider>
-					</profile>
-				</scannerConfigBuildInfo>
-			</storageModule>
-			<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets">
-				<buildTargets>
-					<target name="test local" path="tests/tests" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
-						<buildCommand>rake.bat</buildCommand>
-						<buildTarget>test:all</buildTarget>
-						<stopOnError>true</stopOnError>
-						<useDefaultCommand>false</useDefaultCommand>
-						<runAllBuilders>true</runAllBuilders>
-					</target>
-					<target name="test clean" path="tests/tests" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
-						<buildCommand>rake.bat</buildCommand>
-						<buildArguments/>
-						<buildTarget>clean</buildTarget>
-						<stopOnError>true</stopOnError>
-						<useDefaultCommand>false</useDefaultCommand>
-						<runAllBuilders>true</runAllBuilders>
-					</target>
-				</buildTargets>
+				<buildTargets/>
 			</storageModule>
 		</cconfiguration>
 	</storageModule>

+ 0 - 4
.project

@@ -29,10 +29,6 @@
 					<key>org.eclipse.cdt.make.core.buildCommand</key>
 					<value>make</value>
 				</dictionary>
-				<dictionary>
-					<key>org.eclipse.cdt.make.core.buildLocation</key>
-					<value>${workspace_loc:/tinyusb/test}</value>
-				</dictionary>
 				<dictionary>
 					<key>org.eclipse.cdt.make.core.cleanBuildTarget</key>
 					<value>clean</value>

+ 199 - 0
tests/.cproject

@@ -0,0 +1,199 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?fileVersion 4.0.0?>
+
+<cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
+	<storageModule moduleId="org.eclipse.cdt.core.settings">
+		<cconfiguration id="cdt.managedbuild.toolchain.gnu.cygwin.base.1801115102">
+			<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.toolchain.gnu.cygwin.base.1801115102" moduleId="org.eclipse.cdt.core.settings" name="Default">
+				<externalSettings/>
+				<extensions>
+					<extension id="org.eclipse.cdt.core.PE" point="org.eclipse.cdt.core.BinaryParser"/>
+					<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+					<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+					<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+					<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+					<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
+				</extensions>
+			</storageModule>
+			<storageModule moduleId="cdtBuildSystem" version="4.0.0">
+				<configuration artifactName="${ProjName}" buildProperties="" description="" id="cdt.managedbuild.toolchain.gnu.cygwin.base.1801115102" name="Default" parent="org.eclipse.cdt.build.core.emptycfg">
+					<folderInfo id="cdt.managedbuild.toolchain.gnu.cygwin.base.1801115102.535344002" name="/" resourcePath="">
+						<toolChain id="cdt.managedbuild.toolchain.gnu.mingw.base.1698092731" name="MinGW GCC" superClass="cdt.managedbuild.toolchain.gnu.mingw.base">
+							<targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.PE" id="cdt.managedbuild.target.gnu.platform.mingw.base.1102682489" name="Debug Platform" osList="win32" superClass="cdt.managedbuild.target.gnu.platform.mingw.base"/>
+							<builder buildPath="${workspace_loc:/tests/Default}" id="cdt.managedbuild.target.gnu.builder.base.2116698513" keepEnvironmentInBuildfile="false" name="Gnu Make Builder" superClass="cdt.managedbuild.target.gnu.builder.base"/>
+							<tool id="cdt.managedbuild.tool.gnu.assembler.mingw.base.106110968" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.mingw.base">
+								<inputType id="cdt.managedbuild.tool.gnu.assembler.input.836546927" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
+							</tool>
+							<tool id="cdt.managedbuild.tool.gnu.archiver.mingw.base.110080584" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.mingw.base"/>
+							<tool id="cdt.managedbuild.tool.gnu.cpp.compiler.mingw.base.749914640" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.mingw.base"/>
+							<tool id="cdt.managedbuild.tool.gnu.c.compiler.mingw.base.2075372140" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.mingw.base">
+								<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.519102576" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
+							</tool>
+							<tool id="cdt.managedbuild.tool.gnu.c.linker.mingw.base.621685388" name="MinGW C Linker" superClass="cdt.managedbuild.tool.gnu.c.linker.mingw.base">
+								<inputType id="cdt.managedbuild.tool.gnu.c.linker.input.145062980" superClass="cdt.managedbuild.tool.gnu.c.linker.input">
+									<additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
+									<additionalInput kind="additionalinput" paths="$(LIBS)"/>
+								</inputType>
+							</tool>
+							<tool id="cdt.managedbuild.tool.gnu.cpp.linker.mingw.base.1931278240" name="MinGW C++ Linker" superClass="cdt.managedbuild.tool.gnu.cpp.linker.mingw.base"/>
+						</toolChain>
+					</folderInfo>
+				</configuration>
+			</storageModule>
+			<storageModule moduleId="scannerConfiguration">
+				<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+				<profile id="com.crt.advproject.GCCManagedMakePerProjectProfileCPP">
+					<buildOutputProvider>
+						<openAction enabled="false" filePath=""/>
+						<parser enabled="false"/>
+					</buildOutputProvider>
+					<scannerInfoProvider id="com.crt.advproject.specsFile">
+						<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="arm-none-eabi-c++" useDefault="true"/>
+						<parser enabled="true"/>
+					</scannerInfoProvider>
+				</profile>
+				<profile id="com.crt.advproject.GCCManagedMakePerProjectProfile">
+					<buildOutputProvider>
+						<openAction enabled="false" filePath=""/>
+						<parser enabled="false"/>
+					</buildOutputProvider>
+					<scannerInfoProvider id="com.crt.advproject.specsFile">
+						<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file} " command="arm-none-eabi-gcc" useDefault="true"/>
+						<parser enabled="true"/>
+					</scannerInfoProvider>
+				</profile>
+				<profile id="com.crt.advproject.GASManagedMakePerProjectProfile">
+					<buildOutputProvider>
+						<openAction enabled="false" filePath=""/>
+						<parser enabled="false"/>
+					</buildOutputProvider>
+					<scannerInfoProvider id="com.crt.advproject.specsFile">
+						<runAction arguments="-x assembler-with-cpp -E -P -v -dD ${plugin_state_location}/${specs_file}" command="arm-none-eabi-gcc" useDefault="true"/>
+						<parser enabled="true"/>
+					</scannerInfoProvider>
+				</profile>
+				<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
+					<buildOutputProvider>
+						<openAction enabled="true" filePath=""/>
+						<parser enabled="true"/>
+					</buildOutputProvider>
+					<scannerInfoProvider id="specsFile">
+						<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+						<parser enabled="true"/>
+					</scannerInfoProvider>
+				</profile>
+				<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
+					<buildOutputProvider>
+						<openAction enabled="true" filePath=""/>
+						<parser enabled="true"/>
+					</buildOutputProvider>
+					<scannerInfoProvider id="makefileGenerator">
+						<runAction arguments="-E -P -v -dD" command="" useDefault="true"/>
+						<parser enabled="true"/>
+					</scannerInfoProvider>
+				</profile>
+				<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
+					<buildOutputProvider>
+						<openAction enabled="true" filePath=""/>
+						<parser enabled="true"/>
+					</buildOutputProvider>
+					<scannerInfoProvider id="specsFile">
+						<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+						<parser enabled="true"/>
+					</scannerInfoProvider>
+				</profile>
+				<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP">
+					<buildOutputProvider>
+						<openAction enabled="true" filePath=""/>
+						<parser enabled="true"/>
+					</buildOutputProvider>
+					<scannerInfoProvider id="specsFile">
+						<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+						<parser enabled="true"/>
+					</scannerInfoProvider>
+				</profile>
+				<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC">
+					<buildOutputProvider>
+						<openAction enabled="true" filePath=""/>
+						<parser enabled="true"/>
+					</buildOutputProvider>
+					<scannerInfoProvider id="specsFile">
+						<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+						<parser enabled="true"/>
+					</scannerInfoProvider>
+				</profile>
+				<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
+					<buildOutputProvider>
+						<openAction enabled="true" filePath=""/>
+						<parser enabled="true"/>
+					</buildOutputProvider>
+					<scannerInfoProvider id="specsFile">
+						<runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/${specs_file}&quot;'" command="sh" useDefault="true"/>
+						<parser enabled="true"/>
+					</scannerInfoProvider>
+				</profile>
+				<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
+					<buildOutputProvider>
+						<openAction enabled="true" filePath=""/>
+						<parser enabled="true"/>
+					</buildOutputProvider>
+					<scannerInfoProvider id="specsFile">
+						<runAction arguments="-c 'g++ -E -P -v -dD &quot;${plugin_state_location}/specs.cpp&quot;'" command="sh" useDefault="true"/>
+						<parser enabled="true"/>
+					</scannerInfoProvider>
+				</profile>
+				<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
+					<buildOutputProvider>
+						<openAction enabled="true" filePath=""/>
+						<parser enabled="true"/>
+					</buildOutputProvider>
+					<scannerInfoProvider id="specsFile">
+						<runAction arguments="-c 'gcc -E -P -v -dD &quot;${plugin_state_location}/specs.c&quot;'" command="sh" useDefault="true"/>
+						<parser enabled="true"/>
+					</scannerInfoProvider>
+				</profile>
+			</storageModule>
+			<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
+			<storageModule moduleId="org.eclipse.cdt.core.language.mapping"/>
+			<storageModule moduleId="org.eclipse.cdt.internal.ui.text.commentOwnerProjectMappings"/>
+			<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets">
+				<buildTargets>
+					<target name="default" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
+						<buildCommand>rake.bat</buildCommand>
+						<buildArguments/>
+						<buildTarget>default</buildTarget>
+						<stopOnError>true</stopOnError>
+						<useDefaultCommand>false</useDefaultCommand>
+						<runAllBuilders>true</runAllBuilders>
+					</target>
+					<target name="tests" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
+						<buildCommand>rake.bat</buildCommand>
+						<buildArguments/>
+						<buildTarget>test:all</buildTarget>
+						<stopOnError>true</stopOnError>
+						<useDefaultCommand>false</useDefaultCommand>
+						<runAllBuilders>true</runAllBuilders>
+					</target>
+					<target name="test delta" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
+						<buildCommand>rake.bat</buildCommand>
+						<buildTarget>test:delta</buildTarget>
+						<stopOnError>true</stopOnError>
+						<useDefaultCommand>false</useDefaultCommand>
+						<runAllBuilders>true</runAllBuilders>
+					</target>
+					<target name="clean" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
+						<buildCommand>rake.bat</buildCommand>
+						<buildArguments/>
+						<buildTarget>clean</buildTarget>
+						<stopOnError>true</stopOnError>
+						<useDefaultCommand>false</useDefaultCommand>
+						<runAllBuilders>true</runAllBuilders>
+					</target>
+				</buildTargets>
+			</storageModule>
+		</cconfiguration>
+	</storageModule>
+	<storageModule moduleId="cdtBuildSystem" version="4.0.0">
+		<project id="tests.null.1788750245" name="tests"/>
+	</storageModule>
+</cproject>

+ 82 - 0
tests/.project

@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>tests</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
+			<triggers>clean,full,incremental,</triggers>
+			<arguments>
+				<dictionary>
+					<key>?name?</key>
+					<value></value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.append_environment</key>
+					<value>true</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.autoBuildTarget</key>
+					<value>all</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.buildArguments</key>
+					<value></value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.buildCommand</key>
+					<value>make</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.buildLocation</key>
+					<value>${workspace_loc:/tests/Default}</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.cleanBuildTarget</key>
+					<value>clean</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.contents</key>
+					<value>org.eclipse.cdt.make.core.activeConfigSettings</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.enableAutoBuild</key>
+					<value>false</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.enableCleanBuild</key>
+					<value>true</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.enableFullBuild</key>
+					<value>true</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.fullBuildTarget</key>
+					<value>all</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.stopOnError</key>
+					<value>true</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.useDefaultBuildCmd</key>
+					<value>true</value>
+				</dictionary>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
+			<triggers>full,incremental,</triggers>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.cdt.core.cnature</nature>
+		<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
+		<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
+	</natures>
+</projectDescription>

+ 78 - 0
tests/project.yml

@@ -0,0 +1,78 @@
+---
+
+# 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
+  :use_deep_dependencies: FALSE
+  :build_root: build
+#  :release_build: TRUE
+  :test_file_prefix: test_
+
+:release_build:
+  :output: test_tinyusb.exe
+  :use_assembly: FALSE
+
+:environment:
+
+:extension:
+  :executable: .exe
+
+:paths:
+  :test:
+    - +:test/**
+    - -:test/support
+  :source:
+    - src/**
+    - ../../tinyusb/**
+    - ../../../CMSISv2p10_LPC43xx_DriverLib/inc
+  :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)
+  :commmon: &common_defines []
+  :test:
+    - *common_defines
+    - _TEST_
+    - MCU=MCU_LPC43XX
+    - CORE_M4
+  :test_preprocess:
+    - *common_defines
+    - _TEST_
+    - MCU=MCU_LPC43XX
+    - CORE_M4
+
+: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
+
+#: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:
+    - vendor/ceedling/plugins
+  :enabled:
+    - stdout_pretty_tests_report
+    - module_generator
+...

+ 4 - 0
tests/rakefile.rb

@@ -0,0 +1,4 @@
+PROJECT_CEEDLING_ROOT = "vendor/ceedling"
+load "#{PROJECT_CEEDLING_ROOT}/lib/rakefile.rb"
+
+task :default => %w[ test:all release ]

+ 56 - 0
tests/test/test_hid_host.c

@@ -0,0 +1,56 @@
+/*
+ * test_main.c
+ *
+ *  Created on: Dec 18, 2012
+ *      Author: hathach
+ */
+
+/*
+ * Software License Agreement (BSD License)
+ * Copyright (c) 2012, hathach (tinyusb.net)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ *    this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ *
+ * This file is part of the tiny usb stack.
+ */
+
+#include "unity.h"
+
+void setUp(void)
+{
+}
+
+void tearDown(void)
+{
+}
+
+void test_always_succeed()
+{
+
+}
+
+void test_always_fail()
+{
+  TEST_FAIL();
+}

BIN
tests/vendor/ceedling/docs/CExceptionSummary.pdf


BIN
tests/vendor/ceedling/docs/CMock Summary.pdf


BIN
tests/vendor/ceedling/docs/CeedlingPacket.pdf


BIN
tests/vendor/ceedling/docs/Unity Summary.pdf


+ 27 - 0
tests/vendor/ceedling/lib/build_invoker_utils.rb

@@ -0,0 +1,27 @@
+require 'constants'
+
+
+class BuildInvokerUtils
+
+  constructor :configurator, :streaminator
+  
+  def process_exception(exception, context, test_build=true)
+    if (exception.message =~ /Don't know how to build task '(.+)'/i)
+      error_header  = "ERROR: Rake could not find file referenced in source"
+      error_header += " or test" if (test_build) 
+      error_header += ": '#{$1}'. Possible stale dependency."
+      
+      @streaminator.stderr_puts( error_header )
+
+      if (@configurator.project_use_deep_dependencies)
+        help_message = "Try fixing #include statements or adding missing file. Then run '#{REFRESH_TASK_ROOT}#{context.to_s}' task and try again."      
+        @streaminator.stderr_puts( help_message )
+      end
+      
+      raise ''
+    else
+      raise exception
+    end
+  end
+  
+end

+ 42 - 0
tests/vendor/ceedling/lib/cacheinator.rb

@@ -0,0 +1,42 @@
+
+class Cacheinator
+
+  constructor :cacheinator_helper, :file_path_utils, :file_wrapper, :yaml_wrapper
+  
+  def cache_test_config(hash)
+    @yaml_wrapper.dump( @file_path_utils.form_test_build_cache_path( INPUT_CONFIGURATION_CACHE_FILE), hash )
+  end
+
+  def cache_release_config(hash)
+    @yaml_wrapper.dump( @file_path_utils.form_release_build_cache_path( INPUT_CONFIGURATION_CACHE_FILE ), hash )
+  end
+
+  
+  def diff_cached_test_file( filepath )
+    cached_filepath = @file_path_utils.form_test_build_cache_path( filepath )
+  
+    if (@file_wrapper.exist?( cached_filepath ) and (!@file_wrapper.compare( filepath, cached_filepath )))
+      @file_wrapper.cp(filepath, cached_filepath, {:preserve => false})
+      return filepath
+    elsif (!@file_wrapper.exist?( cached_filepath ))
+      @file_wrapper.cp(filepath, cached_filepath, {:preserve => false}) 
+      return filepath
+    end
+
+    return cached_filepath
+  end
+
+  
+  def diff_cached_test_config?(hash)
+    cached_filepath = @file_path_utils.form_test_build_cache_path(INPUT_CONFIGURATION_CACHE_FILE)
+    
+    return @cacheinator_helper.diff_cached_config?( cached_filepath, hash )
+  end
+
+  def diff_cached_release_config?(hash)
+    cached_filepath = @file_path_utils.form_release_build_cache_path(INPUT_CONFIGURATION_CACHE_FILE)
+    
+    return @cacheinator_helper.diff_cached_config?( cached_filepath, hash )
+  end
+  
+end

+ 12 - 0
tests/vendor/ceedling/lib/cacheinator_helper.rb

@@ -0,0 +1,12 @@
+
+class CacheinatorHelper
+
+  constructor :file_wrapper, :yaml_wrapper
+  
+  def diff_cached_config?(cached_filepath, hash)
+    return true if ( not @file_wrapper.exist?(cached_filepath) )
+    return true if ( (@file_wrapper.exist?(cached_filepath)) and (!(@yaml_wrapper.load(cached_filepath) == hash)) )
+    return false
+  end
+  
+end

+ 27 - 0
tests/vendor/ceedling/lib/ceedling.rb

@@ -0,0 +1,27 @@
+require 'rake'
+
+ERR_MSG = <<EOF
+I expected to see a project.yml file in the current directoy. Please create one
+by hand or by using the 'ceedling' shell command.
+EOF
+
+def ceedling_dir
+  File.join(
+    File.dirname(__FILE__),
+    '..')
+end
+
+def builtin_ceedling_plugins_path
+  File.join(
+    ceedling_dir,
+    'plugins')
+end
+
+ceeling_lib_rakefile = File.join( ceedling_dir,
+                                 'lib',
+                                 'rakefile.rb')
+if File.exists? "./project.yml"
+  load ceeling_lib_rakefile
+else
+  $stderr.puts ERR_MSG
+end

+ 16 - 0
tests/vendor/ceedling/lib/ceedling/version.rb

@@ -0,0 +1,16 @@
+# @private
+module Ceedling
+  module Version
+    # @private
+    GEM = "0.13.0"
+
+    # @private
+    CEEDLING = GEM
+    # @private
+    CEXCEPTION = "1.2.17"
+    # @private
+    CMOCK = "2.0.215"
+    # @private
+    UNITY = "2.1.0"
+  end
+end

+ 16 - 0
tests/vendor/ceedling/lib/ceedling/version.rb.erb

@@ -0,0 +1,16 @@
+# @private
+module Ceedling
+  module Version
+    # @private
+    GEM = "0.11.2"
+
+    # @private
+    CEEDLING = "<%= versions["CEEDLING"] %>"
+    # @private
+    CEXCEPTION = "<%= versions["CEXCEPTION"] %>"
+    # @private
+    CMOCK = "<%= versions["CMOCK"] %>"
+    # @private
+    UNITY = "<%= versions["UNITY"] %>"
+  end
+end

+ 15 - 0
tests/vendor/ceedling/lib/cmock_builder.rb

@@ -0,0 +1,15 @@
+require 'cmock'
+
+class CmockBuilder
+  
+  attr_accessor :cmock
+  
+  def setup 
+    @cmock = nil
+  end
+  
+  def manufacture(cmock_config)
+    @cmock = CMock.new(cmock_config)
+  end
+
+end

+ 329 - 0
tests/vendor/ceedling/lib/configurator.rb

@@ -0,0 +1,329 @@
+require 'defaults'
+require 'constants'
+require 'file_path_utils'
+require 'deep_merge'
+
+
+
+class Configurator
+
+  attr_reader :project_config_hash, :script_plugins, :rake_plugins
+  attr_accessor :project_logging, :project_debug, :project_verbosity, :sanity_checks
+  
+  constructor(:configurator_setup, :configurator_builder, :configurator_plugins, :cmock_builder, :yaml_wrapper, :system_wrapper) do
+    @project_logging   = false
+    @project_debug     = false
+    @project_verbosity = Verbosity::NORMAL
+    @sanity_checks     = TestResultsSanityChecks::NORMAL
+  end
+  
+  def setup
+    # special copy of cmock config to provide to cmock for construction
+    @cmock_config_hash = {}
+
+    # note: project_config_hash is an instance variable so constants and accessors created
+    # in eval() statements in build() have something of proper scope and persistence to reference
+    @project_config_hash = {}
+    @project_config_hash_backup = {}
+    
+    @script_plugins = []
+    @rake_plugins   = []
+  end
+  
+  
+  def replace_flattened_config(config)
+    @project_config_hash.merge!(config)
+    @configurator_setup.build_constants_and_accessors(@project_config_hash, binding())
+  end
+
+  
+  def store_config
+    @project_config_hash_backup = @project_config_hash.clone
+  end
+  
+  
+  def restore_config
+    @project_config_hash = @project_config_hash_backup
+    @configurator_setup.build_constants_and_accessors(@project_config_hash, binding())
+  end
+
+
+  def reset_defaults(config)
+    [:test_compiler,
+     :test_linker,
+     :test_fixture,
+     :test_includes_preprocessor,
+     :test_file_preprocessor,
+     :test_dependencies_generator,
+     :release_compiler,
+     :release_assembler,
+     :release_linker,
+     :release_dependencies_generator].each do |tool|
+      config[:tools].delete(tool) if (not (config[:tools][tool].nil?))
+    end
+  end
+
+
+  def populate_defaults(config)
+    new_config = DEFAULT_CEEDLING_CONFIG.deep_clone
+    new_config.deep_merge!(config)
+    config.replace(new_config)
+
+    @configurator_builder.populate_defaults( config, DEFAULT_TOOLS_TEST )
+    @configurator_builder.populate_defaults( config, DEFAULT_TOOLS_TEST_PREPROCESSORS ) if (config[:project][:use_test_preprocessor])
+    @configurator_builder.populate_defaults( config, DEFAULT_TOOLS_TEST_DEPENDENCIES )  if (config[:project][:use_deep_dependencies])
+    
+    @configurator_builder.populate_defaults( config, DEFAULT_TOOLS_RELEASE )              if (config[:project][:release_build])
+    @configurator_builder.populate_defaults( config, DEFAULT_TOOLS_RELEASE_ASSEMBLER )    if (config[:project][:release_build] and config[:release_build][:use_assembly])
+    @configurator_builder.populate_defaults( config, DEFAULT_TOOLS_RELEASE_DEPENDENCIES ) if (config[:project][:release_build] and config[:project][:use_deep_dependencies])
+  end
+  
+  
+  def populate_cmock_defaults(config)
+    # cmock has its own internal defaults handling, but we need to set these specific values
+    # so they're present for the build environment to access;
+    # note: these need to end up in the hash given to initialize cmock for this to be successful
+    cmock = config[:cmock] || {}
+
+    # yes, we're duplicating the default mock_prefix in cmock, but it's because we need CMOCK_MOCK_PREFIX always available in Ceedling's environment
+    cmock[:mock_prefix] = 'Mock' if (cmock[:mock_prefix].nil?)
+    
+    # just because strict ordering is the way to go
+    cmock[:enforce_strict_ordering] = true                                                  if (cmock[:enforce_strict_ordering].nil?)
+    
+    cmock[:mock_path] = File.join(config[:project][:build_root], TESTS_BASE_PATH, 'mocks')  if (cmock[:mock_path].nil?)
+    cmock[:verbosity] = @project_verbosity                                                  if (cmock[:verbosity].nil?)
+
+    cmock[:plugins] = []                             if (cmock[:plugins].nil?)
+    cmock[:plugins].map! { |plugin| plugin.to_sym }
+    cmock[:plugins] << (:cexception)                 if (!cmock[:plugins].include?(:cexception) and (config[:project][:use_exceptions]))
+    cmock[:plugins].uniq!
+
+    cmock[:unity_helper] = false                     if (cmock[:unity_helper].nil?)
+    
+    if (cmock[:unity_helper])
+      cmock[:includes] << File.basename(cmock[:unity_helper])
+      cmock[:includes].uniq!
+    end
+
+    @runner_config = cmock.merge(config[:test_runner] || {})
+    @cmock_builder.manufacture(cmock)
+  end
+  
+  
+  def get_runner_config
+    @runner_config
+  end
+
+
+  # grab tool names from yaml and insert into tool structures so available for error messages
+  # set up default values
+  def tools_setup(config)
+    config[:tools].each_key do |name|
+      tool = config[:tools][name]
+
+      # populate name if not given      
+      tool[:name] = name.to_s if (tool[:name].nil?)
+
+      # populate stderr redirect option
+      tool[:stderr_redirect] = StdErrRedirect::NONE if (tool[:stderr_redirect].nil?)
+
+      # populate background execution option
+      tool[:background_exec] = BackgroundExec::NONE if (tool[:background_exec].nil?)
+
+      # populate optional option to control verification of executable in search paths
+      tool[:optional] = false if (tool[:optional].nil?)
+    end
+  end
+  
+  
+  def tools_supplement_arguments(config)
+    tools_name_prefix = 'tools_'
+    config[:tools].each_key do |name|
+      tool = @project_config_hash[(tools_name_prefix + name.to_s).to_sym]
+
+      # smoosh in extra arguments if specified at top-level of config (useful for plugins & default gcc tools)
+      # arguments are squirted in at beginning of list
+      top_level_tool = (tools_name_prefix + name.to_s).to_sym
+      if (not config[top_level_tool].nil?)
+         # adding and flattening is not a good idea: might over-flatten if there's array nesting in tool args
+         # use _with_index to preserve order
+         config[top_level_tool][:arguments].each_with_index { |arg, index| tool[:arguments].insert( index, arg ) }
+      end
+    end
+  end
+
+
+  def find_and_merge_plugins(config)
+    # plugins must be loaded before generic path evaluation & magic that happen later;
+    # perform path magic here as discrete step
+    config[:plugins][:load_paths].each do |path|
+      path.replace(@system_wrapper.module_eval(path)) if (path =~ RUBY_STRING_REPLACEMENT_PATTERN)
+      FilePathUtils::standardize(path)
+    end
+    
+    paths_hash = @configurator_plugins.add_load_paths(config)
+  
+    @rake_plugins   = @configurator_plugins.find_rake_plugins(config)
+    @script_plugins = @configurator_plugins.find_script_plugins(config)
+    config_plugins  = @configurator_plugins.find_config_plugins(config)
+    plugin_defaults = @configurator_plugins.find_plugin_defaults(config)
+    
+    config_plugins.each do |plugin|
+      config.deep_merge( @yaml_wrapper.load(plugin) )
+    end
+    
+    plugin_defaults.each do |defaults|
+      @configurator_builder.populate_defaults( config, @yaml_wrapper.load(defaults) )
+    end
+    
+    # special plugin setting for results printing
+    config[:plugins][:display_raw_test_results] = true if (config[:plugins][:display_raw_test_results].nil?)
+    
+    paths_hash.each_pair { |name, path| config[:plugins][name] = path }
+  end
+
+  
+  def eval_environment_variables(config)
+    config[:environment].each do |hash|
+      key   = hash.keys[0]
+      value = hash[key]
+      items = []
+            
+      interstitial = ((key == :path) ? File::PATH_SEPARATOR : '')
+      items = ((value.class == Array) ? hash[key] : [value])
+      
+      items.each { |item| item.replace( @system_wrapper.module_eval( item ) ) if (item =~ RUBY_STRING_REPLACEMENT_PATTERN) }
+      hash[key] = items.join( interstitial )
+      
+      @system_wrapper.env_set( key.to_s.upcase, hash[key] )
+    end
+  end
+
+  
+  def eval_paths(config)
+    # [:plugins]:[load_paths] already handled
+    
+    paths = [ # individual paths that don't follow convention processed below
+      config[:project][:build_root],
+      config[:release_build][:artifacts]]
+
+    eval_path_list( paths )
+
+    config[:paths].each_pair { |collection, paths| eval_path_list( paths ) }
+
+    config[:files].each_pair { |collection, files| eval_path_list( paths ) }
+    
+    # all other paths at secondary hash key level processed by convention:
+    # ex. [:toplevel][:foo_path] & [:toplevel][:bar_paths] are evaluated
+    config.each_pair { |parent, child| eval_path_list( collect_path_list( child ) ) }    
+  end
+  
+  
+  def standardize_paths(config)
+    # [:plugins]:[load_paths] already handled
+    
+    paths = [ # individual paths that don't follow convention processed below
+      config[:project][:build_root],
+      config[:release_build][:artifacts]] # cmock path in case it was explicitly set in config
+
+    paths.flatten.each { |path| FilePathUtils::standardize( path ) }
+
+    config[:paths].each_pair do |collection, paths|
+      paths.each{|path| FilePathUtils::standardize( path )}
+      # ensure that list is an array (i.e. handle case of list being a single string)
+      config[:paths][collection] = [paths].flatten
+    end
+
+    config[:files].each_pair { |collection, files| files.each{ |path| FilePathUtils::standardize( path ) } }
+
+    config[:tools].each_pair { |tool, config| FilePathUtils::standardize( config[:executable] ) }
+    
+    # all other paths at secondary hash key level processed by convention:
+    # ex. [:toplevel][:foo_path] & [:toplevel][:bar_paths] are standardized
+    config.each_pair do |parent, child|
+      collect_path_list( child ).each { |path| FilePathUtils::standardize( path ) }
+    end    
+  end
+
+
+  def validate(config)
+    # collect felonies and go straight to jail
+    raise if (not @configurator_setup.validate_required_sections( config ))
+    
+    # collect all misdemeanors, everybody on probation
+    blotter = []
+    blotter << @configurator_setup.validate_required_section_values( config )
+    blotter << @configurator_setup.validate_paths( config )
+    blotter << @configurator_setup.validate_tools( config )
+    blotter << @configurator_setup.validate_plugins( config )
+    
+    raise if (blotter.include?( false ))
+  end
+    
+  
+  # create constants and accessors (attached to this object) from given hash
+  def build(config, *keys)
+    # create flattened & expanded configuration hash
+    built_config = @configurator_setup.build_project_config( config, @configurator_builder.flattenify( config ) )
+    
+    @project_config_hash = built_config.clone
+    store_config()
+
+    @configurator_setup.build_constants_and_accessors(built_config, binding())
+    
+    # top-level keys disappear when we flatten, so create global constants & accessors to any specified keys
+    keys.each do |key|
+      hash = { key => config[key] }
+      @configurator_setup.build_constants_and_accessors(hash, binding())      
+    end
+  end
+
+
+  # add to constants and accessors as post build step
+  def build_supplement(config_base, config_more)
+    # merge in our post-build additions to base configuration hash
+    config_base.deep_merge!( config_more )
+
+    # flatten our addition hash
+    config_more_flattened = @configurator_builder.flattenify( config_more )
+    
+    # merge our flattened hash with built hash from previous build
+    @project_config_hash.deep_merge!( config_more_flattened )
+    store_config()
+
+    # create more constants and accessors
+    @configurator_setup.build_constants_and_accessors(config_more_flattened, binding())
+    
+    # recreate constants & update accessors with new merged, base values
+    config_more.keys.each do |key|
+      hash = { key => config_base[key] }
+      @configurator_setup.build_constants_and_accessors(hash, binding())
+    end
+  end
+    
+  
+  def insert_rake_plugins(plugins)
+    plugins.each do |plugin|
+      @project_config_hash[:project_rakefile_component_files] << plugin
+    end
+  end
+  
+  ### private ###
+  
+  private
+
+  def collect_path_list( container )
+    paths = []
+    container.each_key { |key| paths << container[key] if (key.to_s =~ /_path(s)?$/) } if (container.class == Hash)
+    return paths.flatten
+  end
+  
+  def eval_path_list( paths )
+    paths.flatten.each do |path|
+      path.replace( @system_wrapper.module_eval( path ) ) if (path =~ RUBY_STRING_REPLACEMENT_PATTERN)
+    end
+  end
+  
+  
+end

+ 437 - 0
tests/vendor/ceedling/lib/configurator_builder.rb

@@ -0,0 +1,437 @@
+require 'rubygems'
+require 'rake'            # for ext() method
+require 'file_path_utils' # for class methods
+require 'defaults'
+require 'constants'       # for Verbosity constants class & base file paths
+
+
+
+class ConfiguratorBuilder
+  
+  constructor :file_system_utils, :file_wrapper, :system_wrapper
+    
+  
+  def build_global_constants(config)
+    config.each_pair do |key, value|
+      formatted_key = key.to_s.upcase
+      # undefine global constant if it already exists
+      Object.send(:remove_const, formatted_key.to_sym) if @system_wrapper.constants_include?(formatted_key)
+      # create global constant
+      Object.module_eval("#{formatted_key} = value")
+    end
+  end
+
+  
+  def build_accessor_methods(config, context)
+    config.each_pair do |key, value|
+      # fill configurator object with accessor methods
+      eval("def #{key.to_s.downcase}() return @project_config_hash[:#{key.to_s}] end", context)
+    end
+  end
+
+  
+  # create a flattened hash from the original configuration structure
+  def flattenify(config)
+    new_hash = {}
+    
+    config.each_key do | parent |
+
+      # gracefully handle empty top-level entries
+      next if (config[parent].nil?)
+
+      case config[parent]
+      when Array
+        config[parent].each do |hash|
+          key = "#{parent.to_s.downcase}_#{hash.keys[0].to_s.downcase}".to_sym
+          new_hash[key] = hash[hash.keys[0]]
+        end
+      when Hash
+        config[parent].each_pair do | child, value |
+          key = "#{parent.to_s.downcase}_#{child.to_s.downcase}".to_sym
+          new_hash[key] = value
+        end
+      # handle entries with no children, only values
+      else
+        new_hash["#{parent.to_s.downcase}".to_sym] = config[parent]
+      end
+      
+    end
+    
+    return new_hash
+  end
+
+  
+  def populate_defaults(config, defaults)
+    defaults.keys.sort.each do |section|
+      defaults[section].keys.sort.each do |entry|
+        config[section][entry] = defaults[section][entry].deep_clone if (config[section].nil? or config[section][entry].nil?)
+      end
+    end
+  end
+
+
+  def clean(in_hash)
+    # ensure that include files inserted into test runners have file extensions & proper ones at that
+    in_hash[:test_runner_includes].map!{|include| include.ext(in_hash[:extension_header])}
+  end
+
+
+  def set_build_paths(in_hash)
+    out_hash = {}
+
+    project_build_artifacts_root = File.join(in_hash[:project_build_root], 'artifacts')
+    project_build_tests_root     = File.join(in_hash[:project_build_root], TESTS_BASE_PATH)
+    project_build_release_root   = File.join(in_hash[:project_build_root], RELEASE_BASE_PATH)
+
+    paths = [
+      [:project_build_artifacts_root,  project_build_artifacts_root, true ],
+      [:project_build_tests_root,      project_build_tests_root,     true ],
+      [:project_build_release_root,    project_build_release_root,   in_hash[:project_release_build] ],
+
+      [:project_test_artifacts_path,     File.join(project_build_artifacts_root, TESTS_BASE_PATH), true ],
+      [:project_test_runners_path,       File.join(project_build_tests_root, 'runners'),           true ],
+      [:project_test_results_path,       File.join(project_build_tests_root, 'results'),           true ],
+      [:project_test_build_output_path,  File.join(project_build_tests_root, 'out'),               true ],
+      [:project_test_build_cache_path,   File.join(project_build_tests_root, 'cache'),             true ],
+      [:project_test_dependencies_path,  File.join(project_build_tests_root, 'dependencies'),      true ],
+
+      [:project_release_artifacts_path,         File.join(project_build_artifacts_root, RELEASE_BASE_PATH), in_hash[:project_release_build] ],
+      [:project_release_build_cache_path,       File.join(project_build_release_root, 'cache'),             in_hash[:project_release_build] ],
+      [:project_release_build_output_path,      File.join(project_build_release_root, 'out'),               in_hash[:project_release_build] ],
+      [:project_release_build_output_asm_path,  File.join(project_build_release_root, 'out', 'asm'),        in_hash[:project_release_build] ],
+      [:project_release_build_output_c_path,    File.join(project_build_release_root, 'out', 'c'),          in_hash[:project_release_build] ],
+      [:project_release_dependencies_path,      File.join(project_build_release_root, 'dependencies'),      in_hash[:project_release_build] ],
+
+      [:project_log_path,   File.join(in_hash[:project_build_root], 'logs'), true ],
+      [:project_temp_path,  File.join(in_hash[:project_build_root], 'temp'), true ],
+
+      [:project_test_preprocess_includes_path,  File.join(project_build_tests_root, 'preprocess/includes'), in_hash[:project_use_test_preprocessor] ],
+      [:project_test_preprocess_files_path,     File.join(project_build_tests_root, 'preprocess/files'),    in_hash[:project_use_test_preprocessor] ],
+    ]
+
+    out_hash[:project_build_paths] = []
+
+    # fetch already set mock path
+    out_hash[:project_build_paths] << in_hash[:cmock_mock_path] if (in_hash[:project_use_mocks])
+
+    paths.each do |path|
+      build_path_name          = path[0]
+      build_path               = path[1]
+      build_path_add_condition = path[2]
+      
+      # insert path into build paths if associated with true condition
+      out_hash[:project_build_paths] << build_path if build_path_add_condition
+      # set path symbol name and path for each entry in paths array
+      out_hash[build_path_name] = build_path
+    end
+
+    return out_hash
+  end
+
+
+  def set_force_build_filepaths(in_hash)
+    out_hash = {}
+    
+    out_hash[:project_test_force_rebuild_filepath]    = File.join( in_hash[:project_test_dependencies_path], 'force_build' )
+    out_hash[:project_release_force_rebuild_filepath] = File.join( in_hash[:project_release_dependencies_path], 'force_build' ) if (in_hash[:project_release_build])
+
+    return out_hash
+  end
+
+
+  def set_rakefile_components(in_hash)
+    out_hash = {
+      :project_rakefile_component_files => 
+        [File.join(CEEDLING_LIB, 'tasks_base.rake'),
+         File.join(CEEDLING_LIB, 'tasks_filesystem.rake'),
+         File.join(CEEDLING_LIB, 'tasks_tests.rake'),
+         File.join(CEEDLING_LIB, 'tasks_vendor.rake'),
+         File.join(CEEDLING_LIB, 'rules_tests.rake')]}
+
+    out_hash[:project_rakefile_component_files] << File.join(CEEDLING_LIB, 'rules_cmock.rake') if (in_hash[:project_use_mocks])
+    out_hash[:project_rakefile_component_files] << File.join(CEEDLING_LIB, 'rules_preprocess.rake') if (in_hash[:project_use_test_preprocessor])
+    out_hash[:project_rakefile_component_files] << File.join(CEEDLING_LIB, 'rules_tests_deep_dependencies.rake') if (in_hash[:project_use_deep_dependencies])
+    out_hash[:project_rakefile_component_files] << File.join(CEEDLING_LIB, 'tasks_tests_deep_dependencies.rake') if (in_hash[:project_use_deep_dependencies])
+
+    out_hash[:project_rakefile_component_files] << File.join(CEEDLING_LIB, 'rules_release_deep_dependencies.rake') if (in_hash[:project_release_build] and in_hash[:project_use_deep_dependencies])
+    out_hash[:project_rakefile_component_files] << File.join(CEEDLING_LIB, 'rules_release.rake') if (in_hash[:project_release_build])
+    out_hash[:project_rakefile_component_files] << File.join(CEEDLING_LIB, 'tasks_release_deep_dependencies.rake') if (in_hash[:project_release_build] and in_hash[:project_use_deep_dependencies])
+    out_hash[:project_rakefile_component_files] << File.join(CEEDLING_LIB, 'tasks_release.rake') if (in_hash[:project_release_build])
+
+    return out_hash
+  end
+  
+
+  def set_library_build_info_filepaths(hash)
+
+    # Notes:
+    #  - Dependency on a change to our input configuration hash is handled elsewhere as it is 
+    #    dynamically formed during ceedling's execution
+    #  - Compiled vendor dependencies like cmock.o, unity.o, cexception.o are handled below;
+    #    here we're interested only in ceedling-based code generation dependencies
+    
+    ceedling_build_info_filepath = File.join(CEEDLING_RELEASE, 'build.info')
+    cmock_build_info_filepath    = FilePathUtils::form_ceedling_vendor_path('cmock/release', 'build.info')
+
+    out_hash = {
+      :ceedling_build_info_filepath => ceedling_build_info_filepath,
+      :cmock_build_info_filepath => cmock_build_info_filepath
+    }
+    
+    return out_hash
+  end
+
+  
+  def set_release_target(in_hash)
+    return {} if (not in_hash[:project_release_build])
+    
+    release_target_file = ((in_hash[:release_build_output].nil?) ? (DEFAULT_RELEASE_TARGET_NAME.ext(in_hash[:extension_executable])) : in_hash[:release_build_output])
+    release_map_file    = ((in_hash[:release_build_output].nil?) ? (DEFAULT_RELEASE_TARGET_NAME.ext(in_hash[:extension_map])) : in_hash[:release_build_output].ext(in_hash[:extension_map]))
+    
+    return {
+      # tempted to make a helper method in file_path_utils? stop right there, pal. you'll introduce a cyclical dependency
+      :project_release_build_target => File.join(in_hash[:project_build_release_root], release_target_file),
+      :project_release_build_map    => File.join(in_hash[:project_build_release_root], release_map_file)
+      }
+  end
+  
+
+  def collect_project_options(in_hash)
+    options = []
+    
+    in_hash[:project_options_paths].each do |path|
+      options << @file_wrapper.directory_listing( File.join(path, '*.yml') )
+    end
+    
+    return {
+      :collection_project_options => options.flatten
+      }
+  end
+  
+
+  def expand_all_path_globs(in_hash)
+    out_hash = {}
+    path_keys = []
+    
+    in_hash.each_key do |key|
+      next if (not key.to_s[0..4] == 'paths')
+      path_keys << key
+    end
+    
+    # sorted to provide assured order of traversal in test calls on mocks
+    path_keys.sort.each do |key|
+      out_hash["collection_#{key.to_s}".to_sym] = @file_system_utils.collect_paths( in_hash[key] )
+    end
+    
+    return out_hash
+  end
+
+
+  def collect_source_and_include_paths(in_hash)
+    return {
+      :collection_paths_source_and_include => 
+        ( in_hash[:collection_paths_source] + 
+          in_hash[:collection_paths_include] ).select {|x| File.directory?(x)}
+      }    
+  end
+
+
+  def collect_source_include_vendor_paths(in_hash)
+    extra_paths = []
+    extra_paths << FilePathUtils::form_ceedling_vendor_path(CEXCEPTION_LIB_PATH) if (in_hash[:project_use_exceptions])
+
+    return {
+      :collection_paths_source_include_vendor => 
+        in_hash[:collection_paths_source_and_include] + 
+        extra_paths
+      }    
+  end
+
+
+  def collect_test_support_source_include_paths(in_hash)
+    return {
+      :collection_paths_test_support_source_include => 
+        (in_hash[:collection_paths_test] +
+        in_hash[:collection_paths_support] +
+        in_hash[:collection_paths_source] + 
+        in_hash[:collection_paths_include] ).select {|x| File.directory?(x)}
+      }    
+  end
+
+
+  def collect_vendor_paths(in_hash)
+    return {:collection_paths_vendor => get_vendor_paths(in_hash)}
+  end
+  
+
+  def collect_test_support_source_include_vendor_paths(in_hash)
+    return {
+      :collection_paths_test_support_source_include_vendor => 
+        in_hash[:collection_paths_test_support_source_include] +
+        get_vendor_paths(in_hash)
+      }    
+  end
+  
+  
+  def collect_tests(in_hash)
+    all_tests = @file_wrapper.instantiate_file_list
+
+    in_hash[:collection_paths_test].each do |path|
+      all_tests.include( File.join(path, "#{in_hash[:project_test_file_prefix]}*#{in_hash[:extension_source]}") )
+    end
+
+    @file_system_utils.revise_file_list( all_tests, in_hash[:files_test] )
+
+    return {:collection_all_tests => all_tests}
+  end
+
+
+  def collect_assembly(in_hash)
+    all_assembly = @file_wrapper.instantiate_file_list
+
+    return {:collection_all_assembly => all_assembly} if (not in_hash[:release_build_use_assembly])
+    
+    in_hash[:collection_paths_source].each do |path|
+      all_assembly.include( File.join(path, "*#{in_hash[:extension_assembly]}") )
+    end
+    
+    @file_system_utils.revise_file_list( all_assembly, in_hash[:files_assembly] )
+
+    return {:collection_all_assembly => all_assembly}
+  end
+
+
+  def collect_source(in_hash)
+    all_source = @file_wrapper.instantiate_file_list
+    in_hash[:collection_paths_source].each do |path|
+      if File.exists?(path) and not File.directory?(path)
+        all_source.include( path )
+      else
+        all_source.include( File.join(path, "*#{in_hash[:extension_source]}") )
+      end
+    end
+    @file_system_utils.revise_file_list( all_source, in_hash[:files_source] )
+    
+    return {:collection_all_source => all_source}
+  end
+
+
+  def collect_headers(in_hash)
+    all_headers = @file_wrapper.instantiate_file_list
+
+    paths = 
+      in_hash[:collection_paths_test] +
+      in_hash[:collection_paths_support] +
+      in_hash[:collection_paths_source] + 
+      in_hash[:collection_paths_include]
+    
+    paths.each do |path|
+      all_headers.include( File.join(path, "*#{in_hash[:extension_header]}") )
+    end
+
+    @file_system_utils.revise_file_list( all_headers, in_hash[:files_include] )
+    
+    return {:collection_all_headers => all_headers}
+  end
+
+
+  def collect_all_existing_compilation_input(in_hash)
+    all_input = @file_wrapper.instantiate_file_list
+
+    paths = 
+      in_hash[:collection_paths_test] + 
+      in_hash[:collection_paths_support] + 
+      in_hash[:collection_paths_source] + 
+      in_hash[:collection_paths_include] +
+      [FilePathUtils::form_ceedling_vendor_path(UNITY_LIB_PATH)]
+    
+    paths << FilePathUtils::form_ceedling_vendor_path(CEXCEPTION_LIB_PATH) if (in_hash[:project_use_exceptions])
+    paths << FilePathUtils::form_ceedling_vendor_path(CMOCK_LIB_PATH) if (in_hash[:project_use_mocks])
+
+    paths.each do |path|
+      all_input.include( File.join(path, "*#{in_hash[:extension_header]}") )
+      if File.exists?(path) and not File.directory?(path)
+        all_input.include( path )
+      else
+        all_input.include( File.join(path, "*#{in_hash[:extension_source]}") )
+      end
+    end
+    
+    @file_system_utils.revise_file_list( all_input, in_hash[:files_test] )
+    @file_system_utils.revise_file_list( all_input, in_hash[:files_support] )
+    @file_system_utils.revise_file_list( all_input, in_hash[:files_source] )
+    @file_system_utils.revise_file_list( all_input, in_hash[:files_include] )
+    # finding assembly files handled explicitly through other means
+
+    return {:collection_all_existing_compilation_input => all_input}    
+  end
+
+
+  def collect_test_and_vendor_defines(in_hash)
+    test_defines = in_hash[:defines_test].clone
+
+    test_defines.concat(in_hash[:unity_defines])
+    test_defines.concat(in_hash[:cmock_defines])      if (in_hash[:project_use_mocks])
+    test_defines.concat(in_hash[:cexception_defines]) if (in_hash[:project_use_exceptions])
+    
+    return {:collection_defines_test_and_vendor => test_defines}
+  end
+
+
+  def collect_release_and_vendor_defines(in_hash)
+    release_defines = in_hash[:defines_release].clone
+    
+    release_defines.concat(in_hash[:cexception_defines]) if (in_hash[:project_use_exceptions])
+    
+    return {:collection_defines_release_and_vendor => release_defines}
+  end
+
+
+  def collect_release_artifact_extra_link_objects(in_hash)
+    objects = []
+
+    # no build paths here so plugins can remap if necessary (i.e. path mapping happens at runtime)
+    objects << CEXCEPTION_C_FILE.ext( in_hash[:extension_object] ) if (in_hash[:project_use_exceptions])
+
+    return {:collection_release_artifact_extra_link_objects => objects}
+  end
+  
+
+  def collect_test_fixture_extra_link_objects(in_hash)
+    #  Note: Symbols passed to compiler at command line can change Unity and CException behavior / configuration;
+    #    we also handle those dependencies elsewhere in compilation dependencies
+    
+    objects = [UNITY_C_FILE]
+    
+    # we don't include paths here because use of plugins or mixing different compilers may require different build paths
+    objects << CEXCEPTION_C_FILE if (in_hash[:project_use_exceptions])
+    objects << CMOCK_C_FILE      if (in_hash[:project_use_mocks])
+    
+    # if we're using mocks & a unity helper is defined & that unity helper includes a source file component (not only a header of macros),
+    # then link in the unity_helper object file too
+    if ( in_hash[:project_use_mocks] and
+         in_hash[:cmock_unity_helper] and 
+         @file_wrapper.exist?(in_hash[:cmock_unity_helper].ext(in_hash[:extension_source])) )
+      objects << File.basename(in_hash[:cmock_unity_helper])
+    end
+
+    # no build paths here so plugins can remap if necessary (i.e. path mapping happens at runtime)
+    objects.map! { |object| object.ext(in_hash[:extension_object]) }
+    
+    return { :collection_test_fixture_extra_link_objects => objects }
+  end
+
+
+  private
+
+  def get_vendor_paths(in_hash)
+    vendor_paths = []
+    vendor_paths << FilePathUtils::form_ceedling_vendor_path(UNITY_LIB_PATH)
+    vendor_paths << FilePathUtils::form_ceedling_vendor_path(CEXCEPTION_LIB_PATH) if (in_hash[:project_use_exceptions])
+    vendor_paths << FilePathUtils::form_ceedling_vendor_path(CMOCK_LIB_PATH)      if (in_hash[:project_use_mocks])
+    vendor_paths << in_hash[:cmock_mock_path]                                     if (in_hash[:project_use_mocks])
+
+    return vendor_paths
+  end
+  
+end

+ 124 - 0
tests/vendor/ceedling/lib/configurator_plugins.rb

@@ -0,0 +1,124 @@
+require 'constants'
+
+class ConfiguratorPlugins
+
+  constructor :stream_wrapper, :file_wrapper, :system_wrapper
+  attr_reader :rake_plugins, :script_plugins
+
+  def setup
+    @rake_plugins   = []
+    @script_plugins = []
+  end
+
+  
+  def add_load_paths(config)
+    plugin_paths = {}
+    
+    config[:plugins][:load_paths].each do |root|
+      @system_wrapper.add_load_path( root ) if ( not @file_wrapper.directory_listing( File.join( root, '*.rb' ) ).empty? )
+    
+      config[:plugins][:enabled].each do |plugin|
+        path = File.join(root, plugin, "lib")
+        old_path = File.join( root, plugin )
+
+        if ( not @file_wrapper.directory_listing( File.join( path, '*.rb' ) ).empty? )
+          plugin_paths[(plugin + '_path').to_sym] = path
+          @system_wrapper.add_load_path( path )
+        elsif ( not @file_wrapper.directory_listing( File.join( old_path, '*.rb' ) ).empty? )
+          plugin_paths[(plugin + '_path').to_sym] = old_path
+          @system_wrapper.add_load_path( old_path )
+        end
+      end
+    end
+    
+    return plugin_paths
+  end
+  
+  
+  # gather up and return .rake filepaths that exist on-disk
+  def find_rake_plugins(config)
+    plugins_with_path = []
+    
+    config[:plugins][:load_paths].each do |root|
+      config[:plugins][:enabled].each do |plugin|
+        rake_plugin_path = File.join(root, plugin, "#{plugin}.rake")
+        if (@file_wrapper.exist?(rake_plugin_path))
+          plugins_with_path << rake_plugin_path
+          @rake_plugins << plugin
+        end
+      end
+    end
+    
+    return plugins_with_path
+  end
+
+
+  # gather up and return just names of .rb classes that exist on-disk
+  def find_script_plugins(config)
+    config[:plugins][:load_paths].each do |root|
+      config[:plugins][:enabled].each do |plugin|
+        script_plugin_path = File.join(root, plugin, "lib", "#{plugin}.rb")
+
+        # Add the old path here to support legacy style. Eventaully remove.
+        old_script_plugin_path = File.join(root, plugin, "#{plugin}.rb")
+
+        if @file_wrapper.exist?(script_plugin_path) or @file_wrapper.exist?(old_script_plugin_path)
+          @script_plugins << plugin 
+        end
+
+        # Print depreciation warning.
+        if @file_wrapper.exist?(old_script_plugin_path)
+          $stderr.puts "WARNING: Depreciated plugin style used in #{plugin}. Use new directory structure!"
+        end
+      end
+    end
+    
+    return @script_plugins 
+  end
+  
+  
+  # gather up and return configuration .yml filepaths that exist on-disk
+  def find_config_plugins(config)
+    plugins_with_path = []
+    
+    config[:plugins][:load_paths].each do |root|
+      config[:plugins][:enabled].each do |plugin|
+        config_plugin_path = File.join(root, plugin, "config", "#{plugin}.yml")
+
+        # Add the old path here to support legacy style. Eventaully remove.
+        old_config_plugin_path = File.join(root, plugin, "#{plugin}.yml")
+
+        if @file_wrapper.exist?(config_plugin_path)
+          plugins_with_path << config_plugin_path
+        elsif @file_wrapper.exist?(old_config_plugin_path)
+          # there's a warning printed for this in find_script_plugins
+          plugins_with_path << old_config_plugin_path
+        end
+      end
+    end
+    
+    return plugins_with_path    
+  end
+
+  
+  # gather up and return default .yml filepaths that exist on-disk
+  def find_plugin_defaults(config)
+    defaults_with_path = []
+    
+    config[:plugins][:load_paths].each do |root|
+      config[:plugins][:enabled].each do |plugin|
+        default_path = File.join(root, plugin, 'config', 'defaults.yml')
+        old_default_path = File.join(root, plugin, 'defaults.yml')
+
+        if @file_wrapper.exist?(default_path)
+          defaults_with_path << default_path
+        elsif @file_wrapper.exist?(old_default_path)
+          defaults_with_path << old_default_path
+        end
+      end
+    end
+
+    return defaults_with_path    
+  end
+  
+end

+ 124 - 0
tests/vendor/ceedling/lib/configurator_setup.rb

@@ -0,0 +1,124 @@
+
+# add sort-ability to symbol so we can order keys array in hash for test-ability 
+class Symbol
+  include Comparable
+
+  def <=>(other)
+    self.to_s <=> other.to_s
+  end
+end
+
+
+class ConfiguratorSetup
+  
+  constructor :configurator_builder, :configurator_validator, :configurator_plugins, :stream_wrapper
+  
+  
+  def build_project_config(config, flattened_config)
+    ### flesh out config
+    @configurator_builder.clean(flattened_config)
+    
+    ### add to hash values we build up from configuration & file system contents
+    flattened_config.merge!(@configurator_builder.set_build_paths(flattened_config))
+    flattened_config.merge!(@configurator_builder.set_force_build_filepaths(flattened_config))
+    flattened_config.merge!(@configurator_builder.set_rakefile_components(flattened_config))
+    flattened_config.merge!(@configurator_builder.set_library_build_info_filepaths(flattened_config))
+    flattened_config.merge!(@configurator_builder.set_release_target(flattened_config))
+    flattened_config.merge!(@configurator_builder.collect_project_options(flattened_config))
+    
+    ### iterate through all entries in paths section and expand any & all globs to actual paths
+    flattened_config.merge!(@configurator_builder.expand_all_path_globs(flattened_config))
+    
+    flattened_config.merge!(@configurator_builder.collect_vendor_paths(flattened_config))
+    flattened_config.merge!(@configurator_builder.collect_source_and_include_paths(flattened_config))
+    flattened_config.merge!(@configurator_builder.collect_source_include_vendor_paths(flattened_config))
+    flattened_config.merge!(@configurator_builder.collect_test_support_source_include_paths(flattened_config))
+    flattened_config.merge!(@configurator_builder.collect_test_support_source_include_vendor_paths(flattened_config))
+    flattened_config.merge!(@configurator_builder.collect_tests(flattened_config))
+    flattened_config.merge!(@configurator_builder.collect_assembly(flattened_config))
+    flattened_config.merge!(@configurator_builder.collect_source(flattened_config))
+    flattened_config.merge!(@configurator_builder.collect_headers(flattened_config))
+    flattened_config.merge!(@configurator_builder.collect_all_existing_compilation_input(flattened_config))
+    flattened_config.merge!(@configurator_builder.collect_test_and_vendor_defines(flattened_config))
+    flattened_config.merge!(@configurator_builder.collect_release_and_vendor_defines(flattened_config))
+    flattened_config.merge!(@configurator_builder.collect_release_artifact_extra_link_objects(flattened_config))
+    flattened_config.merge!(@configurator_builder.collect_test_fixture_extra_link_objects(flattened_config))
+
+    return flattened_config
+  end
+
+  
+  def build_constants_and_accessors(config, context)
+    @configurator_builder.build_global_constants(config)
+    @configurator_builder.build_accessor_methods(config, context)
+  end
+  
+  
+  def validate_required_sections(config)
+    validation = []
+    validation << @configurator_validator.exists?(config, :project)
+    validation << @configurator_validator.exists?(config, :paths)
+
+    return false if (validation.include?(false))
+    return true
+  end
+
+  def validate_required_section_values(config)
+    validation = []
+    validation << @configurator_validator.exists?(config, :project, :build_root)
+    validation << @configurator_validator.exists?(config, :paths, :test)
+    validation << @configurator_validator.exists?(config, :paths, :source)
+
+    return false if (validation.include?(false))
+    return true
+  end
+
+  def validate_paths(config)
+    validation = []
+
+    validation << @configurator_validator.validate_filepath(config, :project, :build_root)
+    validation << @configurator_validator.validate_filepath(config, :cmock, :unity_helper) if config[:cmock][:unity_helper]
+
+    config[:project][:options_paths].each do |path|
+      validation << @configurator_validator.validate_filepath_simple( path, :project, :options_paths )
+    end
+
+    config[:plugins][:load_paths].each do |path|
+      validation << @configurator_validator.validate_filepath_simple( path, :plugins, :load_paths )
+    end
+
+    config[:paths].keys.sort.each do |key|
+      validation << @configurator_validator.validate_path_list(config, :paths, key)
+    end
+
+    return false if (validation.include?(false))
+    return true
+  end
+  
+  def validate_tools(config)
+    validation = []
+
+    config[:tools].keys.sort.each do |key|
+      validation << @configurator_validator.exists?(config, :tools, key, :executable)
+      validation << @configurator_validator.validate_executable_filepath(config, :tools, key, :executable) if (not config[:tools][key][:optional])
+      validation << @configurator_validator.validate_tool_stderr_redirect(config, :tools, key)
+    end
+
+    return false if (validation.include?(false))
+    return true
+  end
+
+  def validate_plugins(config)
+    missing_plugins = 
+      Set.new( config[:plugins][:enabled] ) - 
+      Set.new( @configurator_plugins.rake_plugins ) - 
+      Set.new( @configurator_plugins.script_plugins )
+    
+    missing_plugins.each do |plugin|
+      @stream_wrapper.stderr_puts("ERROR: Ceedling plugin '#{plugin}' contains no rake or ruby class entry point. (Misspelled or missing files?)")
+    end
+    
+    return ( (missing_plugins.size > 0) ? false : true )
+  end
+
+end

+ 184 - 0
tests/vendor/ceedling/lib/configurator_validator.rb

@@ -0,0 +1,184 @@
+require 'rubygems'
+require 'rake' # for ext()
+require 'constants'
+require 'tool_executor'    # for argument replacement pattern
+require 'file_path_utils'  # for glob handling class methods
+
+
+class ConfiguratorValidator
+  
+  constructor :file_wrapper, :stream_wrapper, :system_wrapper
+
+  # walk into config hash verify existence of data at key depth
+  def exists?(config, *keys)
+    hash  = retrieve_value(config, keys)
+    exist = !hash[:value].nil?
+
+    if (not exist)
+      # no verbosity checking since this is lowest level anyhow & verbosity checking depends on configurator
+      @stream_wrapper.stderr_puts("ERROR: Required config file entry #{format_key_sequence(keys, hash[:depth])} does not exist.")    
+    end
+    
+    return exist
+  end
+
+
+  # walk into config hash. verify directory path(s) at given key depth
+  def validate_path_list(config, *keys)
+    hash = retrieve_value(config, keys)
+    list = hash[:value]
+
+    # return early if we couldn't walk into hash and find a value
+    return false if (list.nil?)
+
+    path_list = []
+    exist = true
+    
+    case list
+      when String then path_list << list
+      when Array  then path_list =  list
+    end
+    
+    path_list.each do |path|
+      base_path = FilePathUtils::extract_path(path) # lop off add/subtract notation & glob specifiers
+      
+      if (not @file_wrapper.exist?(base_path))
+        # no verbosity checking since this is lowest level anyhow & verbosity checking depends on configurator
+        @stream_wrapper.stderr_puts("ERROR: Config path #{format_key_sequence(keys, hash[:depth])}['#{base_path}'] does not exist on disk.") 
+        exist = false
+      end 
+    end
+    
+    return exist
+  end
+
+  
+  # simple path verification
+  def validate_filepath_simple(path, *keys)
+    validate_path = path
+    
+    if (not @file_wrapper.exist?(validate_path))
+      # no verbosity checking since this is lowest level anyhow & verbosity checking depends on configurator
+      @stream_wrapper.stderr_puts("ERROR: Config path '#{validate_path}' associated with #{format_key_sequence(keys, keys.size)} does not exist on disk.") 
+      return false
+    end 
+    
+    return true
+  end
+ 
+  # walk into config hash. verify specified file exists.
+  def validate_filepath(config, *keys)
+    hash          = retrieve_value(config, keys)
+    filepath      = hash[:value]
+
+    # return early if we couldn't walk into hash and find a value
+    return false if (filepath.nil?)
+
+    # skip everything if we've got an argument replacement pattern
+    return true if (filepath =~ TOOL_EXECUTOR_ARGUMENT_REPLACEMENT_PATTERN)
+    
+    if (not @file_wrapper.exist?(filepath))
+      # no verbosity checking since this is lowest level anyhow & verbosity checking depends on configurator
+      @stream_wrapper.stderr_puts("ERROR: Config filepath #{format_key_sequence(keys, hash[:depth])}['#{filepath}'] does not exist on disk.") 
+      return false
+    end      
+
+    return true
+  end
+
+  # walk into config hash. verify specified file exists.
+  def validate_executable_filepath(config, *keys)
+    exe_extension = config[:extension][:executable]
+    hash          = retrieve_value(config, keys)
+    filepath      = hash[:value]
+
+    # return early if we couldn't walk into hash and find a value
+    return false if (filepath.nil?)
+
+    # skip everything if we've got an argument replacement pattern
+    return true if (filepath =~ TOOL_EXECUTOR_ARGUMENT_REPLACEMENT_PATTERN)
+    
+    # if there's no path included, verify file exists somewhere in system search paths
+    if (not filepath.include?('/'))
+      exists = false
+      
+      @system_wrapper.search_paths.each do |path|
+        if (@file_wrapper.exist?( File.join(path, filepath)) )
+          exists = true
+          break
+        end
+        
+        if (@file_wrapper.exist?( (File.join(path, filepath)).ext( exe_extension ) ))
+          exists = true
+          break
+        elsif (@system_wrapper.windows? and @file_wrapper.exist?( (File.join(path, filepath)).ext( EXTENSION_WIN_EXE ) ))
+          exists = true
+          break
+        end
+      end
+      
+      if (not exists)
+        # no verbosity checking since this is lowest level anyhow & verbosity checking depends on configurator
+        @stream_wrapper.stderr_puts("ERROR: Config filepath #{format_key_sequence(keys, hash[:depth])}['#{filepath}'] does not exist in system search paths.") 
+        return false        
+      end
+      
+    # if there is a path included, check that explicit filepath exists
+    else
+      if (not @file_wrapper.exist?(filepath))
+        # no verbosity checking since this is lowest level anyhow & verbosity checking depends on configurator
+        @stream_wrapper.stderr_puts("ERROR: Config filepath #{format_key_sequence(keys, hash[:depth])}['#{filepath}'] does not exist on disk.") 
+        return false
+      end      
+    end
+
+    return true
+  end
+  
+  def validate_tool_stderr_redirect(config, tools, tool)
+    redirect = config[tools][tool][:stderr_redirect]
+    if (redirect.class == Symbol)
+      # map constants and force to array of strings for runtime universality across ruby versions
+      if (not StdErrRedirect.constants.map{|constant| constant.to_s}.include?(redirect.to_s.upcase))
+        error = "ERROR: [:#{tools}][:#{tool}][:stderr_redirect][:#{redirect}] is not a recognized option " +
+                "{#{StdErrRedirect.constants.map{|constant| ':' + constant.to_s.downcase}.join(', ')}}."
+        @stream_wrapper.stderr_puts(error) 
+        return false        
+      end
+    end
+    
+    return true
+  end
+  
+  private #########################################
+  
+  
+  def retrieve_value(config, keys)
+    value = nil
+    hash  = config
+    depth = 0
+
+    # walk into hash & extract value at requested key sequence
+    keys.each do |symbol|
+      depth += 1
+      if (not hash[symbol].nil?)
+        hash  = hash[symbol]
+        value = hash
+      else
+        value = nil
+        break
+      end
+    end
+    
+    return {:value => value, :depth => depth}
+  end
+
+
+  def format_key_sequence(keys, depth)
+    walked_keys    = keys.slice(0, depth)
+    formatted_keys = walked_keys.map{|key| "[:#{key.to_s}]"}
+    
+    return formatted_keys.join
+  end
+  
+end

+ 91 - 0
tests/vendor/ceedling/lib/constants.rb

@@ -0,0 +1,91 @@
+
+class Verbosity
+  SILENT      = 0  # as silent as possible (though there are some messages that must be spit out)
+  ERRORS      = 1  # only errors
+  COMPLAIN    = 2  # spit out errors and warnings/notices
+  NORMAL      = 3  # errors, warnings/notices, standard status messages
+  OBNOXIOUS   = 4  # all messages including extra verbose output (used for lite debugging / verification)
+  DEBUG       = 5  # special extra verbose output for hardcore debugging
+end
+
+
+class TestResultsSanityChecks
+  NONE      = 0  # no sanity checking of test results
+  NORMAL    = 1  # perform non-problematic checks
+  THOROUGH  = 2  # perform checks that require inside knowledge of system workings
+end
+
+
+class StdErrRedirect
+  NONE = :none
+  AUTO = :auto
+  WIN  = :win
+  UNIX = :unix
+  TCSH = :tcsh
+end
+
+
+class BackgroundExec
+  NONE = :none
+  AUTO = :auto
+  WIN  = :win
+  UNIX = :unix
+end
+
+
+EXTENSION_WIN_EXE    = '.exe'
+EXTENSION_NONWIN_EXE = '.out'
+
+
+CEXCEPTION_ROOT_PATH = 'c_exception'
+CEXCEPTION_LIB_PATH  = "#{CEXCEPTION_ROOT_PATH}/lib"
+CEXCEPTION_C_FILE    = 'CException.c'
+CEXCEPTION_H_FILE    = 'CException.h'
+
+UNITY_ROOT_PATH        = 'unity'
+UNITY_LIB_PATH         = "#{UNITY_ROOT_PATH}/src"
+UNITY_C_FILE           = 'unity.c'
+UNITY_H_FILE           = 'unity.h'
+UNITY_INTERNALS_H_FILE = 'unity_internals.h'
+
+CMOCK_ROOT_PATH = 'cmock'
+CMOCK_LIB_PATH  = "#{CMOCK_ROOT_PATH}/src"
+CMOCK_C_FILE    = 'cmock.c'
+CMOCK_H_FILE    = 'cmock.h'
+
+
+DEFAULT_CEEDLING_MAIN_PROJECT_FILE = 'project.yml' # main project file
+DEFAULT_CEEDLING_USER_PROJECT_FILE = 'user.yml'    # supplemental user config file
+
+INPUT_CONFIGURATION_CACHE_FILE     = 'input.yml'   # input configuration file dump
+
+
+TEST_ROOT_NAME    = 'test'
+TEST_TASK_ROOT    = TEST_ROOT_NAME + ':'
+TEST_SYM          = TEST_ROOT_NAME.to_sym
+
+RELEASE_ROOT_NAME = 'release'
+RELEASE_TASK_ROOT = RELEASE_ROOT_NAME + ':'
+RELEASE_SYM       = RELEASE_ROOT_NAME.to_sym
+
+REFRESH_ROOT_NAME = 'refresh'
+REFRESH_TASK_ROOT = REFRESH_ROOT_NAME + ':'
+REFRESH_SYM       = REFRESH_ROOT_NAME.to_sym
+
+UTILS_ROOT_NAME   = 'utils'
+UTILS_TASK_ROOT   = UTILS_ROOT_NAME + ':'
+UTILS_SYM         = UTILS_ROOT_NAME.to_sym
+
+OPERATION_COMPILE_SYM = :compile
+OPERATION_LINK_SYM    = :link
+
+
+RUBY_STRING_REPLACEMENT_PATTERN = /#\{.+\}/
+RUBY_EVAL_REPLACEMENT_PATTERN   = /^\{(.+)\}$/
+TOOL_EXECUTOR_ARGUMENT_REPLACEMENT_PATTERN = /(\$\{(\d+)\})/
+TEST_STDOUT_STATISTICS_PATTERN  = /-+\s+(\d+)\s+Tests\s+(\d+)\s+Failures\s+(\d+)\s+Ignored\s+(OK|FAIL)\s*/i
+
+NULL_FILE_PATH = '/dev/null'
+
+TESTS_BASE_PATH   = TEST_ROOT_NAME
+RELEASE_BASE_PATH = RELEASE_ROOT_NAME

+ 380 - 0
tests/vendor/ceedling/lib/defaults.rb

@@ -0,0 +1,380 @@
+require 'constants'
+require 'system_wrapper'
+require 'file_path_utils'
+
+
+DEFAULT_TEST_COMPILER_TOOL = {
+  :executable => FilePathUtils.os_executable_ext('gcc').freeze,
+  :name => 'default_test_compiler'.freeze,
+  :stderr_redirect => StdErrRedirect::NONE.freeze,
+  :background_exec => BackgroundExec::NONE.freeze,
+  :optional => false.freeze,
+  :arguments => [
+    {"-I\"$\"" => 'COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR'}.freeze,
+    {"-I\"$\"" => 'COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE'}.freeze,
+    {"-D$" => 'COLLECTION_DEFINES_TEST_AND_VENDOR'}.freeze,
+    "-DGNU_COMPILER".freeze,
+    "-c \"${1}\"".freeze,
+    "-o \"${2}\"".freeze,
+    # gcc's list file output options are complex; no use of ${3} parameter in default config    
+    ].freeze
+  }
+
+DEFAULT_TEST_LINKER_TOOL = {
+  :executable => FilePathUtils.os_executable_ext('gcc').freeze,
+  :name => 'default_test_linker'.freeze,
+  :stderr_redirect => StdErrRedirect::NONE.freeze,
+  :background_exec => BackgroundExec::NONE.freeze,
+  :optional => false.freeze,
+  :arguments => [
+    "\"${1}\"".freeze,
+    "-o \"${2}\"".freeze,
+    ].freeze
+  }
+  
+DEFAULT_TEST_FIXTURE_TOOL = {
+  :executable => '${1}'.freeze,
+  :name => 'default_test_fixture'.freeze,
+  :stderr_redirect => StdErrRedirect::AUTO.freeze,
+  :background_exec => BackgroundExec::NONE.freeze,
+  :optional => false.freeze,
+  :arguments => [].freeze
+  }
+
+
+
+DEFAULT_TEST_INCLUDES_PREPROCESSOR_TOOL = {
+  :executable => FilePathUtils.os_executable_ext('cpp').freeze,
+  :name => 'default_test_includes_preprocessor'.freeze,
+  :stderr_redirect => StdErrRedirect::NONE.freeze,
+  :background_exec => BackgroundExec::NONE.freeze,
+  :optional => false.freeze,
+  :arguments => [
+    '-MM'.freeze,
+    '-MG'.freeze,
+    # avoid some possibility of deep system lib header file complications by omitting vendor paths
+    # if cpp is run on *nix system, escape spaces in paths; if cpp on windows just use the paths collection as is
+    {"-I\"$\"" => "{SystemWrapper.windows? ? COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE : COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE.map{|path| path.gsub(\/ \/, \'\\\\ \') }}"}.freeze,
+    {"-D$" => 'COLLECTION_DEFINES_TEST_AND_VENDOR'}.freeze,
+    {"-D$" => 'DEFINES_TEST_PREPROCESS'}.freeze,
+    "-DGNU_PREPROCESSOR".freeze,
+    '-w'.freeze,
+    '-nostdinc'.freeze,
+    "\"${1}\"".freeze
+    ].freeze
+  }
+
+DEFAULT_TEST_FILE_PREPROCESSOR_TOOL = {
+  :executable => FilePathUtils.os_executable_ext('gcc').freeze,
+  :name => 'default_test_file_preprocessor'.freeze,
+  :stderr_redirect => StdErrRedirect::NONE.freeze,
+  :background_exec => BackgroundExec::NONE.freeze,
+  :optional => false.freeze,
+  :arguments => [
+    '-E'.freeze,
+    {"-I\"$\"" => 'COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR'}.freeze,
+    {"-I\"$\"" => 'COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE'}.freeze,
+    {"-D$" => 'COLLECTION_DEFINES_TEST_AND_VENDOR'}.freeze,
+    {"-D$" => 'DEFINES_TEST_PREPROCESS'}.freeze,
+    "-DGNU_PREPROCESSOR".freeze,
+    "\"${1}\"".freeze,
+    "-o \"${2}\"".freeze
+    ].freeze
+  }
+
+DEFAULT_TEST_DEPENDENCIES_GENERATOR_TOOL = {
+  :executable => FilePathUtils.os_executable_ext('gcc').freeze,
+  :name => 'default_test_dependencies_generator'.freeze,
+  :stderr_redirect => StdErrRedirect::NONE.freeze,
+  :background_exec => BackgroundExec::NONE.freeze,
+  :optional => false.freeze,
+  :arguments => [
+    {"-I\"$\"" => 'COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR'}.freeze,
+    {"-I\"$\"" => 'COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE'}.freeze,
+    {"-D$" => 'COLLECTION_DEFINES_TEST_AND_VENDOR'}.freeze, 
+    {"-D$" => 'DEFINES_TEST_PREPROCESS'}.freeze,     
+    "-DGNU_PREPROCESSOR".freeze,
+    "-MT \"${3}\"".freeze,
+    '-MM'.freeze,
+    '-MD'.freeze,
+    '-MG'.freeze,
+    "-MF \"${2}\"".freeze,
+    "-c \"${1}\"".freeze,
+    ].freeze
+  }
+
+DEFAULT_RELEASE_DEPENDENCIES_GENERATOR_TOOL = {
+  :executable => FilePathUtils.os_executable_ext('gcc').freeze,
+  :name => 'default_release_dependencies_generator'.freeze,
+  :stderr_redirect => StdErrRedirect::NONE.freeze,
+  :background_exec => BackgroundExec::NONE.freeze,
+  :optional => false.freeze,
+  :arguments => [
+    {"-I\"$\"" => 'COLLECTION_PATHS_SOURCE_INCLUDE_VENDOR'}.freeze,
+    {"-I\"$\"" => 'COLLECTION_PATHS_RELEASE_TOOLCHAIN_INCLUDE'}.freeze,
+    {"-D$" => 'COLLECTION_DEFINES_RELEASE_AND_VENDOR'}.freeze,
+    {"-D$" => 'DEFINES_RELEASE_PREPROCESS'}.freeze,
+    "-DGNU_PREPROCESSOR".freeze,
+    "-MT \"${3}\"".freeze,
+    '-MM'.freeze,
+    '-MD'.freeze,
+    '-MG'.freeze,
+    "-MF \"${2}\"".freeze,
+    "-c \"${1}\"".freeze,
+    ].freeze
+  }
+
+
+DEFAULT_RELEASE_COMPILER_TOOL = {
+  :executable => FilePathUtils.os_executable_ext('gcc').freeze,
+  :name => 'default_release_compiler'.freeze,
+  :stderr_redirect => StdErrRedirect::NONE.freeze,
+  :background_exec => BackgroundExec::NONE.freeze,
+  :optional => false.freeze,
+  :arguments => [
+    {"-I\"$\"" => 'COLLECTION_PATHS_SOURCE_INCLUDE_VENDOR'}.freeze,
+    {"-I\"$\"" => 'COLLECTION_PATHS_RELEASE_TOOLCHAIN_INCLUDE'}.freeze,
+    {"-D$" => 'COLLECTION_DEFINES_RELEASE_AND_VENDOR'}.freeze,
+    "-DGNU_COMPILER".freeze,
+    "-c \"${1}\"".freeze,
+    "-o \"${2}\"".freeze,
+    # gcc's list file output options are complex; no use of ${3} parameter in default config    
+    ].freeze
+  }
+
+DEFAULT_RELEASE_ASSEMBLER_TOOL = {
+  :executable => FilePathUtils.os_executable_ext('as').freeze,
+  :name => 'default_release_assembler'.freeze,
+  :stderr_redirect => StdErrRedirect::NONE.freeze,
+  :background_exec => BackgroundExec::NONE.freeze,
+  :optional => false.freeze,
+  :arguments => [
+    {"-I\"$\"" => 'COLLECTION_PATHS_SOURCE_AND_INCLUDE'}.freeze,
+    "\"${1}\"".freeze,
+    "-o \"${2}\"".freeze,
+    ].freeze
+  }
+
+DEFAULT_RELEASE_LINKER_TOOL = {
+  :executable => FilePathUtils.os_executable_ext('gcc').freeze,
+  :name => 'default_release_linker'.freeze,
+  :stderr_redirect => StdErrRedirect::NONE.freeze,
+  :background_exec => BackgroundExec::NONE.freeze,
+  :optional => false.freeze,
+  :arguments => [
+    "\"${1}\"".freeze,
+    "-o \"${2}\"".freeze,
+    ].freeze
+  }
+
+  
+DEFAULT_TOOLS_TEST = {
+  :tools => {
+    :test_compiler => DEFAULT_TEST_COMPILER_TOOL,
+    :test_linker   => DEFAULT_TEST_LINKER_TOOL,
+    :test_fixture  => DEFAULT_TEST_FIXTURE_TOOL,
+    }
+  }
+  
+DEFAULT_TOOLS_TEST_PREPROCESSORS = {
+  :tools => {
+    :test_includes_preprocessor => DEFAULT_TEST_INCLUDES_PREPROCESSOR_TOOL,
+    :test_file_preprocessor     => DEFAULT_TEST_FILE_PREPROCESSOR_TOOL,
+    }
+  }
+
+DEFAULT_TOOLS_TEST_DEPENDENCIES = {
+  :tools => {
+    :test_dependencies_generator => DEFAULT_TEST_DEPENDENCIES_GENERATOR_TOOL,
+    }
+  }
+
+
+DEFAULT_TOOLS_RELEASE = {
+  :tools => {
+    :release_compiler => DEFAULT_RELEASE_COMPILER_TOOL,
+    :release_linker   => DEFAULT_RELEASE_LINKER_TOOL,
+    }
+  }
+
+DEFAULT_TOOLS_RELEASE_ASSEMBLER = {
+  :tools => {
+    :release_assembler => DEFAULT_RELEASE_ASSEMBLER_TOOL,
+    }
+  }
+
+DEFAULT_TOOLS_RELEASE_DEPENDENCIES = {
+  :tools => {
+    :release_dependencies_generator => DEFAULT_RELEASE_DEPENDENCIES_GENERATOR_TOOL,
+    }
+  }
+
+  
+DEFAULT_RELEASE_TARGET_NAME = 'project'
+
+DEFAULT_CEEDLING_CONFIG = {
+    :project => {
+      # :build_root must be set by user
+      :use_exceptions => true,
+      :use_mocks => true,
+      :compile_threads => 1,
+      :test_threads => 1,
+      :use_test_preprocessor => false,
+      :use_deep_dependencies => false,
+      :test_file_prefix => 'test_',
+      :options_paths => [],
+      :release_build => false,
+    },
+
+    :release_build => {
+      # :output is set while building configuration -- allows smart default system-dependent file extension handling
+      :use_assembly => false,
+      :artifacts => [],
+    },
+
+    :paths => {
+      :test => [],   # must be populated by user
+      :source => [], # must be populated by user
+      :support => [],
+      :include => [],
+      :test_toolchain_include => [],
+      :release_toolchain_include => [],
+    },
+    
+    :files => {
+      :test => [],
+      :source => [],
+      :assembly => [],
+      :support => [],
+      :include => [],
+    },
+    
+    # unlike other top-level entries, environment's value is an array to preserve order
+    :environment => [
+      # when evaluated, this provides wider text field for rake task comments
+      {:rake_columns => '120'},
+    ],
+    
+    :defines => {
+      :test => [],
+      :test_preprocess => [],
+      :release => [],
+      :release_preprocess => [],
+    },
+    
+    :flags => {},
+    
+    :extension => {
+      :header => '.h',
+      :source => '.c',
+      :assembly => '.s',
+      :object => '.o',
+      :executable => ( SystemWrapper.windows? ? EXTENSION_WIN_EXE : EXTENSION_NONWIN_EXE ),
+      :map => '.map',
+      :list => '.lst',
+      :testpass => '.pass',
+      :testfail => '.fail',
+      :dependencies => '.d',
+    },
+
+    :unity => {
+      :defines => []
+    },
+
+    :cmock => {
+      :defines => []
+    },
+
+    :cexception => {
+      :defines => []
+    },
+
+    :test_runner => {
+      :includes => [],
+      :file_suffix => '_runner',
+    },
+
+    # all tools populated while building up config structure
+    :tools => {},
+
+    # empty argument lists for default tools
+    # (these can be overridden in project file to add arguments to tools without totally redefining tools)
+    :test_compiler => { :arguments => [] },
+    :test_linker   => { :arguments => [] },
+    :test_fixture  => { 
+      :arguments => [],
+      :link_objects => [], # compiled object files to always be linked in (e.g. cmock.o if using mocks)
+    },
+    :test_includes_preprocessor  => { :arguments => [] },
+    :test_file_preprocessor      => { :arguments => [] },
+    :test_dependencies_generator => { :arguments => [] },
+    :release_compiler  => { :arguments => [] },
+    :release_linker    => { :arguments => [] },
+    :release_assembler => { :arguments => [] },
+    :release_dependencies_generator => { :arguments => [] },
+
+    :plugins => {
+      :load_paths => [],
+      :enabled => [],
+    }
+  }.freeze
+
+  
+DEFAULT_TESTS_RESULTS_REPORT_TEMPLATE = %q{
+% ignored        = hash[:results][:counts][:ignored]
+% failed         = hash[:results][:counts][:failed]
+% stdout_count   = hash[:results][:counts][:stdout]
+% header_prepend = ((hash[:header].length > 0) ? "#{hash[:header]}: " : '')
+% banner_width   = 25 + header_prepend.length # widest message
+
+% if (ignored > 0)
+<%=@ceedling[:plugin_reportinator].generate_banner(header_prepend + 'IGNORED UNIT TEST SUMMARY')%>
+%   hash[:results][:ignores].each do |ignore|
+%     ignore[:collection].each do |item|
+<%=ignore[:source][:path]%><%=File::SEPARATOR%><%=ignore[:source][:file]%>:<%=item[:line]%>:<%=item[:test]%>
+% if (item[:message].length > 0)
+: "<%=item[:message]%>"
+% else
+<%="\n"%>
+% end
+%     end
+%   end
+
+% end
+% if (failed > 0)
+<%=@ceedling[:plugin_reportinator].generate_banner(header_prepend + 'FAILED UNIT TEST SUMMARY')%>
+%   hash[:results][:failures].each do |failure|
+%     failure[:collection].each do |item|
+<%=failure[:source][:path]%><%=File::SEPARATOR%><%=failure[:source][:file]%>:<%=item[:line]%>:<%=item[:test]%>
+% if (item[:message].length > 0)
+: "<%=item[:message]%>"
+% else
+<%="\n"%>
+% end
+%     end
+%   end
+
+% end
+% if (stdout_count > 0)
+<%=@ceedling[:plugin_reportinator].generate_banner(header_prepend + 'UNIT TEST OTHER OUTPUT')%>
+%   hash[:results][:stdout].each do |string|
+%     string[:collection].each do |item|
+<%=string[:source][:path]%><%=File::SEPARATOR%><%=string[:source][:file]%>: "<%=item%>"
+%     end
+%   end
+
+% end
+% total_string = hash[:results][:counts][:total].to_s
+% format_string = "%#{total_string.length}i"
+<%=@ceedling[:plugin_reportinator].generate_banner(header_prepend + 'OVERALL UNIT TEST SUMMARY')%>
+% if (hash[:results][:counts][:total] > 0)
+TESTED:  <%=hash[:results][:counts][:total].to_s%>
+PASSED:  <%=sprintf(format_string, hash[:results][:counts][:passed])%>
+FAILED:  <%=sprintf(format_string, failed)%>
+IGNORED: <%=sprintf(format_string, ignored)%>
+% else
+
+No tests executed.
+% end
+
+}

+ 92 - 0
tests/vendor/ceedling/lib/dependinator.rb

@@ -0,0 +1,92 @@
+
+class Dependinator
+
+  constructor :configurator, :project_config_manager, :test_includes_extractor, :file_path_utils, :rake_wrapper, :file_wrapper
+
+  def touch_force_rebuild_files
+    @file_wrapper.touch( @configurator.project_test_force_rebuild_filepath ) 
+    @file_wrapper.touch( @configurator.project_release_force_rebuild_filepath ) if (@configurator.project_release_build)
+  end
+
+
+
+  def load_release_object_deep_dependencies(dependencies_list)
+    dependencies_list.each { |dependencies_file| @rake_wrapper.load_dependencies( dependencies_file ) }
+  end
+
+
+  def enhance_release_file_dependencies(files)
+    files.each do |filepath| 
+      @rake_wrapper[filepath].enhance( [@configurator.project_release_force_rebuild_filepath] ) if (@project_config_manager.release_config_changed)
+      @rake_wrapper[filepath].enhance( [@configurator.ceedling_build_info_filepath] )
+    end
+  end
+
+
+
+  def load_test_object_deep_dependencies(files_list)
+    dependencies_list = @file_path_utils.form_test_dependencies_filelist(files_list)
+    dependencies_list.each { |dependencies_file| @rake_wrapper.load_dependencies(dependencies_file) }
+  end
+
+
+  def enhance_runner_dependencies(runner_filepath)
+    @rake_wrapper[runner_filepath].enhance( [@configurator.project_test_force_rebuild_filepath] ) if (@project_config_manager.test_config_changed)
+    @rake_wrapper[runner_filepath].enhance( [@configurator.ceedling_build_info_filepath] )    
+  end
+  
+
+  def enhance_shallow_include_lists_dependencies(include_lists)
+    include_lists.each do |include_list_filepath|
+      @rake_wrapper[include_list_filepath].enhance( [@configurator.project_test_force_rebuild_filepath] ) if (@project_config_manager.test_config_changed)
+      @rake_wrapper[include_list_filepath].enhance( [@configurator.ceedling_build_info_filepath] )
+    end
+  end
+
+
+  def enhance_preprocesed_file_dependencies(files)
+    files.each do |filepath|
+      @rake_wrapper[filepath].enhance( [@configurator.project_test_force_rebuild_filepath] ) if (@project_config_manager.test_config_changed)
+      @rake_wrapper[filepath].enhance( [@configurator.ceedling_build_info_filepath] )
+    end
+  end
+
+
+  def enhance_mock_dependencies(mocks_list)
+    # if input configuration or ceedling changes, make sure these guys get rebuilt
+    mocks_list.each do |mock_filepath|
+      @rake_wrapper[mock_filepath].enhance( [@configurator.project_test_force_rebuild_filepath] ) if (@project_config_manager.test_config_changed)
+      @rake_wrapper[mock_filepath].enhance( [@configurator.cmock_unity_helper] )                  if (@configurator.cmock_unity_helper)
+      @rake_wrapper[mock_filepath].enhance( [@configurator.ceedling_build_info_filepath] )
+      @rake_wrapper[mock_filepath].enhance( [@configurator.cmock_build_info_filepath] )
+    end
+  end
+
+
+  def enhance_dependencies_dependencies(dependencies)
+    dependencies.each do |dependencies_filepath|
+      @rake_wrapper[dependencies_filepath].enhance( [@configurator.project_test_force_rebuild_filepath] ) if (@project_config_manager.test_config_changed)
+      @rake_wrapper[dependencies_filepath].enhance( [@configurator.ceedling_build_info_filepath] )
+    end
+  end
+
+
+  def enhance_test_build_object_dependencies(objects)
+    objects.each do |object_filepath|
+      @rake_wrapper[object_filepath].enhance( [@configurator.project_test_force_rebuild_filepath] ) if (@project_config_manager.test_config_changed)
+      @rake_wrapper[object_filepath].enhance( [@configurator.ceedling_build_info_filepath] )
+    end
+  end
+  
+
+  def enhance_results_dependencies(result_filepath)
+    @rake_wrapper[result_filepath].enhance( [@configurator.project_test_force_rebuild_filepath] ) if (@project_config_manager.test_config_changed)
+    @rake_wrapper[result_filepath].enhance( [@configurator.ceedling_build_info_filepath] )    
+  end
+
+
+  def setup_test_executable_dependencies(test, objects)
+    @rake_wrapper.create_file_task( @file_path_utils.form_test_executable_filepath(test), objects)
+  end
+
+end

+ 9 - 0
tests/vendor/ceedling/lib/erb_wrapper.rb

@@ -0,0 +1,9 @@
+require 'erb'
+
+class ErbWrapper
+  def generate_file(template, data, output_file)
+    File.open(output_file, "w") do |f|
+      f << ERB.new(template, 0, "<>").result(binding)
+    end
+  end
+end

+ 132 - 0
tests/vendor/ceedling/lib/file_finder.rb

@@ -0,0 +1,132 @@
+require 'rubygems'
+require 'rake' # for adding ext() method to string
+
+class FileFinder
+
+  constructor :configurator, :file_finder_helper, :cacheinator, :file_path_utils, :file_wrapper, :yaml_wrapper
+
+  def prepare_search_sources
+    @all_test_source_and_header_file_collection = 
+      @configurator.collection_all_tests +
+      @configurator.collection_all_source + 
+      @configurator.collection_all_headers
+  end
+
+
+  def find_header_file(mock_file)
+    header = File.basename(mock_file).sub(/#{@configurator.cmock_mock_prefix}/, '').ext(@configurator.extension_header)
+
+    found_path = @file_finder_helper.find_file_in_collection(header, @configurator.collection_all_headers, :error)
+
+    return found_path
+  end
+
+
+  def find_header_input_for_mock_file(mock_file)
+    found_path = find_header_file(mock_file)
+    mock_input = found_path
+    
+    if (@configurator.project_use_test_preprocessor)
+      mock_input = @cacheinator.diff_cached_test_file( @file_path_utils.form_preprocessed_file_filepath( found_path ) )
+    end
+    
+    return mock_input
+  end
+  
+
+  def find_source_from_test(test, complain)
+    test_prefix  = @configurator.project_test_file_prefix
+    source_paths = @configurator.collection_all_source
+    
+    source = File.basename(test).sub(/#{test_prefix}/, '')
+
+    # we don't blow up if a test file has no corresponding source file
+    return @file_finder_helper.find_file_in_collection(source, source_paths, complain)
+  end
+
+
+  def find_test_from_runner_path(runner_path)
+    extension_source = @configurator.extension_source
+
+    test_file = File.basename(runner_path).sub(/#{@configurator.test_runner_file_suffix}#{'\\'+extension_source}/, extension_source)
+    
+    found_path = @file_finder_helper.find_file_in_collection(test_file, @configurator.collection_all_tests, :error)
+
+    return found_path
+  end
+
+  
+  def find_test_input_for_runner_file(runner_path)
+    found_path   = find_test_from_runner_path(runner_path)
+    runner_input = found_path
+    
+    if (@configurator.project_use_test_preprocessor)
+      runner_input = @cacheinator.diff_cached_test_file( @file_path_utils.form_preprocessed_file_filepath( found_path ) )
+    end
+    
+    return runner_input
+  end
+  
+
+  def find_test_from_file_path(file_path)
+    test_file = File.basename(file_path).ext(@configurator.extension_source)
+    
+    found_path = @file_finder_helper.find_file_in_collection(test_file, @configurator.collection_all_tests, :error)
+    
+    return found_path    
+  end
+
+
+  def find_test_or_source_or_header_file(file_path)
+    file = File.basename(file_path)
+    return @file_finder_helper.find_file_in_collection(file, @all_test_source_and_header_file_collection, :error)
+  end
+  
+  
+  def find_compilation_input_file(file_path, complain=:error)
+    found_file = nil
+    
+    source_file = File.basename(file_path).ext(@configurator.extension_source)
+
+    # We only collect files that already exist when we start up.
+    # FileLists can produce undesired results for dynamically generated files depending on when they're accessed.
+    # So collect mocks and runners separately and right now.
+    if (source_file =~ /#{@configurator.test_runner_file_suffix}/)
+      found_file = 
+        @file_finder_helper.find_file_in_collection(
+          source_file,
+          @file_wrapper.directory_listing( File.join(@configurator.project_test_runners_path, '*') ),
+          complain)
+          
+    elsif (@configurator.project_use_mocks and (source_file =~ /#{@configurator.cmock_mock_prefix}/))
+      found_file = 
+        @file_finder_helper.find_file_in_collection(
+          source_file,
+          @file_wrapper.directory_listing( File.join(@configurator.cmock_mock_path, '*') ),
+          complain)
+
+    else
+      found_file = 
+        @file_finder_helper.find_file_in_collection(
+          source_file,
+          @configurator.collection_all_existing_compilation_input,
+          complain)
+    end
+
+    return found_file
+  end
+
+  
+  def find_source_file(file_path, complain)
+    source_file = File.basename(file_path).ext(@configurator.extension_source)
+    return @file_finder_helper.find_file_in_collection(source_file, @configurator.collection_all_source, complain)
+  end
+
+  
+  def find_assembly_file(file_path)
+    assembly_file = File.basename(file_path).ext(@configurator.extension_assembly)
+    return @file_finder_helper.find_file_in_collection(assembly_file, @configurator.collection_all_assembly, :error)
+  end
+    
+end
+

+ 54 - 0
tests/vendor/ceedling/lib/file_finder_helper.rb

@@ -0,0 +1,54 @@
+require 'fileutils'
+require 'constants' # for Verbosity enumeration
+
+class FileFinderHelper
+
+  constructor :streaminator
+  
+  
+  def find_file_in_collection(file_name, file_list, complain, extra_message="")
+    file_to_find = nil
+    
+    file_list.each do |item|
+      base_file = File.basename(item)
+
+      # case insensitive comparison
+      if (base_file.casecmp(file_name) == 0)
+        # case sensitive check
+        if (base_file == file_name)
+          file_to_find = item
+          break
+        else
+          blow_up(file_name, "However, a filename having different capitalization was found: '#{item}'.")
+        end
+      end
+      
+    end
+    
+    case (complain)
+      when :error then blow_up(file_name, extra_message) if (file_to_find.nil?)
+      when :warn  then gripe(file_name, extra_message)   if (file_to_find.nil?)
+      #when :ignore then      
+    end
+    
+    return file_to_find
+  end
+
+  private
+  
+  def blow_up(file_name, extra_message="")
+    error = "ERROR: Found no file '#{file_name}' in search paths."
+    error += ' ' if (extra_message.length > 0)
+    @streaminator.stderr_puts(error + extra_message, Verbosity::ERRORS)
+    raise
+  end
+  
+  def gripe(file_name, extra_message="")
+    warning = "WARNING: Found no file '#{file_name}' in search paths."
+    warning += ' ' if (extra_message.length > 0)
+    @streaminator.stderr_puts(warning + extra_message, Verbosity::COMPLAIN)
+  end
+
+end
+
+

+ 189 - 0
tests/vendor/ceedling/lib/file_path_utils.rb

@@ -0,0 +1,189 @@
+require 'rubygems'
+require 'rake' # for ext()
+require 'fileutils'
+require 'system_wrapper'
+
+# global utility methods (for plugins, project files, etc.)
+def ceedling_form_filepath(destination_path, original_filepath, new_extension=nil)
+  filename = File.basename(original_filepath)
+  filename.replace(filename.ext(new_extension)) if (!new_extension.nil?)
+  return File.join( destination_path.gsub(/\\/, '/'), filename )
+end
+
+class FilePathUtils
+
+  GLOB_MATCHER = /[\*\?\{\}\[\]]/
+
+  constructor :configurator, :file_wrapper
+
+
+  ######### class methods ##########
+
+  # standardize path to use '/' path separator & begin with './' & have no trailing path separator
+  def self.standardize(path)
+    path.strip!
+    path.gsub!(/\\/, '/')
+    path.gsub!(/^((\+|-):)?\.\//, '')
+    path.chomp!('/')
+    return path
+  end
+
+  def self.os_executable_ext(executable)
+    return executable.ext('.exe') if SystemWrapper.windows?
+    return executable
+  end
+
+  # extract directory path from between optional add/subtract aggregation modifiers and up to glob specifiers
+  # note: slightly different than File.dirname in that /files/foo remains /files/foo and does not become /files
+  def self.extract_path(path)
+    path = path.sub(/^(\+|-):/, '')
+    
+    # find first occurrence of path separator followed by directory glob specifier: *, ?, {, }, [, ]
+    find_index = (path =~ GLOB_MATCHER)
+    
+    # no changes needed (lop off final path separator)
+    return path.chomp('/') if (find_index.nil?)
+    
+    # extract up to first glob specifier
+    path = path[0..(find_index-1)]
+    
+    # lop off everything up to and including final path separator
+    find_index = path.rindex('/')
+    return path[0..(find_index-1)] if (not find_index.nil?)
+    
+    # return string up to first glob specifier if no path separator found
+    return path
+  end
+
+  # return whether the given path is to be aggregated (no aggregation modifier defaults to same as +:)
+  def self.add_path?(path)
+    return (path =~ /^-:/).nil?
+  end
+  
+  # get path (and glob) lopping off optional +: / -: prefixed aggregation modifiers
+  def self.extract_path_no_aggregation_operators(path)
+    return path.sub(/^(\+|-):/, '')
+  end
+  
+  # all the globs that may be in a path string work fine with one exception;
+  # to recurse through all subdirectories, the glob is dir/**/** but our paths use
+  # convention of only dir/**
+  def self.reform_glob(path)
+    return path if (path =~ /\/\*\*$/).nil?
+    return path + '/**'
+  end
+
+  def self.form_ceedling_vendor_path(*filepaths)
+    return File.join( CEEDLING_VENDOR, filepaths )
+  end
+
+  ######### instance methods ##########
+
+  def form_temp_path(filepath, prefix='')
+    return File.join( @configurator.project_temp_path, prefix + File.basename(filepath) )    
+  end
+  
+  ### release ###
+  def form_release_build_cache_path(filepath)
+    return File.join( @configurator.project_release_build_cache_path, File.basename(filepath) )    
+  end
+  
+  def form_release_dependencies_filepath(filepath)
+    return File.join( @configurator.project_release_dependencies_path, File.basename(filepath).ext(@configurator.extension_dependencies) )
+  end
+  
+  def form_release_build_c_object_filepath(filepath)
+    return File.join( @configurator.project_release_build_output_c_path, File.basename(filepath).ext(@configurator.extension_object) )
+  end
+
+  def form_release_build_asm_object_filepath(filepath)
+    return File.join( @configurator.project_release_build_output_asm_path, File.basename(filepath).ext(@configurator.extension_object) )
+  end
+
+  def form_release_build_c_objects_filelist(files)
+    return (@file_wrapper.instantiate_file_list(files)).pathmap("#{@configurator.project_release_build_output_c_path}/%n#{@configurator.extension_object}")
+  end
+
+  def form_release_build_asm_objects_filelist(files)
+    return (@file_wrapper.instantiate_file_list(files)).pathmap("#{@configurator.project_release_build_output_asm_path}/%n#{@configurator.extension_object}")
+  end
+
+  def form_release_build_c_list_filepath(filepath)
+    return File.join( @configurator.project_release_build_output_c_path, File.basename(filepath).ext(@configurator.extension_list) )
+  end
+  
+  def form_release_dependencies_filelist(files)
+    return (@file_wrapper.instantiate_file_list(files)).pathmap("#{@configurator.project_release_dependencies_path}/%n#{@configurator.extension_dependencies}")
+  end
+  
+  ### tests ###
+  def form_test_build_cache_path(filepath)
+    return File.join( @configurator.project_test_build_cache_path, File.basename(filepath) )    
+  end
+  
+  def form_pass_results_filepath(filepath)
+    return File.join( @configurator.project_test_results_path, File.basename(filepath).ext(@configurator.extension_testpass) )
+  end
+
+  def form_fail_results_filepath(filepath)
+    return File.join( @configurator.project_test_results_path, File.basename(filepath).ext(@configurator.extension_testfail) )
+  end
+
+  def form_runner_filepath_from_test(filepath)
+    return File.join( @configurator.project_test_runners_path, File.basename(filepath, @configurator.extension_source)) + @configurator.test_runner_file_suffix + @configurator.extension_source
+  end
+
+  def form_test_filepath_from_runner(filepath)
+    return filepath.sub(/#{TEST_RUNNER_FILE_SUFFIX}/, '')
+  end
+
+  def form_runner_object_filepath_from_test(filepath)
+    return (form_test_build_object_filepath(filepath)).sub(/(#{@configurator.extension_object})$/, "#{@configurator.test_runner_file_suffix}\\1")
+  end
+
+  def form_test_build_object_filepath(filepath)
+    return File.join( @configurator.project_test_build_output_path, File.basename(filepath).ext(@configurator.extension_object) )
+  end
+
+  def form_test_executable_filepath(filepath)
+    return File.join( @configurator.project_test_build_output_path, File.basename(filepath).ext(@configurator.extension_executable) )    
+  end
+
+  def form_test_build_map_filepath(filepath)
+    return File.join( @configurator.project_test_build_output_path, File.basename(filepath).ext(@configurator.extension_map) )
+  end
+
+  def form_test_build_list_filepath(filepath)
+    return File.join( @configurator.project_test_build_output_path, File.basename(filepath).ext(@configurator.extension_list) )
+  end
+
+  def form_preprocessed_file_filepath(filepath)
+    return File.join( @configurator.project_test_preprocess_files_path, File.basename(filepath) )    
+  end
+
+  def form_preprocessed_includes_list_filepath(filepath)
+    return File.join( @configurator.project_test_preprocess_includes_path, File.basename(filepath) )    
+  end
+
+  def form_test_build_objects_filelist(sources)
+    return (@file_wrapper.instantiate_file_list(sources)).pathmap("#{@configurator.project_test_build_output_path}/%n#{@configurator.extension_object}")
+  end
+  
+  def form_preprocessed_mockable_headers_filelist(mocks)
+    # pathmapping note: "%{#{@configurator.cmock_mock_prefix},}n" replaces mock_prefix with nothing (signified by absence of anything after comma inside replacement brackets)
+    return (@file_wrapper.instantiate_file_list(mocks)).pathmap("#{@configurator.project_test_preprocess_files_path}/%{#{@configurator.cmock_mock_prefix},}n#{@configurator.extension_header}")
+  end
+
+  def form_mocks_source_filelist(mocks)
+    return (@file_wrapper.instantiate_file_list(mocks)).pathmap("#{@configurator.cmock_mock_path}/%n#{@configurator.extension_source}")
+  end
+
+  def form_test_dependencies_filelist(files)
+    return (@file_wrapper.instantiate_file_list(files)).pathmap("#{@configurator.project_test_dependencies_path}/%n#{@configurator.extension_dependencies}")    
+  end
+
+  def form_pass_results_filelist(path, files)
+    return (@file_wrapper.instantiate_file_list(files)).pathmap("#{path}/%n#{@configurator.extension_testpass}")    
+  end
+
+end

+ 69 - 0
tests/vendor/ceedling/lib/file_system_utils.rb

@@ -0,0 +1,69 @@
+require 'rubygems'
+require 'rake'
+require 'set'
+require 'fileutils'
+require 'file_path_utils.rb'
+
+
+class FileSystemUtils
+  
+  constructor :file_wrapper
+
+  # build up path list from input of one or more strings or arrays of (+/-) paths & globs
+  def collect_paths(*paths)
+    raw   = []  # all paths and globs
+    plus  = Set.new # all paths to expand and add
+    minus = Set.new # all paths to remove from plus set
+    
+    # assemble all globs and simple paths, reforming our glob notation to ruby globs
+    paths.each do |paths_container|
+      case (paths_container)
+        when String then raw << (FilePathUtils::reform_glob(paths_container))
+        when Array  then paths_container.each {|path| raw << (FilePathUtils::reform_glob(path))}
+        else raise "Don't know how to handle #{paths_container.class}"
+      end
+    end
+
+    # iterate through each path and glob
+    raw.each do |path|
+    
+      dirs = []  # container for only (expanded) paths
+    
+      # if a glob, expand it and slurp up all non-file paths
+      if path.include?('*')
+        # grab base directory only if globs are snug up to final path separator
+        if (path =~ /\/\*+$/)
+          dirs << FilePathUtils.extract_path(path)
+        end
+        
+        # grab expanded sub-directory globs
+        expanded = @file_wrapper.directory_listing( FilePathUtils.extract_path_no_aggregation_operators(path) )
+        expanded.each do |entry|
+          dirs << entry if @file_wrapper.directory?(entry)
+        end
+        
+      # else just grab simple path
+      # note: we could just run this through glob expansion but such an
+      #       approach doesn't handle a path not yet on disk)
+      else
+        dirs << FilePathUtils.extract_path_no_aggregation_operators(path)
+      end
+      
+      # add dirs to the appropriate set based on path aggregation modifier if present
+      FilePathUtils.add_path?(path) ? plus.merge(dirs) : minus.merge(dirs)
+    end
+
+    return (plus - minus).to_a.uniq
+  end
+
+
+  # given a file list, add to it or remove from it
+  def revise_file_list(list, revisions)
+    revisions.each do |revision|
+      # include or exclude file or glob to file list
+      file = FilePathUtils.extract_path_no_aggregation_operators( revision )
+      FilePathUtils.add_path?(revision) ? list.include(file) : list.exclude(file)
+    end
+  end
+
+end

+ 10 - 0
tests/vendor/ceedling/lib/file_system_wrapper.rb

@@ -0,0 +1,10 @@
+
+class FileSystemWrapper
+
+  def cd(path)
+    FileUtils.cd path do
+      yield
+    end
+  end
+
+end

+ 79 - 0
tests/vendor/ceedling/lib/file_wrapper.rb

@@ -0,0 +1,79 @@
+require 'rubygems'
+require 'rake' # for FileList
+require 'constants'
+require 'fileutils'
+
+
+class FileWrapper
+
+  def get_expanded_path(path)
+    return File.expand_path(path)
+  end
+
+  def basename(path, extension=nil)
+    return File.basename(path, extension) if extension
+    return File.basename(path)
+  end
+
+  def exist?(filepath)
+    return true if (filepath == NULL_FILE_PATH)
+    return File.exist?(filepath)
+  end
+
+  def directory?(path)
+    return File.directory?(path)
+  end
+
+  def dirname(path)
+    return File.dirname(path)
+  end
+
+  def directory_listing(glob)
+    return Dir.glob(glob)
+  end
+
+  def rm_f(filepath, options={})
+    FileUtils.rm_f(filepath, options)
+  end
+
+  def rm_r(filepath, options={})
+    FileUtils.rm_r(filepath, options={})
+  end
+  
+  def cp(source, destination, options={})
+    FileUtils.cp(source, destination, options)
+  end
+
+  def compare(from, to)
+    return FileUtils.compare_file(from, to)
+  end
+  
+  def open(filepath, flags)
+    File.open(filepath, flags) do |file|
+      yield(file)
+    end
+  end
+
+  def read(filepath)
+    return File.read(filepath)
+  end
+
+  def touch(filepath, options={})
+    FileUtils.touch(filepath, options)
+  end
+
+  def write(filepath, contents, flags='w')
+    File.open(filepath, flags) do |file|
+      file.write(contents)
+    end    
+  end
+
+  def readlines(filepath)
+    return File.readlines(filepath)
+  end
+
+  def instantiate_file_list(files=[])
+    return FileList.new(files)
+  end
+
+end

+ 54 - 0
tests/vendor/ceedling/lib/flaginator.rb

@@ -0,0 +1,54 @@
+require 'rubygems'
+require 'rake' # for ext()
+require 'fileutils'
+require 'constants'
+
+
+# :flags:
+#   :release:
+#     :compile:
+#       :*:          # add '-foo' to compilation of all files not main.c
+#         - -foo
+#       :main:       # add '-Wall' to compilation of main.c
+#         - -Wall
+#   :test:
+#     :link:
+#       :test_main:  # add '--bar --baz' to linking of test_main.exe
+#         - --bar
+#         - --baz
+
+
+class Flaginator
+
+  constructor :configurator
+
+  def flag_down( operation, context, file )
+    # create configurator accessor method
+    accessor = ('flags_' + context.to_s).to_sym
+    
+    # create simple filename key from whatever filename provided
+    file_key = File.basename( file ).ext('').to_sym
+    
+    # if no entry in configuration for flags for this context, bail out
+    return [] if not @configurator.respond_to?( accessor )
+    
+    # get flags sub hash associated with this context
+    flags = @configurator.send( accessor )
+
+    # if operation not represented in flags hash, bail out
+    return [] if not flags.include?( operation )
+    
+    # redefine flags to sub hash associated with the operation
+    flags = flags[operation]
+    
+    # if our file is in the flags hash, extract the array of flags
+    if (flags.include?( file_key )) then return flags[file_key]
+    # if our file isn't in the flags hash, but there is default for all other files, extract array of flags
+    elsif (flags.include?( :* )) then return flags[:*]
+    end
+
+    # fall through: flags were specified but none applying to present file
+    return []
+  end
+
+end

+ 164 - 0
tests/vendor/ceedling/lib/generator.rb

@@ -0,0 +1,164 @@
+require 'constants'
+
+
+class Generator
+
+  constructor :configurator,
+              :generator_helper,
+              :preprocessinator,
+              :cmock_builder,
+              :generator_test_runner,
+              :generator_test_results,
+              :flaginator,
+              :test_includes_extractor,
+              :tool_executor,
+              :file_finder,
+              :file_path_utils,
+              :streaminator,
+              :plugin_manager,
+              :file_wrapper
+
+
+  def generate_shallow_includes_list(context, file)
+    @preprocessinator.preprocess_shallow_includes(file)
+  end
+
+  def generate_preprocessed_file(context, file)
+    @streaminator.stdout_puts("Preprocessing #{File.basename(file)}...", Verbosity::NORMAL)
+    @preprocessinator.preprocess_file(file)
+  end
+
+  def generate_dependencies_file(tool, context, source, object, dependencies)
+    @streaminator.stdout_puts("Generating dependencies for #{File.basename(source)}...", Verbosity::NORMAL)
+    
+    command = 
+      @tool_executor.build_command_line(
+        tool,
+        source,
+        dependencies,
+        object)
+    
+    @tool_executor.exec( command[:line], command[:options] )
+  end
+
+  def generate_mock(context, header_filepath)
+    arg_hash = {:header_file => header_filepath, :context => context}
+    @plugin_manager.pre_mock_generate( arg_hash )
+    
+    begin
+      @cmock_builder.cmock.setup_mocks( arg_hash[:header_file] )
+    rescue
+      raise
+    ensure
+      @plugin_manager.post_mock_generate( arg_hash )
+    end
+  end
+
+  # test_filepath may be either preprocessed test file or original test file
+  def generate_test_runner(context, test_filepath, runner_filepath)
+    arg_hash = {:context => context, :test_file => test_filepath, :runner_file => runner_filepath}
+    @plugin_manager.pre_runner_generate(arg_hash)
+    
+    # collect info we need
+    module_name = File.basename(arg_hash[:test_file])
+    test_cases  = @generator_test_runner.find_test_cases( @file_finder.find_test_from_runner_path(runner_filepath) )
+    mock_list   = @test_includes_extractor.lookup_raw_mock_list(arg_hash[:test_file])
+
+    @streaminator.stdout_puts("Generating runner for #{module_name}...", Verbosity::NORMAL)
+    
+    # build runner file
+    begin
+      @generator_test_runner.generate(module_name, runner_filepath, test_cases, mock_list)
+    rescue
+      raise
+    ensure
+      @plugin_manager.post_runner_generate(arg_hash)    
+    end
+  end
+
+  def generate_object_file(tool, context, source, object, list='')    
+    shell_result = {}
+    arg_hash = {:tool => tool, :context => context, :source => source, :object => object, :list => list}
+    @plugin_manager.pre_compile_execute(arg_hash)
+
+    @streaminator.stdout_puts("Compiling #{File.basename(arg_hash[:source])}...", Verbosity::NORMAL)
+    command =
+      @tool_executor.build_command_line( arg_hash[:tool],
+                                         arg_hash[:source],
+                                         arg_hash[:object],
+                                         arg_hash[:list],
+                                         @flaginator.flag_down( OPERATION_COMPILE_SYM, context, source ) )
+
+    begin
+      shell_result = @tool_executor.exec( command[:line], command[:options] )
+    rescue ShellExecutionException => ex
+      shell_result = ex.shell_result
+      raise ''
+    ensure
+      arg_hash[:shell_result] = shell_result
+      @plugin_manager.post_compile_execute(arg_hash)
+    end
+  end
+
+  def generate_executable_file(tool, context, objects, executable, map='')
+    shell_result = {}
+    arg_hash = {:tool => tool, :context => context, :objects => objects, :executable => executable, :map => map}
+    @plugin_manager.pre_link_execute(arg_hash)
+    
+    @streaminator.stdout_puts("Linking #{File.basename(arg_hash[:executable])}...", Verbosity::NORMAL)
+    command =
+      @tool_executor.build_command_line( arg_hash[:tool],
+                                         arg_hash[:objects],
+                                         arg_hash[:executable],
+                                         arg_hash[:map],
+                                         @flaginator.flag_down( OPERATION_LINK_SYM, context, executable ) )
+    
+    begin
+      shell_result = @tool_executor.exec( command[:line], command[:options] )
+    rescue ShellExecutionException => ex
+      notice =    "\n" +
+                  "NOTICE: If the linker reports missing symbols, the following may be to blame:\n" +
+                  "  1. Test lacks #include statements corresponding to needed source files.\n" +
+                  "  2. Project search paths do not contain source files corresponding to #include statements in the test.\n"
+      
+      if (@configurator.project_use_mocks)
+        notice += "  3. Test does not #include needed mocks.\n\n"
+      else
+        notice += "\n"
+      end
+      
+      @streaminator.stderr_puts(notice, Verbosity::COMPLAIN)
+      shell_result = ex.shell_result
+      raise ''
+    ensure
+      arg_hash[:shell_result] = shell_result
+      @plugin_manager.post_link_execute(arg_hash)
+    end
+  end
+
+  def generate_test_results(tool, context, executable, result)
+    arg_hash = {:tool => tool, :context => context, :executable => executable, :result_file => result}
+    @plugin_manager.pre_test_fixture_execute(arg_hash)
+    
+    @streaminator.stdout_puts("Running #{File.basename(arg_hash[:executable])}...", Verbosity::NORMAL)
+    
+    # Unity's exit code is equivalent to the number of failed tests, so we tell @tool_executor not to fail out if there are failures
+    # so that we can run all tests and collect all results
+    command = @tool_executor.build_command_line(arg_hash[:tool], arg_hash[:executable])
+    command[:options][:boom] = false
+    shell_result = @tool_executor.exec( command[:line], command[:options] )
+    
+    @generator_helper.test_results_error_handler(executable, shell_result)
+    
+    processed = @generator_test_results.process_and_write_results( shell_result,
+                                                                   arg_hash[:result_file],
+                                                                   @file_finder.find_test_from_file_path(arg_hash[:executable]) )
+    
+    arg_hash[:result_file]  = processed[:result_file]
+    arg_hash[:results]      = processed[:results]
+    arg_hash[:shell_result] = shell_result # for raw output display if no plugins for formatted display
+    
+    @plugin_manager.post_test_fixture_execute(arg_hash)
+  end
+  
+end

+ 40 - 0
tests/vendor/ceedling/lib/generator_helper.rb

@@ -0,0 +1,40 @@
+require 'constants'
+
+
+class GeneratorHelper
+
+  constructor :streaminator
+
+
+  def test_results_error_handler(executable, shell_result)
+    notice = ''
+    error  = false
+    
+    if (shell_result[:output].nil? or shell_result[:output].strip.empty?)
+      error = true
+      # mirror style of generic tool_executor failure output
+      notice  = "\n" +
+                "ERROR: Test executable \"#{File.basename(executable)}\" failed.\n" +
+                "> Produced no output to $stdout.\n"
+    elsif ((shell_result[:output] =~ TEST_STDOUT_STATISTICS_PATTERN).nil?)
+      error = true
+      # mirror style of generic tool_executor failure output
+      notice  = "\n" +
+                "ERROR: Test executable \"#{File.basename(executable)}\" failed.\n" +
+                "> Produced no final test result counts in $stdout:\n" +
+                "#{shell_result[:output].strip}\n"
+    end
+    
+    if (error)
+      # since we told the tool executor to ignore the exit code, handle it explicitly here
+      notice += "> And exited with status: [#{shell_result[:exit_code]}] (count of failed tests).\n" if (shell_result[:exit_code] != nil)
+      notice += "> And then likely crashed.\n"                                                       if (shell_result[:exit_code] == nil)
+
+      notice += "> This is often a symptom of a bad memory access in source or test code.\n\n"
+
+      @streaminator.stderr_puts(notice, Verbosity::COMPLAIN)
+      raise      
+    end
+  end
+  
+end

+ 89 - 0
tests/vendor/ceedling/lib/generator_test_results.rb

@@ -0,0 +1,89 @@
+require 'rubygems'
+require 'rake' # for .ext()
+require 'constants'
+
+ 
+class GeneratorTestResults
+
+  constructor :configurator, :generator_test_results_sanity_checker, :yaml_wrapper
+ 
+  def process_and_write_results(unity_shell_result, results_file, test_file)
+    output_file   = results_file
+    
+    results = get_results_structure
+    
+    results[:source][:path] = File.dirname(test_file)
+    results[:source][:file] = File.basename(test_file)
+    
+    # process test statistics
+    if (unity_shell_result[:output] =~ TEST_STDOUT_STATISTICS_PATTERN)
+      results[:counts][:total]   = $1.to_i
+      results[:counts][:failed]  = $2.to_i
+      results[:counts][:ignored] = $3.to_i
+      results[:counts][:passed]  = (results[:counts][:total] - results[:counts][:failed] - results[:counts][:ignored])
+    end
+
+    # remove test statistics lines
+    output_string = unity_shell_result[:output].sub(TEST_STDOUT_STATISTICS_PATTERN, '')
+    
+    # bust up the output into individual lines
+    raw_unity_lines = output_string.split(/\n|\r\n/)
+    
+    raw_unity_lines.each do |line|
+      # process unity output
+      case line
+      when /(:IGNORE)/
+        elements = extract_line_elements(line, results[:source][:file])
+        results[:ignores]   << elements[0]
+        results[:stdout]    << elements[1] if (!elements[1].nil?)
+      when /(:PASS$)/
+        elements = extract_line_elements(line, results[:source][:file])
+        results[:successes] << elements[0]
+        results[:stdout]    << elements[1] if (!elements[1].nil?)
+      when /(:FAIL)/
+        elements = extract_line_elements(line, results[:source][:file])
+        results[:failures]  << elements[0]
+        results[:stdout]    << elements[1] if (!elements[1].nil?)
+      else # collect up all other
+        results[:stdout] << line.chomp
+      end
+    end
+    
+    @generator_test_results_sanity_checker.verify(results, unity_shell_result[:exit_code])
+    
+    output_file = results_file.ext(@configurator.extension_testfail) if (results[:counts][:failed] > 0)
+    
+    @yaml_wrapper.dump(output_file, results)
+    
+    return { :result_file => output_file, :result => results }
+  end
+
+  private
+
+  def get_results_structure
+    return {
+      :source    => {:path => '', :file => ''},
+      :successes => [],
+      :failures  => [],
+      :ignores   => [],
+      :counts    => {:total => 0, :passed => 0, :failed => 0, :ignored  => 0},
+      :stdout    => [],
+      }
+  end
+  
+  def extract_line_elements(line, filename)
+    # handle anything preceding filename in line as extra output to be collected
+    stdout = nil
+    stdout_regex = /(.+)#{Regexp.escape(filename)}.+/i
+    
+    if (line =~ stdout_regex)
+      stdout = $1.clone
+      line.sub!(/#{Regexp.escape(stdout)}/, '')
+    end
+    
+    # collect up test results minus and extra output
+    elements = (line.strip.split(':'))[1..-1]
+    return {:test => elements[1], :line => elements[0].to_i, :message => (elements[3..-1].join(':')).strip}, stdout
+  end
+
+end

+ 62 - 0
tests/vendor/ceedling/lib/generator_test_results_sanity_checker.rb

@@ -0,0 +1,62 @@
+require 'constants'
+require 'rubygems'
+require 'rake' # for ext() method
+
+
+class GeneratorTestResultsSanityChecker
+
+  constructor :configurator, :streaminator
+  
+  def verify(results, unity_exit_code)
+  
+    # do no sanity checking if it's disabled
+    return if (@configurator.sanity_checks == TestResultsSanityChecks::NONE)
+  
+    ceedling_ignores_count   = results[:ignores].size
+    ceedling_failures_count  = results[:failures].size
+    ceedling_tests_summation = (ceedling_ignores_count + ceedling_failures_count + results[:successes].size)
+
+    # Exit code handling is not a sanity check that can always be performed because 
+    # command line simulators may or may not pass through Unity's exit code
+    if (@configurator.sanity_checks >= TestResultsSanityChecks::THOROUGH)
+      # many platforms limit exit codes to a maximum of 255
+      if ((ceedling_failures_count != unity_exit_code) and (unity_exit_code < 255))
+        sanity_check_warning(results[:source][:file], "Unity's exit code (#{unity_exit_code}) does not match Ceedling's summation of failed test cases (#{ceedling_failures_count}).")
+      end
+      
+      if ((ceedling_failures_count < 255) and (unity_exit_code == 255))
+        sanity_check_warning(results[:source][:file], "Ceedling's summation of failed test cases (#{ceedling_failures_count}) is less than Unity's exit code (255 or more).")
+      end
+    end
+    
+    if (ceedling_ignores_count != results[:counts][:ignored])
+      sanity_check_warning(results[:source][:file], "Unity's final ignore count (#{results[:counts][:ignored]}) does not match Ceedling's summation of ignored test cases (#{ceedling_ignores_count}).")
+    end
+    
+    if (ceedling_failures_count != results[:counts][:failed])
+      sanity_check_warning(results[:source][:file], "Unity's final fail count (#{results[:counts][:failed]}) does not match Ceedling's summation of failed test cases (#{ceedling_failures_count}).")
+    end
+
+    if (ceedling_tests_summation != results[:counts][:total])
+      sanity_check_warning(results[:source][:file], "Unity's final test count (#{results[:counts][:total]}) does not match Ceedling's summation of all test cases (#{ceedling_tests_summation}).")
+    end
+    
+  end
+
+  private
+  
+  def sanity_check_warning(file, message)
+    notice = "\n" + 
+             "ERROR: Internal sanity check for test fixture '#{file.ext(@configurator.extension_executable)}' finds that #{message}\n" +
+             "  Possible causes:\n" +
+             "    1. Your test + source dereferenced a null pointer.\n" +
+             "    2. Your test + source indexed past the end of a buffer.\n" +
+             "    3. Your test + source committed a memory access violation.\n" +
+             "    4. Your test fixture produced an exit code of 0 despite execution ending prematurely.\n" +
+             "  Sanity check failures of test results are usually a symptom of interrupted test execution.\n\n"
+    
+    @streaminator.stderr_puts( notice )
+    raise
+  end
+
+end

+ 63 - 0
tests/vendor/ceedling/lib/generator_test_runner.rb

@@ -0,0 +1,63 @@
+
+class GeneratorTestRunner
+
+  constructor :configurator, :file_path_utils, :file_wrapper
+
+  def find_test_cases(test_file)
+    tests = []
+    tests_and_line_numbers = []
+    lines = []
+    
+    # if we don't have preprocessor assistance, do some basic preprocessing of our own
+    if (not @configurator.project_use_test_preprocessor)
+      source = @file_wrapper.read(test_file)
+    
+      # remove line comments
+      source = source.gsub(/\/\/.*$/, '')
+      # remove block comments
+      source = source.gsub(/\/\*.*?\*\//m, '')
+    
+      # treat preprocessor directives as a logical line
+      lines = source.split(/(^\s*\#.*$) | (;|\{|\}) /x) # match ;, {, and } as end of lines
+    # otherwise, read the preprocessed file raw
+    else
+      lines = @file_wrapper.read( @file_path_utils.form_preprocessed_file_filepath(test_file) ).split(/;|\{|\}/)
+    end
+    
+    # step 1. find test functions in (possibly preprocessed) file
+    # (note that lines are not broken up at end of lines)
+    lines.each do |line|
+      if (line =~ /^\s*void\s+((T|t)est.*)\s*\(\s*(void)?\s*\)/m)
+        tests << ($1.strip)
+      end
+    end
+    
+    # step 2. associate test functions with line numbers in (non-preprocessed) original file
+    # (note that this time we must scan file contents broken up by end of lines)
+    raw_lines = @file_wrapper.read(test_file).split("\n")
+    raw_index = 0
+    
+    tests.each do |test|
+      raw_lines[raw_index..-1].each_with_index do |line, index|
+        # test function might be declared across lines; look for it by its name followed
+        #  by a few tell-tale signs
+        if (line =~ /#{test}\s*($|\(|\()/)
+          raw_index += (index + 1)
+          tests_and_line_numbers << {:test => test, :line_number => raw_index}
+          break
+        end
+      end
+    end
+    
+    return tests_and_line_numbers
+  end
+  
+  def generate(module_name, runner_filepath, test_cases, mock_list)
+    require 'generate_test_runner.rb'
+    @test_runner_generator ||= UnityTestRunnerGenerator.new( @configurator.get_runner_config )
+    @test_runner_generator.generate( module_name, 
+                                     runner_filepath, 
+                                     test_cases, 
+                                     mock_list)
+  end
+end

+ 31 - 0
tests/vendor/ceedling/lib/loginator.rb

@@ -0,0 +1,31 @@
+
+class Loginator
+
+  constructor :configurator, :project_file_loader, :project_config_manager, :file_wrapper, :system_wrapper
+
+
+  def setup_log_filepath
+    config_files = []
+    config_files << @project_file_loader.main_file
+    config_files << @project_file_loader.user_file
+    config_files.concat( @project_config_manager.options_files )
+    config_files.compact!
+    config_files.map! { |file| file.ext('') }
+    
+    log_name = config_files.join( '_' )
+
+    @project_log_filepath = File.join( @configurator.project_log_path, log_name.ext('.log') )
+  end
+
+
+  def log(string, heading=nil)
+    return if (not @configurator.project_logging)
+  
+    output  = "\n[#{@system_wrapper.time_now}]"
+    output += " :: #{heading}" if (not heading.nil?)
+    output += "\n#{string.strip}\n"
+
+    @file_wrapper.write(@project_log_filepath, output, 'a')
+  end
+  
+end

+ 46 - 0
tests/vendor/ceedling/lib/makefile.rb

@@ -0,0 +1,46 @@
+
+# modified version of Rake's provided make-style dependency loader
+# customizations: 
+#  (1) handles windows drives in paths -- colons don't confuse task demarcation
+#  (2) handles spaces in directory paths
+
+module Rake
+
+  # Makefile loader to be used with the import file loader.
+  class MakefileLoader
+
+    # Load the makefile dependencies in +fn+.
+    def load(fn)
+      open(fn) do |mf|
+        lines = mf.read
+        lines.gsub!(/#[^\n]*\n/m, "") # remove comments
+        lines.gsub!(/\\\n/, ' ')      # string together line continuations into single line
+        lines.split("\n").each do |line|
+          process_line(line)
+        end
+      end
+    end
+
+    private
+
+    # Process one logical line of makefile data.
+    def process_line(line)
+      # split on presence of task demaractor followed by space (i.e don't get confused by a colon in a win path)
+      file_tasks, args = line.split(/:\s/)
+
+      return if args.nil?
+      
+      # split at non-escaped space boundary between files (i.e. escaped spaces in paths are left alone)
+      dependents = args.split(/\b\s+/)
+      # replace escaped spaces and clean up any extra whitespace
+      dependents.map! { |path| path.gsub(/\\ /, ' ').strip }
+
+      file_tasks.strip.split.each do |file_task|
+        file file_task => dependents
+      end
+    end
+  end
+
+  # Install the handler
+  Rake.application.add_loader('mf', MakefileLoader.new)
+end

+ 307 - 0
tests/vendor/ceedling/lib/objects.yml

@@ -0,0 +1,307 @@
+
+file_wrapper:
+
+file_system_wrapper:
+
+stream_wrapper:
+
+rake_wrapper:
+
+yaml_wrapper:
+
+system_wrapper:
+
+cmock_builder:
+
+reportinator:
+
+rake_utils:
+  compose:
+    - rake_wrapper  
+
+system_utils:
+  compose:
+    - system_wrapper  
+
+file_path_utils:
+  compose:
+    - configurator
+    - file_wrapper
+
+file_system_utils:
+  compose: file_wrapper
+
+project_file_loader:
+  compose:
+    - yaml_wrapper
+    - stream_wrapper
+    - system_wrapper
+    - file_wrapper
+
+project_config_manager:
+  compose:
+    - cacheinator
+    - yaml_wrapper
+
+cacheinator:
+  compose:
+    - cacheinator_helper
+    - file_path_utils
+    - file_wrapper
+    - yaml_wrapper
+
+cacheinator_helper:
+  compose:
+    - file_wrapper
+    - yaml_wrapper
+
+tool_executor:
+  compose:
+    - configurator
+    - tool_executor_helper
+    - streaminator
+    - system_wrapper
+
+tool_executor_helper:
+  compose:
+    - streaminator
+    - system_utils
+    - system_wrapper
+
+configurator:
+  compose:
+    - configurator_setup
+    - configurator_plugins
+    - configurator_builder
+    - cmock_builder
+    - yaml_wrapper
+    - system_wrapper
+
+configurator_setup:
+  compose:
+    - configurator_builder
+    - configurator_validator
+    - configurator_plugins
+    - stream_wrapper
+
+configurator_plugins:
+  compose:
+    - stream_wrapper
+    - file_wrapper
+    - system_wrapper
+
+configurator_validator:
+  compose:
+    - file_wrapper
+    - stream_wrapper
+    - system_wrapper
+
+configurator_builder:
+  compose:
+    - file_system_utils
+    - file_wrapper
+    - system_wrapper
+
+loginator:
+  compose:
+    - configurator
+    - project_file_loader
+    - project_config_manager
+    - file_wrapper
+    - system_wrapper
+
+streaminator:
+  compose:
+    - streaminator_helper
+    - verbosinator
+    - loginator
+    - stream_wrapper
+
+streaminator_helper:
+
+setupinator:
+
+plugin_builder:
+
+plugin_manager:
+  compose:
+    - configurator
+    - plugin_manager_helper
+    - streaminator
+    - reportinator
+    - system_wrapper
+
+plugin_manager_helper:
+
+plugin_reportinator:
+  compose:
+    - plugin_reportinator_helper
+    - plugin_manager
+    - reportinator
+
+plugin_reportinator_helper:
+  compose:
+    - configurator
+    - streaminator
+    - yaml_wrapper
+    - file_wrapper
+
+verbosinator:
+  compose: configurator
+
+file_finder:
+  compose:
+    - configurator
+    - file_finder_helper
+    - cacheinator
+    - file_path_utils
+    - file_wrapper
+    - yaml_wrapper
+
+file_finder_helper:
+  compose: streaminator
+
+test_includes_extractor:
+  compose:
+    - configurator
+    - yaml_wrapper
+    - file_wrapper
+
+task_invoker:
+  compose:
+    - dependinator
+    - rake_utils
+    - rake_wrapper
+
+flaginator:
+  compose:
+    - configurator
+
+generator:
+  compose:
+    - configurator
+    - generator_helper
+    - preprocessinator
+    - cmock_builder
+    - generator_test_runner
+    - generator_test_results
+    - flaginator
+    - test_includes_extractor
+    - tool_executor
+    - file_finder
+    - file_path_utils
+    - streaminator
+    - plugin_manager
+    - file_wrapper
+
+generator_helper:
+  compose:
+    - streaminator
+
+generator_test_results:
+  compose:
+    - configurator  
+    - generator_test_results_sanity_checker
+    - yaml_wrapper  
+
+generator_test_results_sanity_checker:
+  compose:
+    - configurator  
+    - streaminator
+
+generator_test_runner:
+  compose:
+    - configurator
+    - file_path_utils
+    - file_wrapper
+
+dependinator:
+  compose:
+    - configurator
+    - project_config_manager
+    - test_includes_extractor
+    - file_path_utils        
+    - rake_wrapper
+    - file_wrapper
+
+preprocessinator:
+  compose:
+    - preprocessinator_helper          
+    - preprocessinator_includes_handler
+    - preprocessinator_file_handler
+    - task_invoker    
+    - file_path_utils
+    - yaml_wrapper
+
+preprocessinator_helper:
+  compose:                    
+    - configurator            
+    - test_includes_extractor 
+    - task_invoker            
+    - file_finder             
+    - file_path_utils         
+
+preprocessinator_includes_handler:
+  compose:
+    - configurator    
+    - tool_executor   
+    - task_invoker    
+    - file_path_utils 
+    - yaml_wrapper    
+    - file_wrapper    
+
+preprocessinator_file_handler:
+  compose:
+    - preprocessinator_extractor
+    - configurator   
+    - tool_executor  
+    - file_path_utils
+    - file_wrapper   
+
+preprocessinator_extractor:
+
+test_invoker:
+  compose:
+    - configurator
+    - test_invoker_helper
+    - plugin_manager
+    - streaminator
+    - preprocessinator
+    - task_invoker
+    - dependinator
+    - project_config_manager
+    - build_invoker_utils
+    - file_path_utils
+    - file_wrapper
+
+test_invoker_helper:
+  compose:
+    - configurator
+    - task_invoker
+    - test_includes_extractor
+    - file_finder
+    - file_path_utils
+    - file_wrapper
+
+release_invoker:
+  compose:
+    - configurator
+    - release_invoker_helper
+    - build_invoker_utils
+    - dependinator
+    - task_invoker
+    - file_path_utils
+    - file_wrapper
+
+release_invoker_helper:
+  compose:
+    - configurator
+    - dependinator
+    - task_invoker
+
+build_invoker_utils:
+  compose:
+    - configurator
+    - streaminator
+
+erb_wrapper:

+ 19 - 0
tests/vendor/ceedling/lib/par_map.rb

@@ -0,0 +1,19 @@
+
+
+def par_map(n, things, &block)
+  queue = Queue.new
+  things.each { |thing| queue << thing }
+  threads = (1..n).collect do
+    Thread.new do
+      begin
+        while true
+          yield queue.pop(true)
+        end
+      rescue ThreadError
+
+      end
+    end
+  end
+  threads.each { |t| t.join }
+end
+

+ 80 - 0
tests/vendor/ceedling/lib/plugin.rb

@@ -0,0 +1,80 @@
+
+class String
+  # reformat a multiline string to have given number of whitespace columns;
+  # helpful for formatting heredocs
+  def left_margin(margin=0)
+    non_whitespace_column = 0
+    new_lines = []
+    
+    # find first line with non-whitespace and count left columns of whitespace
+    self.each_line do |line|
+      if (line =~ /^\s*\S/)
+        non_whitespace_column = $&.length - 1
+        break
+      end
+    end
+    
+    # iterate through each line, chopping off leftmost whitespace columns and add back the desired whitespace margin
+    self.each_line do |line|
+      columns = []
+      margin.times{columns << ' '}
+      # handle special case of line being narrower than width to be lopped off
+      if (non_whitespace_column < line.length)
+        new_lines << "#{columns.join}#{line[non_whitespace_column..-1]}"
+      else
+        new_lines << "\n"
+      end
+    end
+    
+    return new_lines.join
+  end
+end
+
+class Plugin
+  attr_reader :name, :environment
+  attr_accessor :plugin_objects
+
+  def initialize(system_objects, name)
+    @environment = []
+    @ceedling = system_objects
+    @name = name
+    self.setup
+  end
+
+  def setup; end
+
+  # mock generation
+  def pre_mock_generate(arg_hash); end
+  def post_mock_generate(arg_hash); end
+
+  # test runner generation
+  def pre_runner_generate(arg_hash); end
+  def post_runner_generate(arg_hash); end
+
+  # compilation (test or source)
+  def pre_compile_execute(arg_hash); end
+  def post_compile_execute(arg_hash); end
+
+  # linking (test or source)
+  def pre_link_execute(arg_hash); end
+  def post_link_execute(arg_hash); end
+
+  # test fixture execution
+  def pre_test_fixture_execute(arg_hash); end
+  def post_test_fixture_execute(arg_hash); end
+
+  # test task
+  def pre_test; end
+  def post_test; end
+
+  # release task
+  def pre_release; end
+  def post_release; end
+
+  # whole shebang (any use of Ceedling)
+  def pre_build; end
+  def post_build; end
+  
+  def summary; end
+
+end

+ 53 - 0
tests/vendor/ceedling/lib/plugin_builder.rb

@@ -0,0 +1,53 @@
+require 'plugin'
+
+class PluginBuilder
+
+  attr_accessor :plugin_objects
+
+  def construct_plugin(plugin_name, object_map_yaml, system_objects)
+    # @streaminator.stdout_puts("Constructing plugin #{plugin_name}...", Verbosity::OBNOXIOUS)
+    object_map = {}
+    @plugin_objects = {}
+    @system_objects = system_objects
+
+    if object_map_yaml
+      @object_map = YAML.load(object_map_yaml)
+      @object_map.each_key do |obj|
+        construct_object(obj)
+      end
+    else
+      raise "Invalid object map for plugin #{plugin_name}!"
+    end
+
+    return @plugin_objects
+  end
+
+  private
+  
+  def camelize(underscored_name)
+    return underscored_name.gsub(/(_|^)([a-z0-9])/) {$2.upcase}
+  end
+
+  def construct_object(obj)
+    if @plugin_objects[obj].nil?
+      if @object_map[obj] && @object_map[obj]['compose']
+        @object_map[obj]['compose'].each do |dep|
+          construct_object(dep)
+        end
+      end
+      build_object(obj)
+    end
+  end
+
+  def build_object(new_object)
+    if @plugin_objects[new_object.to_sym].nil?
+      # @streaminator.stdout_puts("Building plugin object #{new_object}", Verbosity::OBNOXIOUS)
+      require new_object
+      class_name = camelize(new_object)
+      new_instance = eval("#{class_name}.new(@system_objects, class_name.to_s)")
+      new_instance.plugin_objects = @plugin_objects
+      @plugin_objects[new_object.to_sym] = new_instance
+    end
+  end
+
+end

+ 107 - 0
tests/vendor/ceedling/lib/plugin_manager.rb

@@ -0,0 +1,107 @@
+require 'constants'
+require 'set'
+
+class PluginManager
+
+  constructor :configurator, :plugin_manager_helper, :streaminator, :reportinator, :system_wrapper
+
+  def setup
+    @build_fail_registry = []
+    @plugin_objects = [] # so we can preserve order
+  end
+  
+  def load_plugin_scripts(script_plugins, system_objects)
+    environment = []
+    
+    script_plugins.each do |plugin|
+      # protect against instantiating object multiple times due to processing config multiple times (option files, etc)
+			next if (@plugin_manager_helper.include?(@plugin_objects, plugin))
+      begin
+        @system_wrapper.require_file( "#{plugin}.rb" )
+        object = @plugin_manager_helper.instantiate_plugin_script( camelize(plugin), system_objects, plugin )
+        @plugin_objects << object
+        environment += object.environment
+        
+        # add plugins to hash of all system objects
+        system_objects[plugin.downcase.to_sym] = object
+      rescue
+        puts "Exception raised while trying to load plugin: #{plugin}"
+        raise
+      end
+    end
+    
+    yield( { :environment => environment } ) if (environment.size > 0)
+  end
+  
+  def plugins_failed?
+    return (@build_fail_registry.size > 0)
+  end
+  
+  def print_plugin_failures
+    if (@build_fail_registry.size > 0)
+      report = @reportinator.generate_banner('BUILD FAILURE SUMMARY')
+      
+      @build_fail_registry.each do |failure|
+        report += "#{' - ' if (@build_fail_registry.size > 1)}#{failure}\n"
+      end
+      
+      report += "\n"
+      
+      @streaminator.stderr_puts(report, Verbosity::ERRORS)
+    end    
+  end
+  
+  def register_build_failure(message)
+    @build_fail_registry << message if (message and not message.empty?)
+  end
+
+  #### execute all plugin methods ####
+
+  def pre_mock_generate(arg_hash); execute_plugins(:pre_mock_generate, arg_hash); end
+  def post_mock_generate(arg_hash); execute_plugins(:post_mock_generate, arg_hash); end
+
+  def pre_runner_generate(arg_hash); execute_plugins(:pre_runner_generate, arg_hash); end
+  def post_runner_generate(arg_hash); execute_plugins(:post_runner_generate, arg_hash); end
+
+  def pre_compile_execute(arg_hash); execute_plugins(:pre_compile_execute, arg_hash); end
+  def post_compile_execute(arg_hash); execute_plugins(:post_compile_execute, arg_hash); end
+
+  def pre_link_execute(arg_hash); execute_plugins(:pre_link_execute, arg_hash); end
+  def post_link_execute(arg_hash); execute_plugins(:post_link_execute, arg_hash); end
+
+  def pre_test_fixture_execute(arg_hash); execute_plugins(:pre_test_fixture_execute, arg_hash); end
+  def post_test_fixture_execute(arg_hash)
+    # special arbitration: raw test results are printed or taken over by plugins handling the job
+    @streaminator.stdout_puts(arg_hash[:shell_result][:output]) if (@configurator.plugins_display_raw_test_results)
+    execute_plugins(:post_test_fixture_execute, arg_hash)
+  end
+
+  def pre_test; execute_plugins(:pre_test); end
+  def post_test; execute_plugins(:post_test); end
+
+  def pre_release; execute_plugins(:pre_release); end
+  def post_release; execute_plugins(:post_release); end
+  
+  def pre_build; execute_plugins(:pre_build); end
+  def post_build; execute_plugins(:post_build); end
+  
+  def summary; execute_plugins(:summary); end
+  
+  private ####################################
+  
+  def camelize(underscored_name)
+    return underscored_name.gsub(/(_|^)([a-z0-9])/) {$2.upcase}
+  end
+
+  def execute_plugins(method, *args)
+    @plugin_objects.each do |plugin|
+      begin
+        plugin.send(method, *args)
+      rescue
+        puts "Exception raised in plugin: #{plugin.name}, in method #{method}"
+        raise
+      end
+    end
+  end
+
+end

+ 19 - 0
tests/vendor/ceedling/lib/plugin_manager_helper.rb

@@ -0,0 +1,19 @@
+
+class PluginManagerHelper
+
+  def include?(plugins, name)
+		include = false
+		plugins.each do |plugin|
+			if (plugin.name == name)
+				include = true
+				break
+			end
+		end
+		return include
+  end
+
+  def instantiate_plugin_script(plugin, system_objects, name)
+    return eval("#{plugin}.new(system_objects, name)")
+  end
+
+end

+ 75 - 0
tests/vendor/ceedling/lib/plugin_reportinator.rb

@@ -0,0 +1,75 @@
+require 'constants'
+require 'defaults'
+
+class PluginReportinator
+  
+  constructor :plugin_reportinator_helper, :plugin_manager, :reportinator
+
+  def setup
+    @test_results_template = nil
+  end
+  
+  
+  def set_system_objects(system_objects)
+    @plugin_reportinator_helper.ceedling = system_objects
+  end
+  
+  
+  def fetch_results(results_path, test, options={:boom => false})
+    return @plugin_reportinator_helper.fetch_results( File.join(results_path, test), options )
+  end
+
+  
+  def generate_banner(message)
+    return @reportinator.generate_banner(message)
+  end
+
+  
+  def assemble_test_results(results_list, options={:boom => false})
+    aggregated_results = get_results_structure
+    
+    results_list.each do |result_path| 
+      results = @plugin_reportinator_helper.fetch_results( result_path, options )
+      @plugin_reportinator_helper.process_results(aggregated_results, results)
+    end
+
+    return aggregated_results
+  end
+  
+  
+  def register_test_results_template(template)
+    @test_results_template = template if (@test_results_template.nil?)
+  end
+  
+  
+  def run_test_results_report(hash, verbosity=Verbosity::NORMAL, &block)
+    run_report( $stdout,
+                ((@test_results_template.nil?) ? DEFAULT_TESTS_RESULTS_REPORT_TEMPLATE : @test_results_template),
+                hash,
+                verbosity,
+                &block )
+  end
+  
+  
+  def run_report(stream, template, hash=nil, verbosity=Verbosity::NORMAL)
+    failure = nil
+    failure = yield() if block_given?
+  
+    @plugin_manager.register_build_failure( failure )
+    
+    @plugin_reportinator_helper.run_report( stream, template, hash, verbosity )
+  end
+  
+  private ###############################
+  
+  def get_results_structure
+    return {
+      :successes => [],
+      :failures  => [],
+      :ignores   => [],
+      :stdout    => [],
+      :counts    => {:total => 0, :passed => 0, :failed => 0, :ignored  => 0, :stdout => 0}
+      }
+  end
+ 
+end

+ 52 - 0
tests/vendor/ceedling/lib/plugin_reportinator_helper.rb

@@ -0,0 +1,52 @@
+require 'constants'
+require 'erb'
+require 'rubygems'
+require 'rake' # for ext()
+
+
+class PluginReportinatorHelper
+  
+  attr_writer :ceedling
+  
+  constructor :configurator, :streaminator, :yaml_wrapper, :file_wrapper
+  
+  def fetch_results(results_path, options)
+    pass_path = File.join(results_path.ext( @configurator.extension_testpass ))
+    fail_path = File.join(results_path.ext( @configurator.extension_testfail ))
+
+    if (@file_wrapper.exist?(fail_path))
+      return @yaml_wrapper.load(fail_path)
+    elsif (@file_wrapper.exist?(pass_path))
+      return @yaml_wrapper.load(pass_path)
+    else
+      if (options[:boom])
+        @streaminator.stderr_puts("Could find no test results for '#{File.basename(results_path).ext(@configurator.extension_source)}'", Verbosity::ERRORS)
+        raise
+      end
+    end
+    
+    return {}
+  end
+
+
+  def process_results(aggregate_results, results)
+    return if (results.empty?)
+  
+    aggregate_results[:successes]        << { :source => results[:source].clone, :collection => results[:successes].clone } if (results[:successes].size > 0)
+    aggregate_results[:failures]         << { :source => results[:source].clone, :collection => results[:failures].clone  } if (results[:failures].size > 0)
+    aggregate_results[:ignores]          << { :source => results[:source].clone, :collection => results[:ignores].clone   } if (results[:ignores].size > 0)
+    aggregate_results[:stdout]           << { :source => results[:source].clone, :collection => results[:stdout].clone    } if (results[:stdout].size > 0)
+    aggregate_results[:counts][:total]   += results[:counts][:total]
+    aggregate_results[:counts][:passed]  += results[:counts][:passed]
+    aggregate_results[:counts][:failed]  += results[:counts][:failed]
+    aggregate_results[:counts][:ignored] += results[:counts][:ignored]
+    aggregate_results[:counts][:stdout]  += results[:stdout].size
+  end
+
+
+  def run_report(stream, template, hash, verbosity)
+    output = ERB.new(template, 0, "%<>")
+    @streaminator.stream_puts(stream, output.result(binding()), verbosity)
+  end
+  
+end

+ 43 - 0
tests/vendor/ceedling/lib/preprocessinator.rb

@@ -0,0 +1,43 @@
+
+class Preprocessinator
+
+  attr_reader :preprocess_file_proc
+  
+  constructor :preprocessinator_helper, :preprocessinator_includes_handler, :preprocessinator_file_handler, :task_invoker, :file_path_utils, :yaml_wrapper
+
+
+  def setup
+    # fashion ourselves callbacks @preprocessinator_helper can use
+    @preprocess_includes_proc = Proc.new { |filepath| self.preprocess_shallow_includes(filepath) }
+    @preprocess_file_proc     = Proc.new { |filepath| self.preprocess_file(filepath) }
+  end
+
+
+  def preprocess_test_and_invoke_test_mocks(test)
+    @preprocessinator_helper.preprocess_includes(test, @preprocess_includes_proc)
+
+    mocks_list = @preprocessinator_helper.assemble_mocks_list(test)
+
+    @preprocessinator_helper.preprocess_mockable_headers(mocks_list, @preprocess_file_proc)
+
+    @task_invoker.invoke_test_mocks(mocks_list)
+
+    @preprocessinator_helper.preprocess_test_file(test, @preprocess_file_proc)
+    
+    return mocks_list
+  end
+
+  def preprocess_shallow_includes(filepath)
+    dependencies_rule = @preprocessinator_includes_handler.form_shallow_dependencies_rule(filepath)
+    includes          = @preprocessinator_includes_handler.extract_shallow_includes(dependencies_rule)
+
+    @preprocessinator_includes_handler.write_shallow_includes_list(
+      @file_path_utils.form_preprocessed_includes_list_filepath(filepath), includes)
+  end
+
+  def preprocess_file(filepath)
+    @preprocessinator_includes_handler.invoke_shallow_includes_list(filepath)
+    @preprocessinator_file_handler.preprocess_file( filepath, @yaml_wrapper.load(@file_path_utils.form_preprocessed_includes_list_filepath(filepath)) )
+  end
+
+end

+ 30 - 0
tests/vendor/ceedling/lib/preprocessinator_extractor.rb

@@ -0,0 +1,30 @@
+class PreprocessinatorExtractor
+  def extract_base_file_from_preprocessed_expansion(filepath)
+    # preprocessing by way of toolchain preprocessor expands macros, eliminates
+    # comments, strips out #ifdef code, etc.  however, it also expands in place
+    # each #include'd file.  so, we must extract only the lines of the file
+    # that belong to the file originally preprocessed
+
+    # iterate through all lines and alternate between extract and ignore modes
+    # all lines between a '#'line containing file name of our filepath and the
+    # next '#'line should be extracted
+
+    base_name  = File.basename(filepath)
+    not_pragma = /^#(?!pragma\b)/ # preprocessor directive that's not a #pragma
+    pattern    = /^#.*(\s|\/|\\|\")#{Regexp.escape(base_name)}/
+    found_file = false # have we found the file we care about?
+
+    lines = []
+    File.readlines(filepath).each do |line|
+      if found_file and not line.match(not_pragma)
+        lines << line
+      else
+        found_file = false
+      end
+
+      found_file = true if line.match(pattern)
+    end
+
+    return lines
+  end
+end

+ 21 - 0
tests/vendor/ceedling/lib/preprocessinator_file_handler.rb

@@ -0,0 +1,21 @@
+
+
+class PreprocessinatorFileHandler
+  
+  constructor :preprocessinator_extractor, :configurator, :tool_executor, :file_path_utils, :file_wrapper
+
+  
+  def preprocess_file(filepath, includes)
+    preprocessed_filepath = @file_path_utils.form_preprocessed_file_filepath(filepath)
+        
+    command = @tool_executor.build_command_line(@configurator.tools_test_file_preprocessor, filepath, preprocessed_filepath)
+    @tool_executor.exec(command[:line], command[:options])
+    
+    contents = @preprocessinator_extractor.extract_base_file_from_preprocessed_expansion(preprocessed_filepath)
+
+    includes.each{|include| contents.unshift("#include \"#{include}\"")}
+
+    @file_wrapper.write(preprocessed_filepath, contents.join("\n"))    
+  end
+
+end

+ 46 - 0
tests/vendor/ceedling/lib/preprocessinator_helper.rb

@@ -0,0 +1,46 @@
+
+
+class PreprocessinatorHelper
+  
+  constructor :configurator, :test_includes_extractor, :task_invoker, :file_finder, :file_path_utils
+
+
+  def preprocess_includes(test, preprocess_includes_proc)
+    if (@configurator.project_use_test_preprocessor)
+      preprocessed_includes_list = @file_path_utils.form_preprocessed_includes_list_filepath(test)
+      preprocess_includes_proc.call( @file_finder.find_test_from_file_path(preprocessed_includes_list) )
+      @test_includes_extractor.parse_includes_list(preprocessed_includes_list)
+    else
+      @test_includes_extractor.parse_test_file(test)      
+    end  
+  end
+
+  def assemble_mocks_list(test)
+    return @file_path_utils.form_mocks_source_filelist( @test_includes_extractor.lookup_raw_mock_list(test) )
+  end
+
+  def preprocess_mockable_headers(mock_list, preprocess_file_proc)
+    if (@configurator.project_use_test_preprocessor)
+      preprocess_files_smartly(
+        @file_path_utils.form_preprocessed_mockable_headers_filelist(mock_list),
+        preprocess_file_proc ) { |file| @file_finder.find_header_file(file) }
+    end
+  end
+
+  def preprocess_test_file(test, preprocess_file_proc)
+    return if (!@configurator.project_use_test_preprocessor)
+    
+    preprocess_file_proc.call(test)
+  end
+  
+  private ############################
+
+  def preprocess_files_smartly(file_list, preprocess_file_proc)
+    if (@configurator.project_use_deep_dependencies)
+      @task_invoker.invoke_test_preprocessed_files(file_list)
+    else
+      file_list.each { |file| preprocess_file_proc.call( yield(file) ) }
+    end
+  end
+
+end

+ 55 - 0
tests/vendor/ceedling/lib/preprocessinator_includes_handler.rb

@@ -0,0 +1,55 @@
+
+
+class PreprocessinatorIncludesHandler
+  
+  constructor :configurator, :tool_executor, :task_invoker, :file_path_utils, :yaml_wrapper, :file_wrapper
+
+  # shallow includes: only those headers a source file explicitly includes
+
+  def invoke_shallow_includes_list(filepath)
+    @task_invoker.invoke_test_shallow_include_lists( [@file_path_utils.form_preprocessed_includes_list_filepath(filepath)] )
+  end
+
+  # ask the preprocessor for a make-style dependency rule of only the headers the source file immediately includes
+  def form_shallow_dependencies_rule(filepath)
+    # change filename (prefix of '_') to prevent preprocessor from finding include files in temp directory containing file it's scanning
+    temp_filepath = @file_path_utils.form_temp_path(filepath, '_')
+    
+    # read the file and replace all include statements with a decorated version
+    # (decorating the names creates file names that don't exist, thus preventing the preprocessor 
+    #  from snaking out and discovering the entire include path that winds through the code)
+    contents = @file_wrapper.read(filepath)
+    contents.gsub!( /#include\s+\"\s*(\S+)\s*\"/, "#include \"\\1\"\n#include \"@@@@\\1\"" )
+    @file_wrapper.write( temp_filepath, contents )
+    
+    # extract the make-style dependency rule telling the preprocessor to 
+    #  ignore the fact that it can't find the included files
+    command = @tool_executor.build_command_line(@configurator.tools_test_includes_preprocessor, temp_filepath)
+    shell_result = @tool_executor.exec(command[:line], command[:options])
+    
+    return shell_result[:output]
+  end
+  
+  # headers only; ignore any crazy .c includes
+  def extract_shallow_includes(make_rule)
+    list = []
+    header_extension = @configurator.extension_header
+
+    headers = make_rule.scan(/(\S+#{'\\'+header_extension})/).flatten # escape slashes before dot file extension
+    headers.uniq!
+    headers.map! { |header| header.sub(/(@@@@)|(.+\/)/, '') }
+    headers.sort!
+    
+    headers.each_with_index do |header, index|
+      break if (headers.size == (index-1))
+      list << header if (header == headers[index + 1])
+    end
+
+    return list
+  end
+  
+  def write_shallow_includes_list(filepath, list)
+    @yaml_wrapper.dump(filepath, list)
+  end
+
+end

+ 38 - 0
tests/vendor/ceedling/lib/project_config_manager.rb

@@ -0,0 +1,38 @@
+require 'constants'
+
+
+class ProjectConfigManager
+
+  attr_reader   :options_files, :release_config_changed, :test_config_changed
+  attr_accessor :config_hash
+
+  constructor :cacheinator, :yaml_wrapper
+
+
+  def setup
+    @options_files = []
+    @release_config_changed = false
+    @test_config_changed    = false
+  end
+
+
+  def merge_options(config_hash, option_filepath)
+    @options_files << File.basename( option_filepath )
+    config_hash.deep_merge( @yaml_wrapper.load( option_filepath ) )
+    return config_hash
+  end 
+  
+
+  
+  def process_release_config_change
+    # has project configuration changed since last release build
+    @release_config_changed = @cacheinator.diff_cached_release_config?( @config_hash )
+  end
+
+
+  def process_test_config_change
+    # has project configuration changed since last test build
+    @test_config_changed = @cacheinator.diff_cached_test_config?( @config_hash )
+  end
+
+end

+ 64 - 0
tests/vendor/ceedling/lib/project_file_loader.rb

@@ -0,0 +1,64 @@
+require 'constants'
+
+
+class ProjectFileLoader
+
+  attr_reader :main_file, :user_file
+
+  constructor :yaml_wrapper, :stream_wrapper, :system_wrapper, :file_wrapper
+
+  def setup
+    @main_file = nil
+    @user_file = nil
+    
+    @main_project_filepath = ''
+    @user_project_filepath = ''
+  end
+
+
+  def find_project_files
+    # first go hunting for optional user project file by looking for environment variable and then default location on disk
+    user_filepath = @system_wrapper.env_get('CEEDLING_USER_PROJECT_FILE')
+    
+    if ( not user_filepath.nil? and @file_wrapper.exist?(user_filepath) )
+      @user_project_filepath = user_filepath
+    elsif (@file_wrapper.exist?(DEFAULT_CEEDLING_USER_PROJECT_FILE))
+      @user_project_filepath = DEFAULT_CEEDLING_USER_PROJECT_FILE
+    end
+    
+    # next check for main project file by looking for environment variable and then default location on disk;
+    # blow up if we don't find this guy -- like, he's so totally important
+    main_filepath = @system_wrapper.env_get('CEEDLING_MAIN_PROJECT_FILE')
+    
+    if ( not main_filepath.nil? and @file_wrapper.exist?(main_filepath) )
+      @main_project_filepath = main_filepath
+    elsif (@file_wrapper.exist?(DEFAULT_CEEDLING_MAIN_PROJECT_FILE))
+      @main_project_filepath = DEFAULT_CEEDLING_MAIN_PROJECT_FILE
+    else
+      # no verbosity checking since this is lowest level reporting anyhow &
+      # verbosity checking depends on configurator which in turns needs this class (circular dependency)
+      @stream_wrapper.stderr_puts('Found no Ceedling project file (*.yml)')
+      raise
+    end
+    
+    @main_file = File.basename( @main_project_filepath )
+    @user_file = File.basename( @user_project_filepath ) if ( not @user_project_filepath.empty? )
+  end
+
+
+  def load_project_config
+    config_hash = {}
+    
+    # if there's no user project file, then just provide hash from project file
+    if (@user_project_filepath.empty?)
+      config_hash = @yaml_wrapper.load(@main_project_filepath)
+    # if there is a user project file, load it too and merge it on top of the project file,
+    # superseding anything that's common between them
+    else
+      config_hash = (@yaml_wrapper.load(@main_project_filepath)).merge(@yaml_wrapper.load(@user_project_filepath))
+    end
+    
+    return config_hash
+  end
+      
+end

+ 17 - 0
tests/vendor/ceedling/lib/rake_utils.rb

@@ -0,0 +1,17 @@
+
+class RakeUtils
+  
+  constructor :rake_wrapper
+
+  def task_invoked?(task_regex)
+    task_invoked = false
+    @rake_wrapper.task_list.each do |task|
+      if ((task.already_invoked) and (task.to_s =~ task_regex))
+        task_invoked = true
+        break
+      end
+    end
+    return task_invoked
+  end
+
+end

+ 33 - 0
tests/vendor/ceedling/lib/rake_wrapper.rb

@@ -0,0 +1,33 @@
+require 'rubygems'
+require 'rake'
+require 'makefile' # our replacement for rake's make-style dependency loader
+
+include Rake::DSL if defined?(Rake::DSL)
+
+class Rake::Task
+  attr_reader :already_invoked
+end
+
+class RakeWrapper
+
+  def initialize
+    @makefile_loader = Rake::MakefileLoader.new # use our custom replacement noted above
+  end
+
+  def [](task)
+    return Rake::Task[task]
+  end
+
+  def task_list
+    return Rake::Task.tasks
+  end
+
+  def create_file_task(file_task, dependencies)
+    file(file_task => dependencies)
+  end
+
+  def load_dependencies(dependencies_path)
+    @makefile_loader.load(dependencies_path)
+  end
+
+end

+ 74 - 0
tests/vendor/ceedling/lib/rakefile.rb

@@ -0,0 +1,74 @@
+require 'fileutils'
+
+# get directory containing this here file, back up one directory, and expand to full path
+CEEDLING_ROOT    = File.expand_path(File.dirname(__FILE__) + '/..')
+CEEDLING_LIB     = File.join(CEEDLING_ROOT, 'lib')
+CEEDLING_VENDOR  = File.join(CEEDLING_ROOT, 'vendor')
+CEEDLING_RELEASE = File.join(CEEDLING_ROOT, 'release')
+
+$LOAD_PATH.unshift( CEEDLING_LIB )
+$LOAD_PATH.unshift( File.join(CEEDLING_VENDOR, 'unity/auto') )
+$LOAD_PATH.unshift( File.join(CEEDLING_VENDOR, 'diy/lib') )
+$LOAD_PATH.unshift( File.join(CEEDLING_VENDOR, 'constructor/lib') )
+$LOAD_PATH.unshift( File.join(CEEDLING_VENDOR, 'cmock/lib') )
+$LOAD_PATH.unshift( File.join(CEEDLING_VENDOR, 'deep_merge/lib') )
+
+require 'rake'
+
+require 'diy'
+require 'constructor'
+
+require 'constants'
+require 'target_loader'
+
+
+# construct all our objects
+@ceedling = DIY::Context.from_yaml( File.read( File.join(CEEDLING_LIB, 'objects.yml') ) )
+@ceedling.build_everything
+
+# one-stop shopping for all our setup and such after construction
+@ceedling[:setupinator].ceedling = @ceedling
+
+project_config =
+  begin
+    cfg = @ceedling[:setupinator].load_project_files
+    TargetLoader.inspect(cfg, ENV['TARGET'])
+  rescue TargetLoader::NoTargets
+    cfg
+  rescue TargetLoader::RequestReload
+    @ceedling[:setupinator].load_project_files
+  end
+
+@ceedling[:setupinator].do_setup( project_config )
+
+
+# tell all our plugins we're about to do something
+@ceedling[:plugin_manager].pre_build
+
+# load rakefile component files (*.rake)
+PROJECT_RAKEFILE_COMPONENT_FILES.each { |component| load(component) }
+
+# tell rake to shut up by default (overridden in verbosity / debug tasks as appropriate)
+verbose(false)
+
+
+# end block always executed following rake run
+END {
+  # cache our input configurations to use in comparison upon next execution
+  @ceedling[:cacheinator].cache_test_config( @ceedling[:setupinator].config_hash )    if (@ceedling[:task_invoker].test_invoked?)
+  @ceedling[:cacheinator].cache_release_config( @ceedling[:setupinator].config_hash ) if (@ceedling[:task_invoker].release_invoked?)
+
+  # delete all temp files unless we're in debug mode
+  if (not @ceedling[:configurator].project_debug)
+    @ceedling[:file_wrapper].rm_f( @ceedling[:file_wrapper].directory_listing( File.join(@ceedling[:configurator].project_temp_path, '*') ))
+  end
+
+	# only perform these final steps if we got here without runtime exceptions or errors
+	if (@ceedling[:system_wrapper].ruby_success)
+
+    # tell all our plugins the build is done and process results
+	  @ceedling[:plugin_manager].post_build
+	  @ceedling[:plugin_manager].print_plugin_failures
+	  exit(1) if (@ceedling[:plugin_manager].plugins_failed?)
+	end
+}

+ 58 - 0
tests/vendor/ceedling/lib/release_invoker.rb

@@ -0,0 +1,58 @@
+require 'constants'
+
+
+class ReleaseInvoker
+
+  constructor :configurator, :release_invoker_helper, :build_invoker_utils, :dependinator, :task_invoker, :file_path_utils, :file_wrapper
+
+
+  def setup_and_invoke_c_objects( c_files )
+    objects = @file_path_utils.form_release_build_c_objects_filelist( c_files )
+
+    begin
+      @release_invoker_helper.process_deep_dependencies( @file_path_utils.form_release_dependencies_filelist( c_files ) )
+
+      @dependinator.enhance_release_file_dependencies( objects )
+      @task_invoker.invoke_release_objects( objects )
+    rescue => e
+      @build_invoker_utils.process_exception( e, RELEASE_SYM, false )
+    end
+
+    return objects
+  end
+
+
+  def setup_and_invoke_asm_objects( asm_files )
+    objects = @file_path_utils.form_release_build_asm_objects_filelist( asm_files )
+
+    begin
+      @dependinator.enhance_release_file_dependencies( objects )
+      @task_invoker.invoke_release_objects( objects )
+    rescue => e
+      @build_invoker_utils.process_exception( e, RELEASE_SYM, false )
+    end
+    
+    return objects
+  end
+
+
+  def refresh_c_deep_dependencies
+    return if (not @configurator.project_use_deep_dependencies)
+
+    @file_wrapper.rm_f( 
+      @file_wrapper.directory_listing( 
+        File.join( @configurator.project_release_dependencies_path, '*' + @configurator.extension_dependencies ) ) )
+
+    @release_invoker_helper.process_deep_dependencies( 
+      @file_path_utils.form_release_dependencies_filelist( 
+        @configurator.collection_all_source ) )    
+  end
+
+
+  def artifactinate( *files )
+    files.flatten.each do |file|
+      @file_wrapper.cp( file, @configurator.project_release_artifacts_path ) if @file_wrapper.exist?( file )
+    end
+  end
+
+end

+ 16 - 0
tests/vendor/ceedling/lib/release_invoker_helper.rb

@@ -0,0 +1,16 @@
+
+
+class ReleaseInvokerHelper
+
+  constructor :configurator, :dependinator, :task_invoker
+
+
+  def process_deep_dependencies(dependencies_list)
+    return if (not @configurator.project_use_deep_dependencies)
+
+    @dependinator.enhance_release_file_dependencies( dependencies_list )
+    @task_invoker.invoke_release_dependencies_files( dependencies_list )
+    @dependinator.load_release_object_deep_dependencies( dependencies_list )
+  end
+
+end

+ 9 - 0
tests/vendor/ceedling/lib/reportinator.rb

@@ -0,0 +1,9 @@
+
+class Reportinator
+
+  def generate_banner(message, width=nil)
+    dash_count = ((width.nil?) ? message.strip.length : width)
+    return "#{'-' * dash_count}\n#{message}\n#{'-' * dash_count}\n"
+  end
+
+end

+ 9 - 0
tests/vendor/ceedling/lib/rules_cmock.rake

@@ -0,0 +1,9 @@
+
+
+rule(/#{CMOCK_MOCK_PREFIX}.+#{'\\'+EXTENSION_SOURCE}$/ => [
+    proc do |task_name|
+      @ceedling[:file_finder].find_header_input_for_mock_file(task_name)
+    end  
+  ]) do |mock|
+  @ceedling[:generator].generate_mock(TEST_SYM, mock.source)
+end

+ 26 - 0
tests/vendor/ceedling/lib/rules_preprocess.rake

@@ -0,0 +1,26 @@
+
+
+# invocations against this rule should only happen when enhanced dependencies are enabled;
+# otherwise, dependency tracking will be too shallow and preprocessed files could intermittently
+#  fail to be updated when they actually need to be.
+rule(/#{PROJECT_TEST_PREPROCESS_FILES_PATH}\/.+/ => [
+    proc do |task_name|
+      @ceedling[:file_finder].find_test_or_source_or_header_file(task_name)
+    end  
+  ]) do |file|
+  if (not @ceedling[:configurator].project_use_deep_dependencies)
+    raise 'ERROR: Ceedling preprocessing rule invoked though neccessary auxiliary dependency support not enabled.'
+  end
+  @ceedling[:generator].generate_preprocessed_file(TEST_SYM, file.source)
+end
+
+
+# invocations against this rule can always happen as there are no deeper dependencies to consider
+rule(/#{PROJECT_TEST_PREPROCESS_INCLUDES_PATH}\/.+/ => [
+    proc do |task_name|
+      @ceedling[:file_finder].find_test_or_source_or_header_file(task_name)
+    end  
+  ]) do |file|
+  @ceedling[:generator].generate_shallow_includes_list(TEST_SYM, file.source)
+end
+

+ 79 - 0
tests/vendor/ceedling/lib/rules_release.rake

@@ -0,0 +1,79 @@
+
+RELEASE_COMPILE_TASK_ROOT  = RELEASE_TASK_ROOT + 'compile:'
+RELEASE_ASSEMBLE_TASK_ROOT = RELEASE_TASK_ROOT + 'assemble:'
+
+
+if (RELEASE_BUILD_USE_ASSEMBLY)
+rule(/#{PROJECT_RELEASE_BUILD_OUTPUT_ASM_PATH}\/#{'.+\\'+EXTENSION_OBJECT}$/ => [
+    proc do |task_name|
+      @ceedling[:file_finder].find_assembly_file(task_name)
+    end  
+  ]) do |object|
+  @ceedling[:generator].generate_object_file(
+    TOOLS_RELEASE_ASSEMBLER,
+    RELEASE_SYM,
+    object.source,
+    object.name )
+end
+end
+
+
+rule(/#{PROJECT_RELEASE_BUILD_OUTPUT_C_PATH}\/#{'.+\\'+EXTENSION_OBJECT}$/ => [
+    proc do |task_name|
+      @ceedling[:file_finder].find_compilation_input_file(task_name)
+    end  
+  ]) do |object|
+  @ceedling[:generator].generate_object_file(
+    TOOLS_RELEASE_COMPILER,
+    RELEASE_SYM,
+    object.source,
+    object.name,
+    @ceedling[:file_path_utils].form_release_build_c_list_filepath( object.name ) )
+end
+
+
+rule(/#{PROJECT_RELEASE_BUILD_TARGET}/) do |bin_file|
+  map_file = @ceedling[:configurator].project_release_build_map
+  @ceedling[:generator].generate_executable_file(
+    TOOLS_RELEASE_LINKER,
+    RELEASE_SYM,
+    bin_file.prerequisites,
+    bin_file.name,
+    map_file )
+  @ceedling[:release_invoker].artifactinate( bin_file.name, map_file, @ceedling[:configurator].release_build_artifacts )
+end
+
+
+namespace RELEASE_SYM do
+  # use rules to increase efficiency for large projects (instead of iterating through all sources and creating defined tasks)
+
+  namespace :compile do
+    rule(/^#{RELEASE_COMPILE_TASK_ROOT}\S+#{'\\'+EXTENSION_SOURCE}$/ => [ # compile task names by regex
+        proc do |task_name|
+          source = task_name.sub(/#{RELEASE_COMPILE_TASK_ROOT}/, '')
+          @ceedling[:file_finder].find_source_file(source, :error)
+        end
+    ]) do |compile|
+      @ceedling[:rake_wrapper][:directories].invoke
+      @ceedling[:project_config_manager].process_release_config_change
+      @ceedling[:release_invoker].setup_and_invoke_c_objects( [compile.source] )
+    end
+  end
+  
+  if (RELEASE_BUILD_USE_ASSEMBLY)
+  namespace :assemble do
+    rule(/^#{RELEASE_ASSEMBLE_TASK_ROOT}\S+#{'\\'+EXTENSION_ASSEMBLY}$/ => [ # assemble task names by regex
+        proc do |task_name|
+          source = task_name.sub(/#{RELEASE_ASSEMBLE_TASK_ROOT}/, '')
+          @ceedling[:file_finder].find_assembly_file(source)
+        end
+    ]) do |assemble|
+      @ceedling[:rake_wrapper][:directories].invoke
+      @ceedling[:project_config_manager].process_release_config_change
+      @ceedling[:release_invoker].setup_and_invoke_asm_objects( [assemble.source] )
+    end
+  end
+  end
+
+end
+

+ 15 - 0
tests/vendor/ceedling/lib/rules_release_deep_dependencies.rake

@@ -0,0 +1,15 @@
+
+
+rule(/#{PROJECT_RELEASE_DEPENDENCIES_PATH}\/#{'.+\\'+EXTENSION_DEPENDENCIES}$/ => [
+    proc do |task_name|
+      @ceedling[:file_finder].find_compilation_input_file(task_name)
+    end  
+  ]) do |dep|
+  @ceedling[:generator].generate_dependencies_file(
+  	TOOLS_RELEASE_DEPENDENCIES_GENERATOR,
+  	RELEASE_SYM,
+  	dep.source,
+  	@ceedling[:file_path_utils].form_release_build_c_object_filepath(dep.source),
+  	dep.name)
+end
+

+ 59 - 0
tests/vendor/ceedling/lib/rules_tests.rake

@@ -0,0 +1,59 @@
+
+
+rule(/#{PROJECT_TEST_FILE_PREFIX}#{'.+'+TEST_RUNNER_FILE_SUFFIX}#{'\\'+EXTENSION_SOURCE}$/ => [
+    proc do |task_name|
+      @ceedling[:file_finder].find_test_input_for_runner_file(task_name)
+    end
+  ]) do |runner|
+  @ceedling[:generator].generate_test_runner(TEST_SYM, runner.source, runner.name)
+end
+
+
+rule(/#{PROJECT_TEST_BUILD_OUTPUT_PATH}\/#{'.+\\'+EXTENSION_OBJECT}$/ => [
+    proc do |task_name|
+      @ceedling[:file_finder].find_compilation_input_file(task_name)
+    end
+  ]) do |object|
+  @ceedling[:generator].generate_object_file(
+    TOOLS_TEST_COMPILER,
+    TEST_SYM,
+    object.source,
+    object.name,
+    @ceedling[:file_path_utils].form_test_build_list_filepath( object.name ) )
+end
+
+
+rule(/#{PROJECT_TEST_BUILD_OUTPUT_PATH}\/#{'.+\\'+EXTENSION_EXECUTABLE}$/) do |bin_file|
+  @ceedling[:generator].generate_executable_file(
+    TOOLS_TEST_LINKER,
+    TEST_SYM,
+    bin_file.prerequisites,
+    bin_file.name,
+    @ceedling[:file_path_utils].form_test_build_map_filepath( bin_file.name ) )
+end
+
+
+rule(/#{PROJECT_TEST_RESULTS_PATH}\/#{'.+\\'+EXTENSION_TESTPASS}$/ => [
+    proc do |task_name|
+      @ceedling[:file_path_utils].form_test_executable_filepath(task_name)
+    end
+  ]) do |test_result|
+  @ceedling[:generator].generate_test_results(TOOLS_TEST_FIXTURE, TEST_SYM, test_result.source, test_result.name)
+end
+
+
+namespace TEST_SYM do
+  # use rules to increase efficiency for large projects (instead of iterating through all sources and creating defined tasks)
+  
+  rule(/^#{TEST_TASK_ROOT}\S+$/ => [ # test task names by regex
+      proc do |task_name|
+        test = task_name.sub(/#{TEST_TASK_ROOT}/, '')
+        test = "#{PROJECT_TEST_FILE_PREFIX}#{test}" if not (test.start_with?(PROJECT_TEST_FILE_PREFIX))
+        @ceedling[:file_finder].find_test_from_file_path(test)
+      end
+  ]) do |test|
+    @ceedling[:rake_wrapper][:directories].invoke
+    @ceedling[:test_invoker].setup_and_invoke([test.source])
+  end
+end
+

+ 15 - 0
tests/vendor/ceedling/lib/rules_tests_deep_dependencies.rake

@@ -0,0 +1,15 @@
+
+
+rule(/#{PROJECT_TEST_DEPENDENCIES_PATH}\/#{'.+\\'+EXTENSION_DEPENDENCIES}$/ => [
+    proc do |task_name|
+      @ceedling[:file_finder].find_compilation_input_file(task_name)
+    end  
+  ]) do |dep|
+  @ceedling[:generator].generate_dependencies_file(
+  	TOOLS_TEST_DEPENDENCIES_GENERATOR,
+    TEST_SYM,
+  	dep.source,
+  	@ceedling[:file_path_utils].form_test_build_object_filepath(dep.source),
+  	dep.name)
+end
+

+ 51 - 0
tests/vendor/ceedling/lib/setupinator.rb

@@ -0,0 +1,51 @@
+
+class Setupinator
+
+  attr_reader :config_hash
+  attr_writer :ceedling
+
+  def setup
+    @ceedling = {}
+    @config_hash = {}
+  end
+
+  def load_project_files
+    @ceedling[:project_file_loader].find_project_files
+    return @ceedling[:project_file_loader].load_project_config
+  end
+
+  def do_setup(config_hash)
+    @config_hash = config_hash
+
+    # load up all the constants and accessors our rake files, objects, & external scripts will need;
+    # note: configurator modifies the cmock section of the hash with a couple defaults to tie 
+    #       project together - the modified hash is used to build cmock object
+    @ceedling[:configurator].populate_defaults( config_hash )
+    @ceedling[:configurator].populate_cmock_defaults( config_hash )
+    @ceedling[:configurator].find_and_merge_plugins( config_hash )
+    @ceedling[:configurator].tools_setup( config_hash )
+    @ceedling[:configurator].eval_environment_variables( config_hash )
+    @ceedling[:configurator].eval_paths( config_hash )
+    @ceedling[:configurator].standardize_paths( config_hash )
+    @ceedling[:configurator].validate( config_hash )
+    @ceedling[:configurator].build( config_hash, :environment )
+    
+    @ceedling[:configurator].insert_rake_plugins( @ceedling[:configurator].rake_plugins )
+    @ceedling[:configurator].tools_supplement_arguments( config_hash )
+    
+    # merge in any environment variables plugins specify, after the main build
+    @ceedling[:plugin_manager].load_plugin_scripts( @ceedling[:configurator].script_plugins, @ceedling ) do |env|
+      @ceedling[:configurator].eval_environment_variables( env )
+      @ceedling[:configurator].build_supplement( config_hash, env )
+    end
+    
+    @ceedling[:plugin_reportinator].set_system_objects( @ceedling )
+    @ceedling[:file_finder].prepare_search_sources
+    @ceedling[:loginator].setup_log_filepath
+    @ceedling[:project_config_manager].config_hash = config_hash
+  end
+
+  def reset_defaults(config_hash)
+    @ceedling[:configurator].reset_defaults( config_hash )
+  end
+end

+ 20 - 0
tests/vendor/ceedling/lib/stream_wrapper.rb

@@ -0,0 +1,20 @@
+
+class StreamWrapper
+
+  def stdout_puts(string)
+    $stdout.puts(string)
+  end
+
+  def stdout_flush
+    $stdout.flush
+  end
+  
+  def stderr_puts(string)
+    $stderr.puts(string)
+  end
+
+  def stderr_flush
+    $stderr.flush
+  end
+
+end

+ 41 - 0
tests/vendor/ceedling/lib/streaminator.rb

@@ -0,0 +1,41 @@
+
+class Streaminator
+
+  require 'constants'
+
+  constructor :streaminator_helper, :verbosinator, :loginator, :stream_wrapper
+
+  # for those objects for whom the configurator has already been instantiated,
+  # Streaminator is a convenience object for handling verbosity and writing to the std streams
+
+  def stdout_puts(string, verbosity=Verbosity::NORMAL)
+    if (@verbosinator.should_output?(verbosity))
+      @stream_wrapper.stdout_puts(string)
+      @stream_wrapper.stdout_flush
+    end
+    
+    # write to log as though Verbosity::OBNOXIOUS
+    @loginator.log( string, @streaminator_helper.extract_name($stdout) )
+  end
+
+  def stderr_puts(string, verbosity=Verbosity::NORMAL)
+    if (@verbosinator.should_output?(verbosity))
+      @stream_wrapper.stderr_puts(string)
+      @stream_wrapper.stderr_flush
+    end
+
+    # write to log as though Verbosity::OBNOXIOUS
+    @loginator.log( string, @streaminator_helper.extract_name($stderr) )
+  end
+
+  def stream_puts(stream, string, verbosity=Verbosity::NORMAL)
+    if (@verbosinator.should_output?(verbosity))
+      stream.puts(string)
+      stream.flush
+    end
+
+    # write to log as though Verbosity::OBNOXIOUS
+    @loginator.log( string, @streaminator_helper.extract_name(stream) )
+  end
+
+end

+ 15 - 0
tests/vendor/ceedling/lib/streaminator_helper.rb

@@ -0,0 +1,15 @@
+
+class StreaminatorHelper
+
+  def extract_name(stream)
+    name = case (stream.fileno)
+      when 0 then '#<IO:$stdin>'
+      when 1 then '#<IO:$stdout>'
+      when 2 then '#<IO:$stderr>'
+      else stream.inspect
+    end
+    
+    return name
+  end
+
+end

+ 32 - 0
tests/vendor/ceedling/lib/system_utils.rb

@@ -0,0 +1,32 @@
+
+class Object
+  def deep_clone
+    Marshal::load(Marshal.dump(self))
+  end
+end
+
+
+class SystemUtils
+
+  constructor :system_wrapper
+
+  def setup
+    @tcsh_shell = nil
+  end
+
+  def tcsh_shell?
+    # once run a single time, return state determined at that execution
+    return @tcsh_shell if not @tcsh_shell.nil?
+  
+    result = @system_wrapper.shell_backticks('echo $version')
+
+    if ((result[:exit_code] == 0) and (result[:output].strip =~ /^tcsh/))
+      @tcsh_shell = true
+    else
+      @tcsh_shell = false
+    end
+  
+    return @tcsh_shell
+  end
+
+end

+ 76 - 0
tests/vendor/ceedling/lib/system_wrapper.rb

@@ -0,0 +1,76 @@
+require 'rbconfig'
+
+class SystemWrapper
+
+  # static method for use in defaults
+  def self.windows?
+    return ((RbConfig::CONFIG['host_os'] =~ /mswin|mingw/) ? true : false) if defined?(RbConfig)
+    return ((Config::CONFIG['host_os'] =~ /mswin|mingw/) ? true : false)
+  end
+
+  # class method so as to be mockable for tests
+  def windows?
+    return SystemWrapper.windows?
+  end
+  
+  def module_eval(string)
+    return Object.module_eval("\"" + string + "\"")
+  end
+
+  def eval(string)
+    return eval(string)
+  end
+
+  def search_paths
+    return ENV['PATH'].split(File::PATH_SEPARATOR)
+  end
+
+  def cmdline_args
+    return ARGV
+  end
+
+  def env_set(name, value)
+    ENV[name] = value
+  end
+  
+  def env_get(name)
+    return ENV[name]
+  end
+
+  def time_now
+    return Time.now.asctime
+  end
+
+  def shell_backticks(command)
+    return {
+      :output    => `#{command}`.freeze,
+      :exit_code => ($?.exitstatus).freeze
+    }
+  end
+
+  def shell_system(command)
+    system( command )
+    return {
+      :output    => ''.freeze,
+      :exit_code => ($?.exitstatus).freeze
+    }
+  end
+  
+  def add_load_path(path)
+    $LOAD_PATH.unshift(path)
+  end
+  
+  def require_file(path)
+    require(path)
+  end
+
+  def ruby_success
+    return ($!.nil? || $!.is_a?(SystemExit) && $!.success?)
+  end
+
+  def constants_include?(item)
+    # forcing to strings provides consistency across Ruby versions
+    return Object.constants.map{|constant| constant.to_s}.include?(item.to_s)
+  end
+  
+end

+ 38 - 0
tests/vendor/ceedling/lib/target_loader.rb

@@ -0,0 +1,38 @@
+module TargetLoader
+  class NoTargets    < Exception; end
+  class NoDirectory  < Exception; end
+  class NoDefault    < Exception; end
+  class NoSuchTarget < Exception; end
+
+  class RequestReload < Exception; end
+
+  def self.inspect(config, target_name=nil)
+    unless config[:targets]
+      raise NoTargets
+    end
+
+    targets = config[:targets]
+    unless targets[:targets_directory]
+      raise NoDirectory("No targets directory specified.")
+    end
+    unless targets[:default_target]
+      raise NoDefault("No default target specified.")
+    end
+
+    target_path = lambda {|name| File.join(targets[:targets_directory], name + ".yml")}
+
+    target = if target_name
+               target_path.call(target_name)
+             else
+               target_path.call(targets[:default_target])
+             end
+
+    unless File.exists? target
+      raise NoSuchTarget.new("No such target: #{target}")
+    end
+
+    ENV['CEEDLING_MAIN_PROJECT_FILE'] = target
+
+    raise RequestReload
+  end
+end

+ 89 - 0
tests/vendor/ceedling/lib/task_invoker.rb

@@ -0,0 +1,89 @@
+require "par_map"
+
+class TaskInvoker
+
+  constructor :dependinator, :rake_utils, :rake_wrapper
+
+  def setup
+    @test_regexs = [/^#{TEST_ROOT_NAME}:/]
+    @release_regexs = [/^#{RELEASE_ROOT_NAME}(:|$)/]
+  end
+  
+  def add_test_task_regex(regex)
+    @test_regexs << regex
+  end
+
+  def add_release_task_regex(regex)
+    @release_regexs << regex
+  end
+  
+  def test_invoked?
+    invoked = false
+    
+    @test_regexs.each do |regex|
+      invoked = true if (@rake_utils.task_invoked?(regex))
+      break if invoked
+    end
+    
+    return invoked
+  end
+  
+  def release_invoked?
+    invoked = false
+    
+    @release_regexs.each do |regex|
+      invoked = true if (@rake_utils.task_invoked?(regex))
+      break if invoked
+    end
+    
+    return invoked
+  end
+
+  def invoked?(regex)
+    return @rake_utils.task_invoked?(regex)
+  end
+
+  
+  def invoke_test_mocks(mocks)
+    @dependinator.enhance_mock_dependencies( mocks )
+    mocks.each { |mock| @rake_wrapper[mock].invoke }
+  end
+  
+  def invoke_test_runner(runner)
+    @dependinator.enhance_runner_dependencies( runner )
+    @rake_wrapper[runner].invoke
+  end
+
+  def invoke_test_shallow_include_lists(files)
+    @dependinator.enhance_shallow_include_lists_dependencies( files )
+    files.each { |file| @rake_wrapper[file].invoke }
+  end
+
+  def invoke_test_preprocessed_files(files)
+    @dependinator.enhance_preprocesed_file_dependencies( files )
+    files.each { |file| @rake_wrapper[file].invoke }
+  end
+
+  def invoke_test_dependencies_files(files)
+    @dependinator.enhance_dependencies_dependencies( files )
+    files.each { |file| @rake_wrapper[file].invoke }
+  end
+
+  def invoke_test_results(result)
+    @dependinator.enhance_results_dependencies( result )
+    @rake_wrapper[result].invoke
+  end
+
+  def invoke_release_dependencies_files(files)
+    par_map(PROJECT_COMPILE_THREADS, files) do |file|
+       @rake_wrapper[file].invoke
+    end
+  end
+  
+  def invoke_release_objects(objects)
+    par_map(PROJECT_COMPILE_THREADS, objects) do |object|
+       @rake_wrapper[object].invoke
+    end
+  end
+  
+end

+ 104 - 0
tests/vendor/ceedling/lib/tasks_base.rake

@@ -0,0 +1,104 @@
+require 'constants'
+require 'file_path_utils'
+
+
+desc "Display build environment version info."
+task :version do
+  tools = [
+      ['  Ceedling', CEEDLING_ROOT],
+      ['CException', File.join( CEEDLING_VENDOR, CEXCEPTION_ROOT_PATH)],
+      ['     CMock', File.join( CEEDLING_VENDOR, CMOCK_ROOT_PATH)],
+      ['     Unity', File.join( CEEDLING_VENDOR, UNITY_ROOT_PATH)],
+    ]
+  
+  tools.each do |tool|
+    name      = tool[0]
+    base_path = tool[1]
+    
+    version_string = @ceedling[:file_wrapper].read( File.join(base_path, 'release', 'version.info') ).strip
+    build_string   = @ceedling[:file_wrapper].read( File.join(base_path, 'release', 'build.info') ).strip
+    puts "#{name}:: #{version_string.empty? ? '#.#.' : (version_string + '.')}#{build_string.empty? ? '?' : build_string}"
+  end
+end
+
+
+desc "Set verbose output (silent:[#{Verbosity::SILENT}] - obnoxious:[#{Verbosity::OBNOXIOUS}])."
+task :verbosity, :level do |t, args|
+  verbosity_level = args.level.to_i
+  
+  if (PROJECT_USE_MOCKS)
+    # don't store verbosity level in setupinator's config hash, use a copy;
+    # otherwise, the input configuration will change and trigger entire project rebuilds
+    hash = @ceedling[:setupinator].config_hash[:cmock].clone
+    hash[:verbosity] = verbosity_level
+
+    @ceedling[:cmock_builder].manufacture( hash )  
+  end
+
+  @ceedling[:configurator].project_verbosity = verbosity_level
+
+  # control rake's verbosity with new setting
+  verbose( ((verbosity_level >= Verbosity::OBNOXIOUS) ? true : false) )
+end
+
+
+desc "Enable logging"
+task :logging do
+  @ceedling[:configurator].project_logging = true
+end
+
+
+# non advertised debug task
+task :debug do
+  Rake::Task[:verbosity].invoke(Verbosity::DEBUG)
+  Rake.application.options.trace = true
+  @ceedling[:configurator].project_debug = true
+end
+
+
+# non advertised sanity checking task
+task :sanity_checks, :level do |t, args|
+  check_level = args.level.to_i
+  @ceedling[:configurator].sanity_checks = check_level
+end
+
+
+# list expanded environment variables
+if (not ENVIRONMENT.empty?)
+desc "List all configured environment variables."
+task :environment do
+  ENVIRONMENT.each do |env|
+    env.each_key do |key|
+      name = key.to_s.upcase
+      puts " - #{name}: \"#{env[key]}\""
+    end
+  end  
+end
+end
+
+
+namespace :options do
+
+  COLLECTION_PROJECT_OPTIONS.each do |option_path|
+    option = File.basename(option_path, '.yml')
+
+    desc "Merge #{option} project options."
+    task option.downcase.to_sym do
+      # @ceedling[:setupinator].reset_defaults( @ceedling[:setupinator].config_hash )
+      hash = @ceedling[:project_config_manager].merge_options( @ceedling[:setupinator].config_hash, option_path )
+      @ceedling[:setupinator].do_setup( hash )
+    end
+  end
+
+end
+
+
+# do not present task if there's no plugins
+if (not PLUGINS_ENABLED.empty?)
+desc "Execute plugin result summaries (no build triggering)."
+task :summary do
+	@ceedling[:plugin_manager].summary
+  puts "\nNOTE: Summaries may be out of date with project sources.\n\n"
+end
+end
+

+ 91 - 0
tests/vendor/ceedling/lib/tasks_filesystem.rake

@@ -0,0 +1,91 @@
+
+# rather than require 'rake/clean' & try to override, we replicate for finer control
+CLEAN   = Rake::FileList["**/*~", "**/*.bak"]
+CLOBBER = Rake::FileList.new
+
+CLEAN.clear_exclude.exclude { |fn| fn.pathmap("%f") == 'core' && File.directory?(fn) }
+
+CLEAN.include(File.join(PROJECT_TEST_BUILD_OUTPUT_PATH, '*'))
+CLEAN.include(File.join(PROJECT_TEST_RESULTS_PATH, '*'))
+CLEAN.include(File.join(PROJECT_TEST_DEPENDENCIES_PATH, '*'))
+CLEAN.include(File.join(PROJECT_BUILD_RELEASE_ROOT, '*.*'))
+CLEAN.include(File.join(PROJECT_RELEASE_BUILD_OUTPUT_PATH, '*'))
+CLEAN.include(File.join(PROJECT_RELEASE_DEPENDENCIES_PATH, '*'))
+
+CLOBBER.include(File.join(PROJECT_BUILD_ARTIFACTS_ROOT, '**/*'))
+CLOBBER.include(File.join(PROJECT_BUILD_TESTS_ROOT, '**/*'))
+CLOBBER.include(File.join(PROJECT_BUILD_RELEASE_ROOT, '**/*'))
+CLOBBER.include(File.join(PROJECT_LOG_PATH, '**/*'))
+CLOBBER.include(File.join(PROJECT_TEMP_PATH, '**/*'))
+
+# because of cmock config, mock path can optionally exist apart from standard test build paths
+CLOBBER.include(File.join(CMOCK_MOCK_PATH, '*'))
+
+REMOVE_FILE_PROC = Proc.new { |fn| rm_r fn rescue nil }
+
+# redefine clean so we can override how it advertises itself
+desc "Delete all build artifacts and temporary products."
+task(:clean) do
+  # because :clean is a prerequisite for :clobber, intelligently display the progress message
+  if (not @ceedling[:task_invoker].invoked?(/^clobber$/))
+    @ceedling[:streaminator].stdout_puts("\nCleaning build artifacts...\n(For large projects, this task may take a long time to complete)\n\n")
+  end
+  CLEAN.each { |fn| REMOVE_FILE_PROC.call(fn) }
+end
+
+# redefine clobber so we can override how it advertises itself
+desc "Delete all generated files (and build artifacts)."
+task(:clobber => [:clean]) do
+  @ceedling[:streaminator].stdout_puts("\nClobbering all generated files...\n(For large projects, this task may take a long time to complete)\n\n")
+  CLOBBER.each { |fn| REMOVE_FILE_PROC.call(fn) }
+end
+
+
+PROJECT_BUILD_PATHS.each { |path| directory(path) }
+
+# create directories that hold build output and generated files & touching rebuild dependency sources
+task(:directories => PROJECT_BUILD_PATHS) { @ceedling[:dependinator].touch_force_rebuild_files }
+
+
+# list paths discovered at load time
+namespace :paths do
+  
+  paths = @ceedling[:setupinator].config_hash[:paths]
+  paths.each_key do |section|
+    name = section.to_s.downcase
+    path_list = Object.const_get("COLLECTION_PATHS_#{name.upcase}")
+    
+    if (path_list.size != 0)
+      desc "List all collected #{name} paths."
+      task(name.to_sym) { puts "#{name} paths:"; path_list.sort.each {|path| puts " - #{path}" } }
+    end
+  end
+  
+end
+
+
+# list files & file counts discovered at load time
+namespace :files do
+  
+  categories = [
+    ['test',   COLLECTION_ALL_TESTS],
+    ['source', COLLECTION_ALL_SOURCE],
+    ['header', COLLECTION_ALL_HEADERS]
+    ]
+  categories << ['assembly', COLLECTION_ALL_ASSEMBLY] if (RELEASE_BUILD_USE_ASSEMBLY)
+  
+  categories.each do |category|
+    name       = category[0]
+    collection = category[1]
+    
+    desc "List all collected #{name} files."
+    task(name.to_sym) do
+      puts "#{name} files:"
+      collection.sort.each { |filepath| puts " - #{filepath}" }
+      puts "file count: #{collection.size}"
+    end
+  end
+  
+end
+
+

+ 28 - 0
tests/vendor/ceedling/lib/tasks_release.rake

@@ -0,0 +1,28 @@
+require 'constants'
+require 'file_path_utils'
+
+
+desc "Build release target."
+task RELEASE_SYM => [:directories] do
+  header = "Release build '#{File.basename(PROJECT_RELEASE_BUILD_TARGET)}'"
+  @ceedling[:streaminator].stdout_puts("\n\n#{header}\n#{'-' * header.length}")  
+  
+  begin
+    @ceedling[:plugin_manager].pre_release
+
+    core_objects  = []
+    extra_objects = @ceedling[:file_path_utils].form_release_build_c_objects_filelist( COLLECTION_RELEASE_ARTIFACT_EXTRA_LINK_OBJECTS )
+
+    @ceedling[:project_config_manager].process_release_config_change
+    core_objects.concat( @ceedling[:release_invoker].setup_and_invoke_c_objects( COLLECTION_ALL_SOURCE ) )
+  
+    # if assembler use isn't enabled, COLLECTION_ALL_ASSEMBLY is empty array & nothing happens
+    core_objects.concat( @ceedling[:release_invoker].setup_and_invoke_asm_objects( COLLECTION_ALL_ASSEMBLY ) )
+  
+    file( PROJECT_RELEASE_BUILD_TARGET => (core_objects + extra_objects) )
+    Rake::Task[PROJECT_RELEASE_BUILD_TARGET].invoke
+  ensure
+    @ceedling[:plugin_manager].post_release  
+  end
+end
+

+ 9 - 0
tests/vendor/ceedling/lib/tasks_release_deep_dependencies.rake

@@ -0,0 +1,9 @@
+require 'constants'
+
+namespace REFRESH_SYM do
+
+  task RELEASE_SYM do
+    @ceedling[:release_invoker].refresh_c_deep_dependencies
+  end
+
+end

+ 52 - 0
tests/vendor/ceedling/lib/tasks_tests.rake

@@ -0,0 +1,52 @@
+require 'constants'
+
+
+namespace TEST_SYM do
+  
+  desc "Run all unit tests."
+  task :all => [:directories] do
+    @ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS)
+  end
+
+  desc "Run single test ([*] real test or source file name, no path)."
+  task :* do
+    message = "\nOops! '#{TEST_ROOT_NAME}:*' isn't a real task. " +
+              "Use a real test or source file name (no path) in place of the wildcard.\n" +
+              "Example: rake #{TEST_ROOT_NAME}:foo.c\n\n"
+  
+    @ceedling[:streaminator].stdout_puts( message )
+  end
+  
+  desc "Run tests for changed files."
+  task :delta => [:directories] do
+    @ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS, TEST_SYM, {:force_run => false})
+  end
+  
+  desc "Run tests by matching regular expression pattern."
+  task :pattern, [:regex] => [:directories] do |t, args|
+    matches = []
+    
+    COLLECTION_ALL_TESTS.each { |test| matches << test if (test =~ /#{args.regex}/) }
+  
+    if (matches.size > 0)
+      @ceedling[:test_invoker].setup_and_invoke(matches, TEST_SYM, {:force_run => false})
+    else
+      @ceedling[:streaminator].stdout_puts("\nFound no tests matching pattern /#{args.regex}/.")
+    end
+  end
+
+  desc "Run tests whose test path contains [dir] or [dir] substring."
+  task :path, [:dir] => [:directories] do |t, args|
+    matches = []
+    
+    COLLECTION_ALL_TESTS.each { |test| matches << test if File.dirname(test).include?(args.dir.gsub(/\\/, '/')) }
+  
+    if (matches.size > 0)
+      @ceedling[:test_invoker].setup_and_invoke(matches, TEST_SYM, {:force_run => false})
+    else
+      @ceedling[:streaminator].stdout_puts("\nFound no tests including the given path or path component.")
+    end
+  end
+
+end
+

+ 9 - 0
tests/vendor/ceedling/lib/tasks_tests_deep_dependencies.rake

@@ -0,0 +1,9 @@
+require 'constants'
+
+namespace REFRESH_SYM do
+
+  task TEST_SYM do
+    @ceedling[:test_invoker].refresh_deep_dependencies
+  end
+
+end

+ 36 - 0
tests/vendor/ceedling/lib/tasks_vendor.rake

@@ -0,0 +1,36 @@
+require 'constants'
+require 'file_path_utils'
+
+# create file dependencies to ensure C-based components of vendor tools are recompiled when they are updated with new versions
+# forming these explicitly rather than depend on auxiliary dependencies so all scenarios are explicitly covered
+
+file( @ceedling[:file_path_utils].form_test_build_object_filepath( UNITY_C_FILE ) => [
+  FilePathUtils.form_ceedling_vendor_path( UNITY_LIB_PATH, UNITY_C_FILE ),
+  FilePathUtils.form_ceedling_vendor_path( UNITY_LIB_PATH, UNITY_H_FILE ),
+  FilePathUtils.form_ceedling_vendor_path( UNITY_LIB_PATH, UNITY_INTERNALS_H_FILE ) ]
+  )
+
+
+if (PROJECT_USE_MOCKS)
+file( @ceedling[:file_path_utils].form_test_build_object_filepath( CMOCK_C_FILE ) => [
+  FilePathUtils.form_ceedling_vendor_path( CMOCK_LIB_PATH, CMOCK_C_FILE ),
+  FilePathUtils.form_ceedling_vendor_path( CMOCK_LIB_PATH, CMOCK_H_FILE ) ]
+  )
+end
+
+
+if (PROJECT_USE_EXCEPTIONS)
+file( @ceedling[:file_path_utils].form_test_build_object_filepath( CEXCEPTION_C_FILE ) => [
+  FilePathUtils.form_ceedling_vendor_path( CEXCEPTION_LIB_PATH, CEXCEPTION_C_FILE ),
+  FilePathUtils.form_ceedling_vendor_path( CEXCEPTION_LIB_PATH, CEXCEPTION_H_FILE ) ]
+  )
+end
+
+
+if (PROJECT_USE_EXCEPTIONS and PROJECT_RELEASE_BUILD)
+file( @ceedling[:file_path_utils].form_release_build_c_object_filepath( CEXCEPTION_C_FILE ) => [
+  FilePathUtils.form_ceedling_vendor_path( CEXCEPTION_LIB_PATH, CEXCEPTION_C_FILE ),
+  FilePathUtils.form_ceedling_vendor_path( CEXCEPTION_LIB_PATH, CEXCEPTION_H_FILE ) ]
+  )
+end
+

+ 81 - 0
tests/vendor/ceedling/lib/test_includes_extractor.rb

@@ -0,0 +1,81 @@
+
+class TestIncludesExtractor
+
+  constructor :configurator, :yaml_wrapper, :file_wrapper
+
+
+  def setup
+    @includes  = {}
+    @mocks     = {}
+  end
+
+
+  # for includes_list file, slurp up array from yaml file and sort & store includes
+  def parse_includes_list(includes_list)
+    gather_and_store_includes( includes_list, @yaml_wrapper.load(includes_list) )
+  end
+
+  # open, scan for, and sort & store includes of test file
+  def parse_test_file(test)
+    gather_and_store_includes( test, extract_from_file(test) )
+  end
+
+  # mocks with no file extension
+  def lookup_raw_mock_list(test)
+    file_key = form_file_key(test)
+    return [] if @mocks[file_key].nil?
+    return @mocks[file_key]
+  end
+  
+  # includes with file extension
+  def lookup_includes_list(file)
+    file_key = form_file_key(file)
+    return [] if (@includes[file_key]).nil?
+    return @includes[file_key]
+  end
+  
+  private #################################
+  
+  def form_file_key(filepath)
+    return File.basename(filepath).to_sym
+  end
+
+  def extract_from_file(file)
+    includes = []
+    header_extension = @configurator.extension_header
+    
+    contents = @file_wrapper.read(file)
+
+    # remove line comments
+    contents = contents.gsub(/\/\/.*$/, '')
+    # remove block comments
+    contents = contents.gsub(/\/\*.*?\*\//m, '')
+    
+    contents.split("\n").each do |line|
+      # look for include statement
+      scan_results = line.scan(/#include\s+\"\s*(.+#{'\\'+header_extension})\s*\"/)
+      
+      includes << scan_results[0][0] if (scan_results.size > 0)
+    end
+    
+    return includes.uniq
+  end
+
+  def gather_and_store_includes(file, includes)
+    mock_prefix      = @configurator.cmock_mock_prefix
+    header_extension = @configurator.extension_header
+    file_key         = form_file_key(file)
+    @mocks[file_key] = []
+      
+    # add includes to lookup hash
+    @includes[file_key] = includes
+      
+    includes.each do |include_file|          
+      # check if include is a mock
+      scan_results = include_file.scan(/(#{mock_prefix}.+)#{'\\'+header_extension}/)
+      # add mock to lookup hash
+      @mocks[file_key] << scan_results[0][0] if (scan_results.size > 0)
+    end
+  end
+  
+end

+ 97 - 0
tests/vendor/ceedling/lib/test_invoker.rb

@@ -0,0 +1,97 @@
+require 'constants'
+
+
+class TestInvoker
+
+  attr_reader :sources, :tests, :mocks
+
+  constructor :configurator,
+              :test_invoker_helper,
+              :plugin_manager,
+              :streaminator,
+              :preprocessinator,
+              :task_invoker,
+              :dependinator,
+              :project_config_manager,
+              :build_invoker_utils,
+              :file_path_utils,
+              :file_wrapper
+
+  def setup
+    @sources = []
+    @tests   = []
+    @mocks   = []
+  end
+  
+  def setup_and_invoke(tests, context=TEST_SYM, options={:force_run => true})
+  
+    @tests = tests
+
+    @project_config_manager.process_test_config_change
+  
+    @tests.each do |test|
+      # announce beginning of test run
+      header = "Test '#{File.basename(test)}'"
+      @streaminator.stdout_puts("\n\n#{header}\n#{'-' * header.length}")
+
+      begin
+        @plugin_manager.pre_test
+        
+        # collect up test fixture pieces & parts
+        runner       = @file_path_utils.form_runner_filepath_from_test( test )
+        mock_list    = @preprocessinator.preprocess_test_and_invoke_test_mocks( test )
+        sources      = @test_invoker_helper.extract_sources( test )
+        extras       = @configurator.collection_test_fixture_extra_link_objects
+        core         = [test] + mock_list + sources
+        objects      = @file_path_utils.form_test_build_objects_filelist( [runner] + core + extras )
+        results_pass = @file_path_utils.form_pass_results_filepath( test )
+        results_fail = @file_path_utils.form_fail_results_filepath( test )
+        
+        # clean results files so we have a missing file with which to kick off rake's dependency rules
+        @test_invoker_helper.clean_results( {:pass => results_pass, :fail => results_fail}, options )
+
+        # load up auxiliary dependencies so deep changes cause rebuilding appropriately
+        @test_invoker_helper.process_deep_dependencies( core ) do |dependencies_list| 
+          @dependinator.load_test_object_deep_dependencies( dependencies_list )
+        end
+
+        # tell rake to create test runner if needed
+        @task_invoker.invoke_test_runner( runner )
+
+        # enhance object file dependencies to capture externalities influencing regeneration
+        @dependinator.enhance_test_build_object_dependencies( objects )
+
+        # associate object files with executable
+        @dependinator.setup_test_executable_dependencies( test, objects )
+
+        # 3, 2, 1... launch
+        @task_invoker.invoke_test_results( results_pass )        
+      rescue => e
+        @build_invoker_utils.process_exception( e, context )
+      ensure
+        @plugin_manager.post_test        
+      end
+      
+      # store away what's been processed
+      @mocks.concat( mock_list )
+      @sources.concat( sources )
+    end
+
+    # post-process collected mock list
+    @mocks.uniq!
+    
+    # post-process collected sources list
+    @sources.uniq!
+  end
+
+
+  def refresh_deep_dependencies
+    @file_wrapper.rm_f( 
+      @file_wrapper.directory_listing( 
+        File.join( @configurator.project_test_dependencies_path, '*' + @configurator.extension_dependencies ) ) )
+
+    @test_invoker_helper.process_deep_dependencies( 
+      @configurator.collection_all_tests + @configurator.collection_all_source )
+  end
+
+end

+ 28 - 0
tests/vendor/ceedling/lib/test_invoker_helper.rb

@@ -0,0 +1,28 @@
+
+class TestInvokerHelper
+
+  constructor :configurator, :task_invoker, :test_includes_extractor, :file_finder, :file_path_utils, :file_wrapper
+
+  def clean_results(results, options)
+    @file_wrapper.rm_f( results[:fail] )
+    @file_wrapper.rm_f( results[:pass] ) if (options[:force_run])
+  end
+
+  def process_deep_dependencies(files)
+    return if (not @configurator.project_use_deep_dependencies)
+
+    dependencies_list = @file_path_utils.form_test_dependencies_filelist( files )
+    @task_invoker.invoke_test_dependencies_files( dependencies_list )
+    yield( dependencies_list ) if block_given?
+  end
+  
+  def extract_sources(test)
+    sources  = []
+    includes = @test_includes_extractor.lookup_includes_list(test)
+    
+    includes.each { |include| sources << @file_finder.find_compilation_input_file(include, :ignore) }
+    
+    return sources.compact
+  end
+  
+end

+ 212 - 0
tests/vendor/ceedling/lib/tool_executor.rb

@@ -0,0 +1,212 @@
+require 'constants'
+
+class ShellExecutionException < RuntimeError
+  attr_reader :shell_result
+  def initialize(shell_result)
+    @shell_result = shell_result
+  end
+end
+
+class ToolExecutor
+
+  constructor :configurator, :tool_executor_helper, :streaminator, :system_wrapper
+
+  def setup
+    @tool_name  = ''
+    @executable = ''
+  end
+
+  # build up a command line from yaml provided config
+  def build_command_line(tool_config, *args)
+    @tool_name  = tool_config[:name]
+    @executable = tool_config[:executable]
+
+    command = {}
+
+    # basic premise is to iterate top to bottom through arguments using '$' as 
+    #  a string replacement indicator to expand globals or inline yaml arrays
+    #  into command line arguments via substitution strings
+    command[:line] = [
+      @tool_executor_helper.osify_path_separators( expandify_element(@executable, *args) ),
+      build_arguments(tool_config[:arguments], *args),
+      ].join(' ').strip
+
+    command[:options] = {
+      :stderr_redirect => @tool_executor_helper.stderr_redirection(tool_config, @configurator.project_logging),
+      :background_exec => tool_config[:background_exec]
+      }
+    
+    return command
+  end
+
+
+  # shell out, execute command, and return response
+  def exec(command, options={}, args=[])
+    options[:boom] = true if (options[:boom].nil?)
+    options[:stderr_redirect] = StdErrRedirect::NONE if (options[:stderr_redirect].nil?)
+    options[:background_exec] = BackgroundExec::NONE if (options[:background_exec].nil?)
+
+    # build command line
+    command_line = [
+      @tool_executor_helper.background_exec_cmdline_prepend( options ),
+      command.strip,
+      args,
+      @tool_executor_helper.stderr_redirect_cmdline_append( options ),
+      @tool_executor_helper.background_exec_cmdline_append( options ),
+      ].flatten.compact.join(' ')
+
+    shell_result = {}
+    
+    # depending on background exec option, we shell out differently
+    if (options[:background_exec] != BackgroundExec::NONE)
+      shell_result = @system_wrapper.shell_system( command_line )
+    else
+      shell_result = @system_wrapper.shell_backticks( command_line )
+    end
+    
+    @tool_executor_helper.print_happy_results( command_line, shell_result, options[:boom] )
+    @tool_executor_helper.print_error_results( command_line, shell_result, options[:boom] )
+    
+    # go boom if exit code isn't 0 (but in some cases we don't want a non-0 exit code to raise)
+    raise ShellExecutionException.new(shell_result) if ((shell_result[:exit_code] != 0) and options[:boom])
+    
+    return shell_result
+  end
+
+  
+  private #############################
+
+  
+  def build_arguments(config, *args)
+    build_string = ''
+    
+    return nil if (config.nil?)
+    
+    # iterate through each argument
+
+    # the yaml blob array needs to be flattened so that yaml substitution
+    # is handled correctly, since it creates a nested array when an anchor is
+    # dereferenced
+    config.flatten.each do |element|
+      argument = ''
+      
+      case(element)
+        # if we find a simple string then look for string replacement operators
+        #  and expand with the parameters in this method's argument list
+        when String then argument = expandify_element(element, *args)
+        # if we find a hash, then we grab the key as a substitution string and expand the
+        #  hash's value(s) within that substitution string
+        when Hash   then argument = dehashify_argument_elements(element)
+      end
+
+      build_string.concat("#{argument} ") if (argument.length > 0)
+    end
+    
+    build_string.strip!
+    return build_string if (build_string.length > 0)
+    return nil
+  end
+
+
+  # handle simple text string argument & argument array string replacement operators
+  def expandify_element(element, *args)
+    match = //
+    to_process = nil
+    args_index = 0
+
+    # handle ${#} input replacement
+    if (element =~ TOOL_EXECUTOR_ARGUMENT_REPLACEMENT_PATTERN)
+      args_index = ($2.to_i - 1)
+
+      if (args.nil? or args[args_index].nil?)
+        @streaminator.stderr_puts("ERROR: Tool '#{@tool_name}' expected valid argument data to accompany replacement operator #{$1}.", Verbosity::ERRORS)
+        raise
+      end
+
+      match = /#{Regexp.escape($1)}/
+      to_process = args[args_index]
+    end
+      
+    # simple string argument: replace escaped '\$' and strip
+    element.sub!(/\\\$/, '$')
+    element.strip!
+
+    # handle inline ruby execution
+    if (element =~ RUBY_EVAL_REPLACEMENT_PATTERN)
+      element.replace(eval($1))
+    end
+
+    build_string = ''
+
+    # handle array or anything else passed into method to be expanded in place of replacement operators
+    case (to_process)
+      when Array then to_process.each {|value| build_string.concat( "#{element.sub(match, value.to_s)} " ) } if (to_process.size > 0)
+      else build_string.concat( element.sub(match, to_process.to_s) )
+    end
+
+    # handle inline ruby string substitution
+    if (build_string =~ RUBY_STRING_REPLACEMENT_PATTERN)
+      build_string.replace(@system_wrapper.module_eval(build_string))
+    end
+    
+    return build_string.strip
+  end
+
+  
+  # handle argument hash: keys are substitution strings, values are data to be expanded within substitution strings
+  def dehashify_argument_elements(hash)
+    build_string = ''
+    elements = []
+
+    # grab the substitution string (hash key)
+    substitution = hash.keys[0].to_s
+    # grab the string(s) to squirt into the substitution string (hash value)
+    expand = hash[hash.keys[0]]
+
+    if (expand.nil?)
+      @streaminator.stderr_puts("ERROR: Tool '#{@tool_name}' could not expand nil elements for substitution string '#{substitution}'.", Verbosity::ERRORS)
+      raise
+    end
+    
+    # array-ify expansion input if only a single string
+    expansion = ((expand.class == String) ? [expand] : expand)
+    
+    expansion.each do |item|
+      # code eval substitution
+      if (item =~ RUBY_EVAL_REPLACEMENT_PATTERN)
+        elements << eval($1)
+      # string eval substitution
+      elsif (item =~ RUBY_STRING_REPLACEMENT_PATTERN)
+        elements << @system_wrapper.module_eval(item)
+      # global constants
+      elsif (@system_wrapper.constants_include?(item))
+        const = Object.const_get(item)
+        if (const.nil?)
+          @streaminator.stderr_puts("ERROR: Tool '#{@tool_name}' found constant '#{item}' to be nil.", Verbosity::ERRORS)
+          raise
+        else
+          elements << const
+        end
+      elsif (item.class == Array)
+        elements << item
+      elsif (item.class == String)
+        @streaminator.stderr_puts("ERROR: Tool '#{@tool_name}' cannot expand nonexistent value '#{item}' for substitution string '#{substitution}'.", Verbosity::ERRORS)
+        raise        
+      else
+        @streaminator.stderr_puts("ERROR: Tool '#{@tool_name}' cannot expand value having type '#{item.class}' for substitution string '#{substitution}'.", Verbosity::ERRORS)
+        raise        
+      end
+    end
+    
+    # expand elements (whether string or array) into substitution string & replace escaped '\$'
+    elements.flatten!
+    elements.each do |element|
+      build_string.concat( substitution.sub(/([^\\]*)\$/, "\\1#{element}") ) # don't replace escaped '\$' but allow us to replace just a lonesome '$'
+      build_string.gsub!(/\\\$/, '$')
+      build_string.concat(' ')
+    end
+
+    return build_string.strip
+  end
+
+end

+ 115 - 0
tests/vendor/ceedling/lib/tool_executor_helper.rb

@@ -0,0 +1,115 @@
+require 'constants' # for Verbosity enumeration & $stderr redirect enumeration
+
+class ToolExecutorHelper
+
+  constructor :streaminator, :system_utils, :system_wrapper
+
+  def stderr_redirection(tool_config, logging)
+    # if there's no logging enabled, return :stderr_redirect unmodified
+    return tool_config[:stderr_redirect] if (not logging)
+    
+    # if there is logging enabled but the redirect is a custom value (not enum), return the custom string
+    return tool_config[:stderr_redirect] if (tool_config[:stderr_redirect].class == String)
+ 
+    # if logging is enabled but there's no custom string, return the AUTO enumeration so $stderr goes into the log
+    return StdErrRedirect::AUTO
+  end
+
+  def background_exec_cmdline_prepend(tool_config)
+    return nil if (tool_config[:background_exec].nil?)
+    
+    config_exec = tool_config[:background_exec]
+    
+    if ((config_exec == BackgroundExec::AUTO) and (@system_wrapper.windows?))
+      return 'start'
+    end
+
+    if (config_exec == BackgroundExec::WIN)
+      return 'start'
+    end
+
+    return nil
+  end
+
+  def osify_path_separators(executable)
+    return executable.gsub(/\//, '\\') if (@system_wrapper.windows?)
+    return executable
+  end
+  
+  def stderr_redirect_cmdline_append(tool_config)
+    return nil if (tool_config[:stderr_redirect].nil?)
+    
+    config_redirect = tool_config[:stderr_redirect]
+    redirect        = StdErrRedirect::NONE
+    
+    if (config_redirect == StdErrRedirect::AUTO)
+       if (@system_wrapper.windows?)
+         redirect = StdErrRedirect::WIN
+       else
+         if (@system_utils.tcsh_shell?)
+           redirect = StdErrRedirect::TCSH
+         else
+           redirect = StdErrRedirect::UNIX           
+         end
+       end
+    end
+
+    case redirect
+      # we may need more complicated processing after some learning with various environments
+      when StdErrRedirect::NONE then nil
+      when StdErrRedirect::WIN  then '2>&1'
+      when StdErrRedirect::UNIX then '2>&1'
+      when StdErrRedirect::TCSH then '|&'
+      else redirect.to_s
+    end
+  end
+
+  def background_exec_cmdline_append(tool_config)
+    return nil if (tool_config[:background_exec].nil?)
+
+    config_exec = tool_config[:background_exec]
+    
+    # if :auto & windows, then we already prepended 'start' and should append nothing
+    return nil if ((config_exec == BackgroundExec::AUTO) and (@system_wrapper.windows?))
+
+    # if :auto & not windows, then we append standard '&'
+    return '&' if ((config_exec == BackgroundExec::AUTO) and (not @system_wrapper.windows?))
+
+    # if explicitly Unix, then append '&'
+    return '&' if (config_exec == BackgroundExec::UNIX)
+    
+    # all other cases, including :none, :win, & anything unrecognized, append nothing
+    return nil
+  end
+
+  # if command succeeded and we have verbosity cranked up, spill our guts
+  def print_happy_results(command_str, shell_result, boom=true)
+    if ((shell_result[:exit_code] == 0) or ((shell_result[:exit_code] != 0) and not boom))
+      output  = "> Shell executed command:\n"
+      output += "#{command_str}\n"
+      output += "> Produced output:\n"             if (not shell_result[:output].empty?)
+      output += "#{shell_result[:output].strip}\n" if (not shell_result[:output].empty?)
+      output += "> And exited with status: [#{shell_result[:exit_code]}].\n" if (shell_result[:exit_code] != 0)
+      output += "\n"
+  
+      @streaminator.stdout_puts(output, Verbosity::OBNOXIOUS)
+    end
+  end
+
+  # if command failed and we have verbosity set to minimum error level, spill our guts
+  def print_error_results(command_str, shell_result, boom=true)
+    if ((shell_result[:exit_code] != 0) and boom)
+      output  = "ERROR: Shell command failed.\n"
+      output += "> Shell executed command:\n"
+      output += "'#{command_str}'\n"
+      output += "> Produced output:\n"             if (not shell_result[:output].empty?)
+      output += "#{shell_result[:output].strip}\n" if (not shell_result[:output].empty?)
+      output += "> And exited with status: [#{shell_result[:exit_code]}].\n" if (shell_result[:exit_code] != nil)
+      output += "> And then likely crashed.\n"                               if (shell_result[:exit_code] == nil)
+      output += "\n"
+
+      @streaminator.stderr_puts(output, Verbosity::ERRORS)
+    end
+  end
+  
+end

+ 10 - 0
tests/vendor/ceedling/lib/verbosinator.rb

@@ -0,0 +1,10 @@
+
+class Verbosinator
+
+  constructor :configurator
+
+  def should_output?(level)
+    return (level <= @configurator.project_verbosity)
+  end
+
+end

+ 16 - 0
tests/vendor/ceedling/lib/yaml_wrapper.rb

@@ -0,0 +1,16 @@
+require 'yaml'
+
+
+class YamlWrapper
+
+  def load(filepath)
+    return YAML.load(File.read(filepath))
+  end
+
+  def dump(filepath, structure)
+    File.open(filepath, 'w') do |output|
+      YAML.dump(structure, output)
+    end
+  end
+
+end

+ 15 - 0
tests/vendor/ceedling/plugins/bullseye/assets/template.erb

@@ -0,0 +1,15 @@
+% function_string = hash[:coverage][:functions].to_s
+% branch_string   = hash[:coverage][:branches].to_s
+% format_string   = "%#{[function_string.length, branch_string.length].max}i"
+<%=@ceedling[:plugin_reportinator].generate_banner("#{hash[:header]}: CODE COVERAGE SUMMARY")%>
+% if (!hash[:coverage][:functions].nil?)
+FUNCTIONS: <%=sprintf(format_string, hash[:coverage][:functions])%>%
+% else
+FUNCTIONS: none
+% end
+% if (!hash[:coverage][:branches].nil?)
+BRANCHES:  <%=sprintf(format_string, hash[:coverage][:branches])%>%
+% else
+BRANCHES:  none
+% end
+

+ 162 - 0
tests/vendor/ceedling/plugins/bullseye/bullseye.rake

@@ -0,0 +1,162 @@
+
+directory(BULLSEYE_BUILD_OUTPUT_PATH)
+directory(BULLSEYE_RESULTS_PATH)
+directory(BULLSEYE_ARTIFACTS_PATH)
+directory(BULLSEYE_DEPENDENCIES_PATH)
+
+CLEAN.include(File.join(BULLSEYE_BUILD_OUTPUT_PATH, '*'))
+CLEAN.include(File.join(BULLSEYE_RESULTS_PATH, '*'))
+CLEAN.include(File.join(BULLSEYE_DEPENDENCIES_PATH, '*'))
+
+CLOBBER.include(File.join(BULLSEYE_BUILD_PATH, '**/*'))
+
+
+rule(/#{BULLSEYE_BUILD_OUTPUT_PATH}\/#{'.+\\'+EXTENSION_OBJECT}$/ => [
+    proc do |task_name|
+      @ceedling[:file_finder].find_compilation_input_file(task_name)
+    end
+  ]) do |object|
+
+  if (File.basename(object.source) =~ /^(#{PROJECT_TEST_FILE_PREFIX}|#{CMOCK_MOCK_PREFIX}|#{BULLSEYE_IGNORE_SOURCES.join('|')})/i)
+    @ceedling[:generator].generate_object_file(
+      TOOLS_BULLSEYE_COMPILER,
+      BULLSEYE_SYM,
+      object.source,
+      object.name,
+      @ceedling[:file_path_utils].form_test_build_list_filepath( object.name ) )
+  else
+    @ceedling[BULLSEYE_SYM].generate_coverage_object_file(object.source, object.name)
+  end
+
+end
+
+rule(/#{BULLSEYE_BUILD_OUTPUT_PATH}\/#{'.+\\'+EXTENSION_EXECUTABLE}$/) do |bin_file|
+  @ceedling[:generator].generate_executable_file(
+    TOOLS_BULLSEYE_LINKER,
+    BULLSEYE_SYM,
+    bin_file.prerequisites,
+    bin_file.name,
+    @ceedling[:file_path_utils].form_test_build_map_filepath(bin_file.name))
+end
+
+rule(/#{BULLSEYE_RESULTS_PATH}\/#{'.+\\'+EXTENSION_TESTPASS}$/ => [
+     proc do |task_name|
+       @ceedling[:file_path_utils].form_test_executable_filepath(task_name)
+     end
+  ]) do |test_result|
+  @ceedling[:generator].generate_test_results(TOOLS_BULLSEYE_FIXTURE, BULLSEYE_SYM, test_result.source, test_result.name)
+end
+
+rule(/#{BULLSEYE_DEPENDENCIES_PATH}\/#{'.+\\'+EXTENSION_DEPENDENCIES}$/ => [
+    proc do |task_name|
+      @ceedling[:file_finder].find_compilation_input_file(task_name)
+    end
+  ]) do |dep|
+  @ceedling[:generator].generate_dependencies_file(
+    TOOLS_TEST_DEPENDENCIES_GENERATOR,
+    BULLSEYE_SYM,
+    dep.source,
+    File.join(BULLSEYE_BUILD_OUTPUT_PATH, File.basename(dep.source).ext(EXTENSION_OBJECT) ),
+    dep.name)
+end
+
+task :directories => [BULLSEYE_BUILD_OUTPUT_PATH, BULLSEYE_RESULTS_PATH, BULLSEYE_DEPENDENCIES_PATH, BULLSEYE_ARTIFACTS_PATH]
+
+namespace BULLSEYE_SYM do
+
+  task :source_coverage => COLLECTION_ALL_SOURCE.pathmap("#{BULLSEYE_BUILD_OUTPUT_PATH}/%n#{@ceedling[:configurator].extension_object}")
+
+  desc "Run code coverage for all tests"
+  task :all => [:directories] do
+    @ceedling[:configurator].replace_flattened_config(@ceedling[BULLSEYE_SYM].config)
+    @ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS, BULLSEYE_SYM)
+    @ceedling[:configurator].restore_config
+  end
+
+  desc "Run single test w/ coverage ([*] real test or source file name, no path)."
+  task :* do
+    message = "\nOops! '#{BULLSEYE_ROOT_NAME}:*' isn't a real task. " +
+              "Use a real test or source file name (no path) in place of the wildcard.\n" +
+              "Example: rake #{BULLSEYE_ROOT_NAME}:foo.c\n\n"
+  
+    @ceedling[:streaminator].stdout_puts( message )
+  end
+  
+  desc "Run tests by matching regular expression pattern."
+  task :pattern, [:regex] => [:directories] do |t, args|
+    matches = []
+    
+    COLLECTION_ALL_TESTS.each do |test|
+      matches << test if test =~ /#{args.regex}/
+    end
+  
+    if (matches.size > 0)
+      @ceedling[:configurator].replace_flattened_config(@ceedling[BULLSEYE_SYM].config)
+      @ceedling[:test_invoker].setup_and_invoke(matches, BULLSEYE_SYM, {:force_run => false})
+      @ceedling[:configurator].restore_config
+    else
+      @ceedling[:streaminator].stdout_puts("\nFound no tests matching pattern /#{args.regex}/.")
+    end
+  end
+
+  desc "Run tests whose test path contains [dir] or [dir] substring."
+  task :path, [:dir] => [:directories] do |t, args|
+    matches = []
+    
+    COLLECTION_ALL_TESTS.each do |test|
+      matches << test if File.dirname(test).include?(args.dir.gsub(/\\/, '/'))
+    end
+  
+    if (matches.size > 0)
+      @ceedling[:configurator].replace_flattened_config(@ceedling[BULLSEYE_SYM].config)
+      @ceedling[:test_invoker].setup_and_invoke(matches, BULLSEYE_SYM, {:force_run => false})
+      @ceedling[:configurator].restore_config
+    else
+      @ceedling[:streaminator].stdout_puts("\nFound no tests including the given path or path component.")
+    end
+  end
+
+  desc "Run code coverage for changed files"
+  task :delta => [:directories] do
+    @ceedling[:configurator].replace_flattened_config(@ceedling[BULLSEYE_SYM].config)
+    @ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS, BULLSEYE_SYM, {:force_run => false})
+    @ceedling[:configurator].restore_config
+  end
+  
+  # use a rule to increase efficiency for large projects
+  # bullseye test tasks by regex
+  rule(/^#{BULLSEYE_TASK_ROOT}\S+$/ => [
+      proc do |task_name|
+        test = task_name.sub(/#{BULLSEYE_TASK_ROOT}/, '')
+        test = "#{PROJECT_TEST_FILE_PREFIX}#{test}" if not (test.start_with?(PROJECT_TEST_FILE_PREFIX))
+        @ceedling[:file_finder].find_test_from_file_path(test)
+      end
+  ]) do |test|
+    @ceedling[:rake_wrapper][:directories].invoke
+    @ceedling[:configurator].replace_flattened_config(@ceedling[BULLSEYE_SYM].config)
+    @ceedling[:test_invoker].setup_and_invoke([test.source], BULLSEYE_SYM)
+    @ceedling[:configurator].restore_config
+  end
+
+end
+
+if PROJECT_USE_DEEP_DEPENDENCIES
+namespace REFRESH_SYM do
+  task BULLSEYE_SYM do
+    @ceedling[:configurator].replace_flattened_config(@ceedling[BULLSEYE_SYM].config)
+    @ceedling[:test_invoker].refresh_deep_dependencies
+    @ceedling[:configurator].restore_config
+  end
+end
+end
+
+namespace UTILS_SYM do
+  
+  desc "Open Bullseye code coverage browser"
+  task BULLSEYE_SYM do
+    command = @ceedling[:tool_executor].build_command_line(TOOLS_BULLSEYE_BROWSER)
+    @ceedling[:tool_executor].exec(command[:line], command[:options])
+  end
+  
+end
+

+ 53 - 0
tests/vendor/ceedling/plugins/bullseye/config/defaults.yml

@@ -0,0 +1,53 @@
+---
+
+:paths:
+  :bullseye_toolchain_include: []
+
+:tools:
+  :bullseye_instrumentation:
+    :executable: covc
+    :arguments:
+      - '--file $': ENVIRONMENT_COVFILE
+      - -q
+      - ${1}
+  :bullseye_compiler:
+    :executable: gcc
+    :arguments:
+      - -g
+      - -I"$": COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR
+      - -I"$": COLLECTION_PATHS_BULLSEYE_TOOLCHAIN_INCLUDE
+      - -D$: COLLECTION_DEFINES_TEST_AND_VENDOR
+      - -DBULLSEYE_COMPILER
+      - -c "${1}"
+      - -o "${2}"
+  :bullseye_linker:
+    :executable: gcc
+    :arguments:
+      - ${1}
+      - -o ${2}
+      - -L$: PLUGINS_BULLSEYE_LIB_PATH
+      - -lcov
+  :bullseye_fixture:
+    :executable: ${1}
+  :bullseye_report_covsrc:
+    :executable: covsrc
+    :arguments:
+      - '--file $': ENVIRONMENT_COVFILE
+      - -q
+      - -w140
+  :bullseye_report_covfn:
+    :executable: covfn
+    :stderr_redirect: :auto
+    :arguments:
+      - '--file $': ENVIRONMENT_COVFILE
+      - --width 120
+      - --no-source
+      - '"${1}"'
+  :bullseye_browser:
+    :executable: CoverageBrowser
+    :background_exec: :auto
+    :optional: TRUE
+    :arguments:
+      - '"$"': ENVIRONMENT_COVFILE
+
+...

+ 172 - 0
tests/vendor/ceedling/plugins/bullseye/lib/bullseye.rb

@@ -0,0 +1,172 @@
+require 'plugin'
+require 'constants'
+
+BULLSEYE_ROOT_NAME         = 'bullseye'
+BULLSEYE_TASK_ROOT         = BULLSEYE_ROOT_NAME + ':'
+BULLSEYE_SYM               = BULLSEYE_ROOT_NAME.to_sym
+
+BULLSEYE_BUILD_PATH        = "#{PROJECT_BUILD_ROOT}/#{BULLSEYE_ROOT_NAME}"
+BULLSEYE_BUILD_OUTPUT_PATH = "#{BULLSEYE_BUILD_PATH}/out"
+BULLSEYE_RESULTS_PATH      = "#{BULLSEYE_BUILD_PATH}/results"
+BULLSEYE_DEPENDENCIES_PATH = "#{BULLSEYE_BUILD_PATH}/dependencies"
+BULLSEYE_ARTIFACTS_PATH    = "#{PROJECT_BUILD_ARTIFACTS_ROOT}/#{BULLSEYE_ROOT_NAME}"
+
+BULLSEYE_IGNORE_SOURCES    = ['unity', 'cmock', 'cexception']
+
+
+class Bullseye < Plugin
+
+  def setup
+    @result_list = []
+    @environment = [ {:covfile => File.join( BULLSEYE_ARTIFACTS_PATH, 'test.cov' )} ]
+    @plugin_root = File.expand_path(File.join(File.dirname(__FILE__), '..'))
+    @coverage_template_all = @ceedling[:file_wrapper].read(File.join(@plugin_root, 'assets/template.erb'))
+  end
+
+  def config
+    {
+      :project_test_build_output_path     => BULLSEYE_BUILD_OUTPUT_PATH,
+      :project_test_results_path          => BULLSEYE_RESULTS_PATH,
+      :project_test_dependencies_path     => BULLSEYE_DEPENDENCIES_PATH,
+      :defines_test                       => DEFINES_TEST + ['CODE_COVERAGE'],
+      :collection_defines_test_and_vendor => COLLECTION_DEFINES_TEST_AND_VENDOR + ['CODE_COVERAGE']
+    }
+  end
+
+  def generate_coverage_object_file(source, object)
+    arg_hash = {:tool => TOOLS_BULLSEYE_INSTRUMENTATION, :context => BULLSEYE_SYM, :source => source, :object => object}
+    @ceedling[:plugin_manager].pre_compile_execute(arg_hash)
+
+    @ceedling[:streaminator].stdout_puts("Compiling #{File.basename(source)} with coverage...")
+    compile_command  = 
+      @ceedling[:tool_executor].build_command_line(
+        TOOLS_BULLSEYE_COMPILER,
+        source,
+        object,
+        @ceedling[:file_path_utils].form_test_build_list_filepath( object ) )
+    coverage_command = @ceedling[:tool_executor].build_command_line(TOOLS_BULLSEYE_INSTRUMENTATION, compile_command[:line] )
+
+    shell_result     = @ceedling[:tool_executor].exec( coverage_command[:line], coverage_command[:options] )
+    
+    arg_hash[:shell_result] = shell_result
+    @ceedling[:plugin_manager].post_compile_execute(arg_hash)
+  end
+
+  def post_test_fixture_execute(arg_hash)
+    result_file = arg_hash[:result_file]
+  
+    if ((result_file =~ /#{BULLSEYE_RESULTS_PATH}/) and (not @result_list.include?(result_file)))
+      @result_list << arg_hash[:result_file]
+    end
+  end
+    
+  def post_build
+    return if (not @ceedling[:task_invoker].invoked?(/^#{BULLSEYE_TASK_ROOT}/))
+
+    # test results
+    results = @ceedling[:plugin_reportinator].assemble_test_results(@result_list)
+    hash = {
+      :header => BULLSEYE_ROOT_NAME.upcase,
+      :results => results
+    }
+    
+    @ceedling[:plugin_reportinator].run_test_results_report(hash) do
+      message = ''
+      message = 'Unit test failures.' if (results[:counts][:failed] > 0)
+      message
+    end
+    
+    # coverage results
+    return if (verify_coverage_file() == false)
+    if (@ceedling[:task_invoker].invoked?(/^#{BULLSEYE_TASK_ROOT}(all|delta)/))
+      command      = @ceedling[:tool_executor].build_command_line(TOOLS_BULLSEYE_REPORT_COVSRC)
+      shell_result = @ceedling[:tool_executor].exec(command[:line], command[:options])
+      report_coverage_results_all(shell_result[:output])
+    else
+      report_per_function_coverage_results(@ceedling[:test_invoker].sources)
+    end
+  end
+
+  def summary
+    return if (verify_coverage_file() == false)
+    result_list = @ceedling[:file_path_utils].form_pass_results_filelist( BULLSEYE_RESULTS_PATH, COLLECTION_ALL_TESTS )
+
+    # test results
+    # get test results for only those tests in our configuration and of those only tests with results on disk
+    hash = {
+      :header => BULLSEYE_ROOT_NAME.upcase,
+      :results => @ceedling[:plugin_reportinator].assemble_test_results(result_list, {:boom => false})
+    }
+
+    @ceedling[:plugin_reportinator].run_test_results_report(hash)
+    
+    # coverage results
+    command = @ceedling[:tool_executor].build_command_line(TOOLS_BULLSEYE_REPORT_COVSRC)
+    shell_result = @ceedling[:tool_executor].exec(command[:line], command[:options])
+    report_coverage_results_all(shell_result[:output])
+  end
+
+  private ###################################
+
+  def report_coverage_results_all(coverage)
+    results = {
+      :header => BULLSEYE_ROOT_NAME.upcase,
+      :coverage => {
+        :functions => nil,
+        :branches  => nil
+      }
+    }
+
+    if (coverage =~ /^Total.*?=\s+([0-9]+)\%/)
+      results[:coverage][:functions] = $1.to_i
+    end
+    
+    if (coverage =~ /^Total.*=\s+([0-9]+)\%\s*$/)
+      results[:coverage][:branches] = $1.to_i
+    end
+
+    @ceedling[:plugin_reportinator].run_report($stdout, @coverage_template_all, results)
+  end
+
+  def report_per_function_coverage_results(sources)
+    banner = @ceedling[:plugin_reportinator].generate_banner( "#{BULLSEYE_ROOT_NAME.upcase}: CODE COVERAGE SUMMARY" )
+    @ceedling[:streaminator].stdout_puts "\n" + banner
+
+    coverage_sources = sources.clone
+    coverage_sources.delete_if {|item| item =~ /#{CMOCK_MOCK_PREFIX}.+#{EXTENSION_SOURCE}$/}
+    coverage_sources.delete_if {|item| item =~ /#{BULLSEYE_IGNORE_SOURCES.join('|')}#{EXTENSION_SOURCE}$/}
+
+    coverage_sources.each do |source|
+      command          = @ceedling[:tool_executor].build_command_line(TOOLS_BULLSEYE_REPORT_COVFN, source)
+      shell_results    = @ceedling[:tool_executor].exec(command[:line], command[:options])
+      coverage_results = shell_results[:output].deep_clone
+      coverage_results.sub!(/.*\n.*\n/,'') # Remove the Bullseye tool banner
+      if (coverage_results =~ /warning cov814: report is empty/)
+        coverage_results = "WARNING: #{source} contains no coverage data!\n\n"
+        @ceedling[:streaminator].stdout_puts(coverage_results, Verbosity::COMPLAIN)
+      else
+        coverage_results += "\n"
+        @ceedling[:streaminator].stdout_puts(coverage_results)
+      end
+    end
+  end
+
+  def verify_coverage_file
+    exist = @ceedling[:file_wrapper].exist?( ENVIRONMENT_COVFILE )
+
+    if (!exist)
+      banner = @ceedling[:plugin_reportinator].generate_banner( "#{BULLSEYE_ROOT_NAME.upcase}: CODE COVERAGE SUMMARY" )
+      @ceedling[:streaminator].stdout_puts "\n" + banner + "\nNo coverage file.\n\n"
+    end
+    
+    return exist
+  end
+  
+end
+
+
+# end blocks always executed following rake run
+END {
+  # cache our input configurations to use in comparison upon next execution
+  @ceedling[:cacheinator].cache_test_config( @ceedling[:setupinator].config_hash ) if (@ceedling[:task_invoker].invoked?(/^#{BULLSEYE_TASK_ROOT}/))
+}

+ 0 - 0
tests/vendor/ceedling/plugins/bullseye/readme.txt


+ 34 - 0
tests/vendor/ceedling/plugins/gcov/defaults.yml

@@ -0,0 +1,34 @@
+---
+
+:tools:
+  :gcov_compiler:
+    :executable: gcc
+    :arguments:
+      - -g
+      - -fprofile-arcs
+      - -ftest-coverage
+      - -I"$": COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR
+      - -I"$": COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE
+      - -D$: COLLECTION_DEFINES_TEST_AND_VENDOR
+      - -DGCOV_COMPILER
+      - -c "${1}"
+      - -o "${2}"
+  :gcov_linker:
+    :executable: gcc
+    :arguments:
+      - -fprofile-arcs
+      - -ftest-coverage
+      - ${1}
+      - -o ${2}
+  :gcov_fixture:
+    :executable: ${1}
+  :gcov_report:
+    :executable: gcov
+    :arguments:
+      - -n
+      - -p
+      - -b
+      - -o "$": GCOV_BUILD_OUTPUT_PATH
+      - "\"${1}\""
+
+...

+ 152 - 0
tests/vendor/ceedling/plugins/gcov/gcov.rake

@@ -0,0 +1,152 @@
+
+directory(GCOV_BUILD_OUTPUT_PATH)
+directory(GCOV_RESULTS_PATH)
+directory(GCOV_ARTIFACTS_PATH)
+directory(GCOV_DEPENDENCIES_PATH)
+
+CLEAN.include(File.join(GCOV_BUILD_OUTPUT_PATH, '*'))
+CLEAN.include(File.join(GCOV_RESULTS_PATH, '*'))
+CLEAN.include(File.join(GCOV_DEPENDENCIES_PATH, '*'))
+
+CLOBBER.include(File.join(GCOV_BUILD_PATH, '**/*'))
+
+
+rule(/#{GCOV_BUILD_OUTPUT_PATH}\/#{'.+\\'+EXTENSION_OBJECT}$/ => [
+    proc do |task_name|
+      @ceedling[:file_finder].find_compilation_input_file(task_name)
+    end
+  ]) do |object|
+
+  if (File.basename(object.source) =~ /^(#{PROJECT_TEST_FILE_PREFIX}|#{CMOCK_MOCK_PREFIX}|#{GCOV_IGNORE_SOURCES.join('|')})/i)
+    @ceedling[:generator].generate_object_file(
+      TOOLS_GCOV_COMPILER,
+      GCOV_SYM,
+      object.source,
+      object.name,
+      @ceedling[:file_path_utils].form_test_build_list_filepath( object.name ) )
+  else
+    @ceedling[GCOV_SYM].generate_coverage_object_file(object.source, object.name)
+  end
+
+end
+
+rule(/#{GCOV_BUILD_OUTPUT_PATH}\/#{'.+\\'+EXTENSION_EXECUTABLE}$/) do |bin_file|
+  @ceedling[:generator].generate_executable_file(
+    TOOLS_GCOV_LINKER,
+    GCOV_SYM,
+    bin_file.prerequisites,
+    bin_file.name,
+    @ceedling[:file_path_utils].form_test_build_map_filepath(bin_file.name))
+end
+
+rule(/#{GCOV_RESULTS_PATH}\/#{'.+\\'+EXTENSION_TESTPASS}$/ => [
+     proc do |task_name|
+       @ceedling[:file_path_utils].form_test_executable_filepath(task_name)
+     end
+  ]) do |test_result|
+  @ceedling[:generator].generate_test_results(TOOLS_GCOV_FIXTURE, GCOV_SYM, test_result.source, test_result.name)
+end
+
+rule(/#{GCOV_DEPENDENCIES_PATH}\/#{'.+\\'+EXTENSION_DEPENDENCIES}$/ => [
+    proc do |task_name|
+      @ceedling[:file_finder].find_compilation_input_file(task_name)
+    end
+  ]) do |dep|
+  @ceedling[:generator].generate_dependencies_file(
+    TOOLS_TEST_DEPENDENCIES_GENERATOR,
+    GCOV_SYM,
+    dep.source,
+    File.join(GCOV_BUILD_OUTPUT_PATH, File.basename(dep.source).ext(EXTENSION_OBJECT) ),
+    dep.name)
+end
+
+task :directories => [GCOV_BUILD_OUTPUT_PATH, GCOV_RESULTS_PATH, GCOV_DEPENDENCIES_PATH, GCOV_ARTIFACTS_PATH]
+
+namespace GCOV_SYM do
+
+  task :source_coverage => COLLECTION_ALL_SOURCE.pathmap("#{GCOV_BUILD_OUTPUT_PATH}/%n#{@ceedling[:configurator].extension_object}")
+
+  desc "Run code coverage for all tests"
+  task :all => [:directories] do
+    @ceedling[:configurator].replace_flattened_config(@ceedling[GCOV_SYM].config)
+    @ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS, GCOV_SYM)
+    @ceedling[:configurator].restore_config
+  end
+
+  desc "Run single test w/ coverage ([*] real test or source file name, no path)."
+  task :* do
+    message = "\nOops! '#{GCOV_ROOT_NAME}:*' isn't a real task. " +
+              "Use a real test or source file name (no path) in place of the wildcard.\n" +
+              "Example: rake #{GCOV_ROOT_NAME}:foo.c\n\n"
+  
+    @ceedling[:streaminator].stdout_puts( message )
+  end
+  
+  desc "Run tests by matching regular expression pattern."
+  task :pattern, [:regex] => [:directories] do |t, args|
+    matches = []
+    
+    COLLECTION_ALL_TESTS.each do |test|
+      matches << test if test =~ /#{args.regex}/
+    end
+  
+    if (matches.size > 0)
+      @ceedling[:configurator].replace_flattened_config(@ceedling[GCOV_SYM].config)
+      @ceedling[:test_invoker].setup_and_invoke(matches, GCOV_SYM, {:force_run => false})
+      @ceedling[:configurator].restore_config
+    else
+      @ceedling[:streaminator].stdout_puts("\nFound no tests matching pattern /#{args.regex}/.")
+    end
+  end
+
+  desc "Run tests whose test path contains [dir] or [dir] substring."
+  task :path, [:dir] => [:directories] do |t, args|
+    matches = []
+    
+    COLLECTION_ALL_TESTS.each do |test|
+      matches << test if File.dirname(test).include?(args.dir.gsub(/\\/, '/'))
+    end
+  
+    if (matches.size > 0)
+      @ceedling[:configurator].replace_flattened_config(@ceedling[GCOV_SYM].config)
+      @ceedling[:test_invoker].setup_and_invoke(matches, GCOV_SYM, {:force_run => false})
+      @ceedling[:configurator].restore_config
+    else
+      @ceedling[:streaminator].stdout_puts("\nFound no tests including the given path or path component.")
+    end
+  end
+
+  desc "Run code coverage for changed files"
+  task :delta => [:directories] do
+    @ceedling[:configurator].replace_flattened_config(@ceedling[GCOV_SYM].config)
+    @ceedling[:test_invoker].setup_and_invoke(COLLECTION_ALL_TESTS, GCOV_SYM, {:force_run => false})
+    @ceedling[:configurator].restore_config
+  end
+  
+  # use a rule to increase efficiency for large projects
+  # gcov test tasks by regex
+  rule(/^#{GCOV_TASK_ROOT}\S+$/ => [
+      proc do |task_name|
+        test = task_name.sub(/#{GCOV_TASK_ROOT}/, '')
+        test = "#{PROJECT_TEST_FILE_PREFIX}#{test}" if not (test.start_with?(PROJECT_TEST_FILE_PREFIX))
+        @ceedling[:file_finder].find_test_from_file_path(test)
+      end
+  ]) do |test|
+    @ceedling[:rake_wrapper][:directories].invoke
+    @ceedling[:configurator].replace_flattened_config(@ceedling[GCOV_SYM].config)
+    @ceedling[:test_invoker].setup_and_invoke([test.source], GCOV_SYM)
+    @ceedling[:configurator].restore_config
+  end
+  
+end
+
+if PROJECT_USE_DEEP_DEPENDENCIES
+namespace REFRESH_SYM do
+  task GCOV_SYM do
+    @ceedling[:configurator].replace_flattened_config(@ceedling[GCOV_SYM].config)
+    @ceedling[:test_invoker].refresh_deep_dependencies
+    @ceedling[:configurator].restore_config
+  end
+end
+end
+

+ 128 - 0
tests/vendor/ceedling/plugins/gcov/gcov.rb

@@ -0,0 +1,128 @@
+require 'plugin'
+require 'constants'
+
+GCOV_ROOT_NAME         = 'gcov'
+GCOV_TASK_ROOT         = GCOV_ROOT_NAME + ':'
+GCOV_SYM               = GCOV_ROOT_NAME.to_sym
+
+GCOV_BUILD_PATH        = "#{PROJECT_BUILD_ROOT}/#{GCOV_ROOT_NAME}"
+GCOV_BUILD_OUTPUT_PATH = "#{GCOV_BUILD_PATH}/out"
+GCOV_RESULTS_PATH      = "#{GCOV_BUILD_PATH}/results"
+GCOV_DEPENDENCIES_PATH = "#{GCOV_BUILD_PATH}/dependencies"
+GCOV_ARTIFACTS_PATH    = "#{PROJECT_BUILD_ARTIFACTS_ROOT}/#{GCOV_ROOT_NAME}"
+
+GCOV_IGNORE_SOURCES    = ['unity', 'cmock', 'cexception']
+
+
+class Gcov < Plugin
+
+  attr_reader :config
+
+  def setup
+    @result_list = []  
+  
+    @config = {
+      :project_test_build_output_path     => GCOV_BUILD_OUTPUT_PATH,
+      :project_test_results_path          => GCOV_RESULTS_PATH,
+      :project_test_dependencies_path     => GCOV_DEPENDENCIES_PATH,
+      :defines_test                       => DEFINES_TEST + ['CODE_COVERAGE'],
+      :collection_defines_test_and_vendor => COLLECTION_DEFINES_TEST_AND_VENDOR + ['CODE_COVERAGE']
+      }
+    
+    @coverage_template_all = @ceedling[:file_wrapper].read( File.join( PLUGINS_GCOV_PATH, 'template.erb') )
+  end
+
+  def generate_coverage_object_file(source, object)
+    compile_command = 
+      @ceedling[:tool_executor].build_command_line(
+        TOOLS_GCOV_COMPILER,
+        source,
+        object,
+        @ceedling[:file_path_utils].form_test_build_list_filepath( object ) )
+    @ceedling[:streaminator].stdout_puts("Compiling #{File.basename(source)} with coverage...")
+    @ceedling[:tool_executor].exec( compile_command[:line], compile_command[:options] )
+  end
+
+  def post_test_fixture_execute(arg_hash)
+    result_file = arg_hash[:result_file]
+  
+    if ((result_file =~ /#{GCOV_RESULTS_PATH}/) and (not @result_list.include?(result_file)))
+      @result_list << arg_hash[:result_file]
+    end
+  end
+    
+  def post_build
+    return if (not @ceedling[:task_invoker].invoked?(/^#{GCOV_TASK_ROOT}/))
+
+    # test results
+    results = @ceedling[:plugin_reportinator].assemble_test_results(@result_list)
+    hash = {
+      :header => GCOV_ROOT_NAME.upcase,
+      :results => results
+    }
+    
+    @ceedling[:plugin_reportinator].run_test_results_report(hash) do
+      message = ''
+      message = 'Unit test failures.' if (results[:counts][:failed] > 0)
+      message
+    end
+    
+    if (@ceedling[:task_invoker].invoked?(/^#{GCOV_TASK_ROOT}(all|delta)/))
+      report_coverage_results_summary(@ceedling[:test_invoker].sources)
+    else
+      report_per_file_coverage_results(@ceedling[:test_invoker].sources)
+    end
+  end
+
+  def summary
+    result_list = @ceedling[:file_path_utils].form_pass_results_filelist( GCOV_RESULTS_PATH, COLLECTION_ALL_TESTS )
+
+    # test results
+    # get test results for only those tests in our configuration and of those only tests with results on disk
+    hash = {
+      :header => GCOV_ROOT_NAME.upcase,
+      :results => @ceedling[:plugin_reportinator].assemble_test_results(result_list, {:boom => false})
+    }
+
+    @ceedling[:plugin_reportinator].run_test_results_report(hash)
+    
+    # coverage results
+    # command = @ceedling[:tool_executor].build_command_line(TOOLS_GCOV_REPORT_COVSRC)
+    # shell_result = @ceedling[:tool_executor].exec(command[:line], command[:options])
+    # report_coverage_results_all(shell_result[:output])
+  end
+
+  private ###################################
+
+  def report_coverage_results_summary(sources)
+
+  end
+
+  def report_per_file_coverage_results(sources)
+    banner = @ceedling[:plugin_reportinator].generate_banner "#{GCOV_ROOT_NAME.upcase}: CODE COVERAGE SUMMARY"
+    @ceedling[:streaminator].stdout_puts "\n" + banner
+
+    coverage_sources = sources.clone
+    coverage_sources.delete_if {|item| item =~ /#{CMOCK_MOCK_PREFIX}.+#{EXTENSION_SOURCE}$/}
+    coverage_sources.delete_if {|item| item =~ /#{GCOV_IGNORE_SOURCES.join('|')}#{EXTENSION_SOURCE}$/}
+
+    coverage_sources.each do |source|
+      basename         = File.basename(source)
+      command          = @ceedling[:tool_executor].build_command_line(TOOLS_GCOV_REPORT, basename)
+      shell_results    = @ceedling[:tool_executor].exec(command[:line], command[:options])
+      coverage_results = shell_results[:output]
+
+      if (coverage_results.strip =~ /(File\s+'#{Regexp.escape(source)}'.+$)/m)
+        report = ((($1.lines.to_a)[1..-1])).map{|line| basename + ' ' + line}.join('')
+        @ceedling[:streaminator].stdout_puts(report + "\n\n")
+      end
+    end
+  end
+
+end
+
+# end blocks always executed following rake run
+END {
+  # cache our input configurations to use in comparison upon next execution
+  @ceedling[:cacheinator].cache_test_config( @ceedling[:setupinator].config_hash ) if (@ceedling[:task_invoker].invoked?(/^#{GCOV_TASK_ROOT}/))
+}

+ 0 - 0
tests/vendor/ceedling/plugins/gcov/readme.txt


+ 15 - 0
tests/vendor/ceedling/plugins/gcov/template.erb

@@ -0,0 +1,15 @@
+% function_string = hash[:coverage][:functions].to_s
+% branch_string   = hash[:coverage][:branches].to_s
+% format_string   = "%#{[function_string.length, branch_string.length].max}i"
+<%=@ceedling[:plugin_reportinator].generate_banner("#{BULLSEYE_ROOT_NAME.upcase}: CODE COVERAGE SUMMARY")%>
+% if (!hash[:coverage][:functions].nil?)
+FUNCTIONS: <%=sprintf(format_string, hash[:coverage][:functions])%>%
+% else
+FUNCTIONS: none
+% end
+% if (!hash[:coverage][:branches].nil?)
+BRANCHES:  <%=sprintf(format_string, hash[:coverage][:branches])%>%
+% else
+BRANCHES:  none
+% end
+

Некоторые файлы не были показаны из-за большого количества измененных файлов