ceedling 1.2.1 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (276) hide show
  1. checksums.yaml +4 -4
  2. data/GIT_COMMIT_SHA +1 -1
  3. data/README.md +11 -9
  4. data/Rakefile +2 -1
  5. data/assets/features/ceedling +1 -1
  6. data/assets/fixtures/test_source_file_duplicate_basenames/test_calc_typo.c +14 -0
  7. data/assets/fixtures/tests_with_duplicate_test_names/test_foo.c +21 -0
  8. data/bin/actionator.rb +6 -12
  9. data/bin/app_cfg.rb +14 -1
  10. data/bin/ceedling +13 -3
  11. data/bin/cli.rb +25 -6
  12. data/bin/cli_handler.rb +113 -73
  13. data/bin/cli_helper.rb +51 -78
  14. data/bin/installation_selector.rb +81 -0
  15. data/bin/launchinator.rb +69 -0
  16. data/bin/mixin_resolvinator.rb +26 -36
  17. data/bin/objects.yml +13 -0
  18. data/bin/path_validator.rb +25 -29
  19. data/bin/versionator.rb +47 -48
  20. data/ceedling.gemspec +19 -15
  21. data/docs/BreakingChanges.md +70 -1
  22. data/docs/Changelog.md +74 -3
  23. data/docs/KnownIssues.md +90 -4
  24. data/docs/ReleaseNotes.md +13 -0
  25. data/lib/ceedling/build_log_writer.rb +33 -0
  26. data/lib/ceedling/config/build_root_policy.rb +70 -0
  27. data/lib/ceedling/config/configurator.rb +14 -16
  28. data/lib/ceedling/config/configurator_builder.rb +58 -28
  29. data/lib/ceedling/config/configurator_paths_validator.rb +179 -0
  30. data/lib/ceedling/config/configurator_plugins.rb +1 -1
  31. data/lib/ceedling/config/configurator_setup.rb +15 -28
  32. data/lib/ceedling/config/configurator_validator.rb +1 -110
  33. data/lib/ceedling/defaults.rb +4 -4
  34. data/lib/ceedling/dependencies/README.md +18 -16
  35. data/lib/ceedling/dependencies/dependency_cache_store.rb +20 -3
  36. data/lib/ceedling/dependencies/dependency_hasher.rb +35 -1
  37. data/lib/ceedling/filename_extension.rb +7 -1
  38. data/lib/ceedling/filesystem/FILE_TREE.md +253 -0
  39. data/lib/ceedling/filesystem/PATH_HANDLING.md +304 -0
  40. data/lib/ceedling/filesystem/README.md +41 -0
  41. data/lib/ceedling/filesystem/build_subpath.rb +102 -0
  42. data/lib/ceedling/filesystem/destination_collision.rb +25 -0
  43. data/lib/ceedling/{file_finder.rb → filesystem/file_finder.rb} +159 -104
  44. data/lib/ceedling/{file_finder_helper.rb → filesystem/file_finder_helper.rb} +36 -31
  45. data/lib/ceedling/filesystem/file_path_collection_utils.rb +183 -0
  46. data/lib/ceedling/{file_path_utils.rb → filesystem/file_path_utils.rb} +108 -97
  47. data/lib/ceedling/{file_system_wrapper.rb → filesystem/file_system_wrapper.rb} +6 -5
  48. data/lib/ceedling/filesystem/file_tree.rb +241 -0
  49. data/lib/ceedling/{file_wrapper.rb → filesystem/file_wrapper.rb} +45 -38
  50. data/lib/ceedling/filesystem/path_exclusions.rb +30 -0
  51. data/lib/ceedling/{path_matcher.rb → filesystem/path_matcher.rb} +27 -23
  52. data/lib/ceedling/{path_mirror.rb → filesystem/path_mirror.rb} +19 -18
  53. data/lib/ceedling/filesystem/path_spellings.rb +55 -0
  54. data/lib/ceedling/filesystem/project_paths.rb +184 -0
  55. data/lib/ceedling/filesystem/working_dir.rb +40 -0
  56. data/lib/ceedling/generators/generator.rb +5 -6
  57. data/lib/ceedling/generators/generator_partials.rb +1 -1
  58. data/lib/ceedling/generators/generator_test_results_backtrace.rb +7 -7
  59. data/lib/ceedling/handoff.rb +121 -0
  60. data/lib/ceedling/includes/include_factory.rb +3 -3
  61. data/lib/ceedling/includes/include_pathinator.rb +67 -10
  62. data/lib/ceedling/includes/includes.rb +1 -1
  63. data/lib/ceedling/installation.rb +92 -0
  64. data/lib/ceedling/library_resolver.rb +99 -0
  65. data/lib/ceedling/objects.yml +23 -2
  66. data/lib/ceedling/partials/partializer.rb +4 -4
  67. data/lib/ceedling/partials/partializer_config.rb +3 -1
  68. data/lib/ceedling/plugins/report_log_writer_plugin.rb +3 -5
  69. data/lib/ceedling/preprocess/preprocessinator_file_assembler.rb +1 -1
  70. data/lib/ceedling/preprocess/preprocessinator_includes_handler.rb +1 -1
  71. data/lib/ceedling/preprocess/preprocessinator_line_marker_includes_extractor.rb +1 -1
  72. data/lib/ceedling/rake_app/rakefile_component_resolver.rb +10 -17
  73. data/lib/ceedling/rakefile.rb +15 -1
  74. data/lib/ceedling/rakefiles/base/tasks_base.rake +1 -1
  75. data/lib/ceedling/rakefiles/release/rules_release.rake +4 -7
  76. data/lib/ceedling/release_invoker/release_build_executor.rb +33 -44
  77. data/lib/ceedling/release_invoker/release_build_planner.rb +14 -6
  78. data/lib/ceedling/reportinator.rb +3 -4
  79. data/lib/ceedling/setupinator.rb +94 -90
  80. data/lib/ceedling/test_context_extractor.rb +5 -1
  81. data/lib/ceedling/test_invoker/PIPELINE.md +9 -7
  82. data/lib/ceedling/test_invoker/mock_locator.rb +60 -2
  83. data/lib/ceedling/test_invoker/partials_manager.rb +12 -0
  84. data/lib/ceedling/test_invoker/standin_headerinator.rb +22 -13
  85. data/lib/ceedling/test_invoker/test_build_executor.rb +24 -40
  86. data/lib/ceedling/test_invoker/test_build_planner.rb +37 -18
  87. data/lib/ceedling/test_invoker/test_build_setup.rb +17 -12
  88. data/lib/ceedling/test_invoker/test_build_validations.rb +16 -31
  89. data/lib/ceedling/test_invoker/test_file_preprocessinator.rb +1 -1
  90. data/lib/ceedling/test_invoker/test_invoker.rb +6 -1
  91. data/lib/ceedling/test_invoker/test_pipeline_helpers.rb +1 -1
  92. data/lib/ceedling/test_invoker/test_source_file_directive_resolver.rb +40 -20
  93. data/lib/ceedling/tool_executor.rb +9 -3
  94. data/lib/ceedling/yaml_wrapper.rb +3 -2
  95. data/lib/version.rb +1 -1
  96. data/plugins/bullseye/config/defaults_bullseye.rb +1 -1
  97. data/plugins/bullseye/lib/bullseye.rb +9 -7
  98. data/plugins/cppcheck/config/defaults_cppcheck.rb +1 -1
  99. data/plugins/dependencies/config/defaults.yml +6 -6
  100. data/plugins/dependencies/dependencies.rake +4 -1
  101. data/plugins/dependencies/lib/dependencies.rb +18 -8
  102. data/plugins/dependencies/lib/dependency_spec.rb +17 -5
  103. data/plugins/fff/lib/fff.rb +45 -39
  104. data/plugins/fff/spec/fff_cmock_wrapper_spec.rb +128 -0
  105. data/plugins/gcov/config/defaults_gcov.rb +1 -1
  106. data/plugins/gcov/lib/console_reportinator.rb +4 -18
  107. data/plugins/gcov/lib/gcov.rb +9 -2
  108. data/plugins/gcov/lib/gcovr_reportinator.rb +8 -40
  109. data/plugins/gcov/lib/gcovr_thresholds.rb +74 -0
  110. data/plugins/gcov/lib/reportgenerator_reportinator.rb +1 -1
  111. data/plugins/module_generator/lib/module_generator.rb +3 -3
  112. data/plugins/report_tests_teamcity_stdout/lib/report_tests_teamcity_stdout.rb +53 -80
  113. data/plugins/sanitize/lib/sanitize_records.rb +3 -6
  114. data/plugins/valgrind/lib/valgrind.rb +14 -7
  115. data/site-local/configuration/reference/cmock.html +2 -1
  116. data/site-local/configuration/reference/files.html +9 -3
  117. data/site-local/configuration/reference/libraries.html +21 -3
  118. data/site-local/configuration/reference/paths.html +29 -6
  119. data/site-local/configuration/reference/project.html +17 -7
  120. data/site-local/configuration/reference/release-build.html +2 -0
  121. data/site-local/configuration/reference/tools.html +9 -0
  122. data/site-local/configuration/which-ceedling.html +377 -17
  123. data/site-local/development/releases.html +5 -1
  124. data/site-local/development/workflow.html +48 -43
  125. data/site-local/getting-started/builds.html +2 -0
  126. data/site-local/getting-started/command-line.html +56 -4
  127. data/site-local/plugins/dependencies.html +6 -3
  128. data/site-local/plugins/fff.html +42 -11
  129. data/site-local/plugins/gcov/gcovr.html +15 -12
  130. data/site-local/plugins/gcov/setup.html +2 -1
  131. data/site-local/plugins/report-tests-raw-output-log.html +3 -1
  132. data/site-local/plugins/report-tests-teamcity-stdout.html +6 -3
  133. data/site-local/plugins/sanitize.html +7 -6
  134. data/site-local/plugins/valgrind.html +8 -7
  135. data/site-local/project/upgrade.html +3 -0
  136. data/site-local/reference/command-line.html +9 -2
  137. data/site-local/sitemap.xml.gz +0 -0
  138. data/site-local/snapshot/plugins/bullseye/config/defaults_bullseye.rb +1 -1
  139. data/site-local/snapshot/plugins/gcov/config/defaults_gcov.rb +1 -1
  140. data/site-local/testing-guide/conventions.html +53 -0
  141. data/spec/integration/actionator_file_operations_spec.rb +91 -1
  142. data/spec/integration/compile_commands_json_db_spec.rb +1 -1
  143. data/spec/integration/configurator_validation_spec.rb +1 -1
  144. data/spec/integration/dependencies/dependency_tracker_cache_hardening_spec.rb +26 -0
  145. data/spec/integration/dependencies/dependency_tracker_core_spec.rb +22 -0
  146. data/spec/integration/launcher_handoff_spec.rb +297 -0
  147. data/spec/integration/mixin_loading_spec.rb +1 -1
  148. data/spec/integration/module_generator_spec.rb +3 -3
  149. data/spec/integration/nested_test_artifacts_spec.rb +99 -0
  150. data/spec/integration/partials_module_resolution_spec.rb +7 -4
  151. data/spec/integration/path_intake_scenarios_spec.rb +101 -0
  152. data/spec/integration/path_scenarios_spec.rb +322 -0
  153. data/spec/integration/quote_include_isolator_spec.rb +1 -1
  154. data/spec/integration/report_build_warnings_log_spec.rb +1 -1
  155. data/spec/integration/report_tests_log_factory_spec.rb +1 -1
  156. data/spec/integration/test_executable_manifest_spec.rb +1 -1
  157. data/spec/integration/test_includes_extractinator_spec.rb +2 -1
  158. data/spec/integration/test_naming_spec.rb +198 -0
  159. data/spec/integration/test_source_file_checks_spec.rb +166 -0
  160. data/spec/integration/tool_validator_spec.rb +1 -1
  161. data/spec/integration/upgrade_docs_spec.rb +84 -0
  162. data/spec/integration/vendor_copy_transient_io_retry_spec.rb +74 -5
  163. data/spec/support/file_tree_helper.rb +88 -0
  164. data/spec/support/integration/configurator_integration_helper.rb +6 -6
  165. data/spec/support/integration/dependency_tracker_integration_helper.rb +1 -1
  166. data/spec/support/integration/partials_generation_integration_helper.rb +2 -2
  167. data/spec/support/integration/path_scenario_harness.rb +173 -0
  168. data/spec/support/integration/spec_integration_helper.rb +1 -1
  169. data/spec/support/integration/test_includes_harness.rb +13 -2
  170. data/spec/support/spec_helper.rb +2 -0
  171. data/spec/support/system/spec_system_helper.rb +7 -6
  172. data/spec/support/system/system_context.rb +21 -3
  173. data/spec/support/working_dir_tracking.rb +49 -0
  174. data/spec/system/build_output_mirroring_spec.rb +2 -2
  175. data/spec/system/conditional_mock_includes_spec.rb +1 -1
  176. data/spec/system/delta_builds_spec.rb +137 -11
  177. data/spec/system/gcov_deployment_spec.rb +1 -0
  178. data/spec/system/header_include_path_validation_spec.rb +4 -4
  179. data/spec/system/launcher_handoff_spec.rb +82 -0
  180. data/spec/system/mock_include_folders_spec.rb +91 -0
  181. data/spec/system/parent_directory_reference_spec.rb +5 -3
  182. data/spec/system/path_handling_probes_spec.rb +192 -0
  183. data/spec/system/preprocessing_parameterized_tests_spec.rb +1 -1
  184. data/spec/system/rake_release_task_path_disambiguation_spec.rb +6 -6
  185. data/spec/system/rake_test_task_path_disambiguation_spec.rb +85 -0
  186. data/spec/system/special_path_characters_spec.rb +86 -0
  187. data/spec/system/support/common_test_cases.rb +23 -21
  188. data/spec/system/support/gcov_common_test_cases.rb +37 -9
  189. data/spec/system/support/sanitize_common_test_cases.rb +2 -2
  190. data/spec/system/support/valgrind_common_test_cases.rb +12 -12
  191. data/spec/system/test_pipeline_stop_points_spec.rb +4 -4
  192. data/spec/system/test_runner_system_include_spec.rb +1 -1
  193. data/spec/system/test_source_file_directive_spec.rb +34 -5
  194. data/spec/units/bin/actionator_spec.rb +14 -17
  195. data/spec/units/bin/app_cfg_spec.rb +37 -0
  196. data/spec/units/bin/cli_handler_spec.rb +282 -6
  197. data/spec/units/bin/cli_helper_spec.rb +116 -45
  198. data/spec/units/bin/installation_selector_spec.rb +114 -0
  199. data/spec/units/bin/launchinator_spec.rb +171 -0
  200. data/spec/units/bin/path_validator_spec.rb +10 -0
  201. data/spec/units/bin/versionator_spec.rb +38 -6
  202. data/spec/units/build_log_writer_spec.rb +57 -0
  203. data/spec/units/config/build_root_policy_spec.rb +61 -0
  204. data/spec/units/config/configurator_builder_spec.rb +37 -6
  205. data/spec/units/config/configurator_paths_validator_spec.rb +292 -0
  206. data/spec/units/config/configurator_setup_spec.rb +9 -65
  207. data/spec/units/config/configurator_spec.rb +27 -5
  208. data/spec/units/config/configurator_validator_spec.rb +1 -172
  209. data/spec/units/default_tools_quoting_spec.rb +83 -0
  210. data/spec/units/dependencies/dependency_cache_store_spec.rb +33 -2
  211. data/spec/units/dependencies/dependency_debug_tree_spec.rb +1 -1
  212. data/spec/units/dependencies/dependency_hasher_spec.rb +42 -1
  213. data/spec/units/dependencies/dependency_path_normalizer_spec.rb +1 -1
  214. data/spec/units/dependencies/dependency_tracker_spec.rb +3 -1
  215. data/spec/units/filename_extension_spec.rb +11 -0
  216. data/spec/units/filesystem/build_subpath_spec.rb +122 -0
  217. data/spec/units/filesystem/destination_collision_spec.rb +30 -0
  218. data/spec/units/{file_finder_helper_spec.rb → filesystem/file_finder_helper_spec.rb} +46 -1
  219. data/spec/units/{file_finder_spec.rb → filesystem/file_finder_spec.rb} +188 -22
  220. data/spec/units/{file_path_collection_utils_spec.rb → filesystem/file_path_collection_utils_spec.rb} +181 -1
  221. data/spec/units/{file_path_utils_spec.rb → filesystem/file_path_utils_spec.rb} +204 -87
  222. data/spec/units/filesystem/file_system_wrapper_spec.rb +33 -0
  223. data/spec/units/filesystem/file_tree_spec.rb +511 -0
  224. data/spec/units/filesystem/file_tree_windows_spec.rb +78 -0
  225. data/spec/units/{file_wrapper_spec.rb → filesystem/file_wrapper_spec.rb} +95 -12
  226. data/spec/units/{path_matcher_spec.rb → filesystem/path_matcher_spec.rb} +12 -1
  227. data/spec/units/{path_mirror_spec.rb → filesystem/path_mirror_spec.rb} +32 -4
  228. data/spec/units/filesystem/project_paths_spec.rb +147 -0
  229. data/spec/units/filesystem/working_dir_spec.rb +71 -0
  230. data/spec/units/generators/generator_spec.rb +12 -6
  231. data/spec/units/generators/generator_test_results_backtrace_spec.rb +50 -46
  232. data/spec/units/generators/generator_test_results_spec.rb +1 -1
  233. data/spec/units/handoff_spec.rb +205 -0
  234. data/spec/units/includes/include_factory_spec.rb +3 -3
  235. data/spec/units/includes/include_pathinator_spec.rb +46 -0
  236. data/spec/units/installation_spec.rb +228 -0
  237. data/spec/units/library_resolver_spec.rb +157 -0
  238. data/spec/units/mock_locator_spec.rb +76 -1
  239. data/spec/units/partials/partializer_config_spec.rb +7 -0
  240. data/spec/units/partials/partializer_spec.rb +1 -0
  241. data/spec/units/partials_manager_spec.rb +17 -0
  242. data/spec/units/plugins/bullseye_spec.rb +15 -2
  243. data/spec/units/plugins/console_reportinator_spec.rb +18 -20
  244. data/spec/units/plugins/dependencies_spec.rb +33 -7
  245. data/spec/units/plugins/dependency_spec_spec.rb +17 -0
  246. data/spec/units/plugins/gcov_spec.rb +28 -3
  247. data/spec/units/plugins/gcovr_reportinator_spec.rb +19 -47
  248. data/spec/units/plugins/gcovr_thresholds_spec.rb +95 -0
  249. data/spec/units/plugins/module_generator_spec.rb +5 -5
  250. data/spec/units/plugins/report_build_warnings_log_spec.rb +3 -3
  251. data/spec/units/plugins/report_log_writer_plugin_spec.rb +10 -1
  252. data/spec/units/plugins/report_tests_raw_output_log_spec.rb +13 -2
  253. data/spec/units/plugins/report_tests_teamcity_stdout_spec.rb +77 -3
  254. data/spec/units/plugins/reportgenerator_reportinator_spec.rb +1 -1
  255. data/spec/units/plugins/sanitize_records_spec.rb +3 -1
  256. data/spec/units/plugins/sanitize_spec.rb +1 -1
  257. data/spec/units/plugins/valgrind_spec.rb +23 -8
  258. data/spec/units/preprocess/preprocessinator_comment_stripper_spec.rb +1 -1
  259. data/spec/units/rake_app/rakefile_component_resolver_spec.rb +21 -0
  260. data/spec/units/release_build_executor_spec.rb +53 -14
  261. data/spec/units/release_build_planner_spec.rb +15 -3
  262. data/spec/units/reportinator_spec.rb +5 -5
  263. data/spec/units/standin_headerinator_spec.rb +31 -1
  264. data/spec/units/test_build_executor_spec.rb +65 -3
  265. data/spec/units/test_build_planner_spec.rb +104 -2
  266. data/spec/units/test_build_setup_spec.rb +96 -12
  267. data/spec/units/test_context_extractor_spec.rb +25 -0
  268. data/spec/units/test_file_preprocessinator_spec.rb +17 -1
  269. data/spec/units/test_includes_extractinator_spec.rb +5 -1
  270. data/spec/units/test_invoker/objects_wiring_spec.rb +6 -3
  271. data/spec/units/test_invoker_spec.rb +14 -1
  272. data/spec/units/test_source_file_directive_resolver_spec.rb +38 -7
  273. data/spec/units/tool_executor_spec.rb +26 -0
  274. data/spec/units/yaml_wrapper_spec.rb +11 -0
  275. metadata +109 -25
  276. data/lib/ceedling/file_path_collection_utils.rb +0 -164
@@ -0,0 +1,92 @@
1
+ # =========================================================================
2
+ # Ceedling - Test-Centered Build System for C
3
+ # ThrowTheSwitch.org
4
+ # Copyright (c) 2010-26 Mike Karlesky, Mark VanderVoord, & Greg Williams
5
+ # SPDX-License-Identifier: MIT
6
+ # =========================================================================
7
+
8
+ require 'rbconfig'
9
+
10
+ # One Ceedling installation, identified by its root directory. Its version is read
11
+ # from its own files, never from the constants of whichever installation loaded first.
12
+ #
13
+ # An application loaded by an older launcher reaches this file through require_relative
14
+ # with someone else's code already loaded, so it depends on nothing else in lib/.
15
+ class Installation
16
+
17
+ VERSION_FILE = File.join( 'lib', 'version.rb' )
18
+ BUILTIN_MIXINS_DIR = File.join( 'vendor', 'unity', 'test', 'targets' )
19
+
20
+ attr_reader :root
21
+
22
+ def self.windows?
23
+ return (RbConfig::CONFIG['host_os'] =~ /mswin|mingw|msys|ucrt/i) ? true : false
24
+ end
25
+
26
+ def initialize(root)
27
+ @root = File.expand_path( root )
28
+ end
29
+
30
+ def bin_path
31
+ return File.join( @root, 'bin', 'ceedling' )
32
+ end
33
+
34
+ def rakefile_path
35
+ return File.join( @root, 'lib', 'ceedling', 'rakefile.rb' )
36
+ end
37
+
38
+ # The version tag, or nil when this installation's lib/version.rb is missing or unreadable
39
+ def version
40
+ @version = read_version() unless defined?( @version )
41
+ return @version
42
+ end
43
+
44
+ # True when this installation's launcher validates a built-in mixin directory the
45
+ # installation does not hold. `--local` vendoring left it out from 1.1.0 through 1.2.1,
46
+ # so such a copy fails on its own and only ever ran inside another launcher's process.
47
+ def missing_builtin_mixins?
48
+ mixins_filepath = File.join( @root, 'bin', 'mixins.rb' )
49
+ return false if !File.exist?( mixins_filepath )
50
+ return false if !File.read( mixins_filepath ).include?( 'BUILTIN_MIXIN_LOAD_PATHS' )
51
+
52
+ return !File.directory?( File.join( @root, BUILTIN_MIXINS_DIR ) )
53
+ end
54
+
55
+ # Symlinks resolve, and Windows paths compare without case
56
+ def ==(other)
57
+ return false unless other.is_a?( Installation )
58
+ return Installation.windows? ? identity.casecmp?( other.identity ) : (identity == other.identity)
59
+ end
60
+
61
+ # "Ceedling 1.2.0", or without the version when it cannot be read
62
+ def name(noun='Ceedling')
63
+ return [noun, version()].compact.join( ' ' )
64
+ end
65
+
66
+ # "Ceedling 1.2.0 at /path/", or without the version when it cannot be read
67
+ def describe(noun='Ceedling')
68
+ return "#{name( noun )} at #{@root}/"
69
+ end
70
+
71
+ protected
72
+
73
+ def identity
74
+ return File.realpath( @root )
75
+ rescue SystemCallError
76
+ return @root
77
+ end
78
+
79
+ private
80
+
81
+ # lib/version.rb defines Ceedling::Version. Evaluated within an anonymous module, its
82
+ # constants land beneath that module instead of reopening the loaded ::Ceedling.
83
+ def read_version
84
+ filepath = File.join( @root, VERSION_FILE )
85
+ sandbox = Module.new
86
+ sandbox.module_eval( File.read( filepath ), filepath )
87
+ return sandbox.const_get( :Ceedling, false ).const_get( :Version, false ).const_get( :TAG, false )
88
+ rescue StandardError, ScriptError
89
+ return nil
90
+ end
91
+
92
+ end
@@ -0,0 +1,99 @@
1
+ # =========================================================================
2
+ # Ceedling - Test-Centered Build System for C
3
+ # ThrowTheSwitch.org
4
+ # Copyright (c) 2010-26 Mike Karlesky, Mark VanderVoord, & Greg Williams
5
+ # SPDX-License-Identifier: MIT
6
+ # =========================================================================
7
+
8
+ # Finds the library files a link's :libraries entries name, so a link can depend on their
9
+ # contents. Which file a toolchain links is its own choice, so every candidate that exists
10
+ # is found. A library only the toolchain's own search paths hold is not found, and a link
11
+ # never depends on a file that doesn't exist, which would read as changed on every build.
12
+ class LibraryResolver
13
+
14
+ # Library extensions toolchains search for beyond a project's own :extension ↳ :libraries
15
+ TOOLCHAIN_EXTENSIONS = ['.a', '.so', '.dylib', '.tbd', '.dll.a', '.lib'].freeze
16
+
17
+ constructor :file_wrapper, :file_path_collection_utils
18
+
19
+ def setup
20
+ @lock = Mutex.new
21
+ @directories = {}
22
+ end
23
+
24
+ # entries: :libraries entries, each a bare name, an `-l:` filename, or a path
25
+ # search_paths: :paths ↳ :libraries, decorated and globbed as configured
26
+ # files: library files known exactly, as a plugin knows those it builds
27
+ def resolve(entries:, search_paths:, files: [])
28
+ return resolve_within( entries, @file_path_collection_utils.collect_paths( search_paths ), files )
29
+ end
30
+
31
+ # The library files a link with these :libraries entries depends on: those the entries
32
+ # resolve to on :paths ↳ :libraries, and those plugins publish in LIBRARIES_LINK_FILES
33
+ def link_files(entries)
34
+ return resolve_within( entries, search_directories(), ((defined? LIBRARIES_LINK_FILES) ? LIBRARIES_LINK_FILES : nil) || [] )
35
+ end
36
+
37
+ # A link's library search path arguments: each directory of search_directories in
38
+ # :libraries ↳ :path_flag
39
+ def search_path_arguments
40
+ directories = search_directories()
41
+ return directories unless defined?( LIBRARIES_PATH_FLAG )
42
+
43
+ return directories.map { |directory| LIBRARIES_PATH_FLAG.gsub( '${1}' ) { directory } }
44
+ end
45
+
46
+ # The directories :paths ↳ :libraries names, decorated and globbed as configured, with
47
+ # those plugins add once configuration is done. Collected once for a set of paths, since
48
+ # every link asks.
49
+ def search_directories
50
+ paths = ((defined? PATHS_LIBRARIES) ? PATHS_LIBRARIES : nil) || []
51
+ return @lock.synchronize { @directories[paths.dup] ||= @file_path_collection_utils.collect_paths( paths ) }
52
+ end
53
+
54
+ private
55
+
56
+ def resolve_within(entries, directories, files)
57
+ candidates = Array( entries ).flatten.compact.flat_map { |entry| candidates_for( entry.to_s, directories ) }
58
+ candidates += Array( files )
59
+
60
+ found = candidates.map { |path| @file_path_collection_utils.shortest_path_from_working( File.expand_path( path ) ) }
61
+ return found.select { |path| library_file?( path ) }.uniq
62
+ end
63
+
64
+ # An `-l:` entry names its exact filename, a path names itself, and a bare name names every
65
+ # library filename a toolchain makes of it
66
+ def candidates_for(entry, directories)
67
+ return within( directories, entry.delete_prefix( ':' ) ) if entry.start_with?( ':' )
68
+ return [entry] if absolute?( entry )
69
+ return [entry] + within( directories, entry ) if path?( entry )
70
+
71
+ return within( directories, *library_filenames( entry ) )
72
+ end
73
+
74
+ def within(directories, *names)
75
+ return directories.product( names ).map { |directory, name| File.join( directory, name ) }
76
+ end
77
+
78
+ def library_filenames(name)
79
+ return ["lib#{name}", name].product( extensions ).map( &:join )
80
+ end
81
+
82
+ # A path, or a filename with a library extension, rather than a name the linker expands
83
+ def path?(entry)
84
+ return entry.match?( %r{[/\\]} ) || extensions.any? { |extension| entry.end_with?( extension ) }
85
+ end
86
+
87
+ def absolute?(entry)
88
+ return File.expand_path( entry ) == entry || entry.match?( %r{\A[A-Za-z]:[/\\]} )
89
+ end
90
+
91
+ def extensions
92
+ @extensions ||= ((defined?( EXTENSION_LIBRARIES ) ? EXTENSION_LIBRARIES.to_a : []) + TOOLCHAIN_EXTENSIONS).uniq
93
+ end
94
+
95
+ def library_file?(path)
96
+ return @file_wrapper.exist?( path ) && !@file_wrapper.directory?( path )
97
+ end
98
+
99
+ end
@@ -87,6 +87,7 @@ configurator_setup:
87
87
  compose:
88
88
  - configurator_builder
89
89
  - configurator_validator
90
+ - configurator_paths_validator
90
91
  - loginator
91
92
  - reportinator
92
93
  - file_wrapper
@@ -101,11 +102,17 @@ configurator_plugins:
101
102
  configurator_validator:
102
103
  compose:
103
104
  - config_walkinator
104
- - file_wrapper
105
105
  - loginator
106
106
  - reportinator
107
107
  - tool_validator
108
108
 
109
+ configurator_paths_validator:
110
+ compose:
111
+ - config_walkinator
112
+ - file_wrapper
113
+ - loginator
114
+ - reportinator
115
+
109
116
  configurator_builder:
110
117
  compose:
111
118
  - file_path_collection_utils
@@ -148,6 +155,10 @@ plugin_reportinator_helper:
148
155
 
149
156
  verbosinator:
150
157
 
158
+ build_log_writer:
159
+ compose:
160
+ - file_wrapper
161
+
151
162
  file_finder:
152
163
  compose:
153
164
  - configurator
@@ -247,7 +258,7 @@ generator_test_results_backtrace:
247
258
  - generator_test_results
248
259
  - generator_helper
249
260
  - file_path_utils
250
- - file_wrapper
261
+ - build_log_writer
251
262
  - loginator
252
263
 
253
264
  generator_test_results_sanity_checker:
@@ -402,6 +413,7 @@ test_invoker:
402
413
  - loginator
403
414
  - verbosinator
404
415
  - test_pipeline_manager
416
+ - include_pathinator
405
417
 
406
418
  test_pipeline_manager:
407
419
  compose:
@@ -434,6 +446,7 @@ test_build_setup:
434
446
  - dependinator
435
447
  - test_search_paths
436
448
  - mock_locator
449
+ - test_source_file_directive_resolver
437
450
 
438
451
  test_includes_extractinator:
439
452
  compose:
@@ -559,6 +572,8 @@ test_build_executor:
559
572
  - gcc_dependency_parser
560
573
  - quote_include_isolator
561
574
  - generator_helper
575
+ - library_resolver
576
+ - mock_locator
562
577
 
563
578
  partials_manager:
564
579
  compose:
@@ -603,6 +618,12 @@ release_build_executor:
603
618
  - file_wrapper
604
619
  - file_path_utils
605
620
  - dependinator
621
+ - library_resolver
622
+
623
+ library_resolver:
624
+ compose:
625
+ - file_wrapper
626
+ - file_path_collection_utils
606
627
 
607
628
  gcc_dependency_parser:
608
629
 
@@ -14,7 +14,7 @@ require 'ceedling/c_extractor/c_extractor'
14
14
  require 'ceedling/c_extractor/c_extractor_constants'
15
15
  require 'ceedling/c_extractor/c_extractor_types'
16
16
  require 'ceedling/constants'
17
- require 'ceedling/path_mirror'
17
+ require 'ceedling/filesystem/path_mirror'
18
18
  require 'ceedling/encodinator'
19
19
 
20
20
  class Partializer
@@ -124,7 +124,7 @@ class Partializer
124
124
  def _find_module_source(_module, header_filepath)
125
125
  return @file_finder.find_source_file(_module, :ignore) if _names_directory?(_module)
126
126
 
127
- subdir = header_filepath.nil? ? '' : PathMirror.relative_subdir(header_filepath, _header_roots)
127
+ subdir = header_filepath.nil? ? '' : PathMirror.relative_subdir_from_clean_roots( header_filepath, _header_roots )
128
128
 
129
129
  unless subdir.empty?
130
130
  found = @file_finder.find_source_file(File.join(subdir, _module), :ignore)
@@ -135,9 +135,9 @@ class Partializer
135
135
  end
136
136
 
137
137
  # Roots a header can sit beneath, which is what its mirrored directory is measured
138
- # against. The same three the project-wide header collection is built from.
138
+ # against: those the project-wide header collection is built from, spelled canonically.
139
139
  def _header_roots
140
- @configurator.paths_test + @configurator.paths_support + @configurator.paths_include
140
+ @configurator.collection_mirror_roots_headers
141
141
  end
142
142
 
143
143
  # Either separator counts. The qualifier validation already rejects a backslash-rooted
@@ -9,6 +9,7 @@ require 'stringio'
9
9
  require 'strscan'
10
10
  require 'ceedling/partials/partials'
11
11
  require 'ceedling/exceptions'
12
+ require 'ceedling/filesystem/project_paths'
12
13
  require 'ceedling/c_extractor/c_extractor_preprocessing'
13
14
  require 'ceedling/encodinator'
14
15
 
@@ -259,7 +260,8 @@ class PartializerConfig
259
260
  _validate_qualifier!( dir, macro_name )
260
261
  _validate_module!( params[1], macro_name, dir: dir )
261
262
 
262
- File.join( dir, params[1] )
263
+ # A `.` segment names nothing, so `./drv` and `drv` name one module
264
+ File.join( *ProjectPaths.segments( dir ), params[1] )
263
265
  end
264
266
 
265
267
  # The module argument names one file's stem and nothing more. The preprocessor emits it
@@ -27,12 +27,10 @@ class ReportLogWriterPlugin < Plugin
27
27
 
28
28
  private
29
29
 
30
- # Ensures <build root>/artifacts/<context>/ exists and returns the full
31
- # filepath for `filename` inside it.
30
+ # Returns the filepath for `filename` beneath <build root>/artifacts/<context>/, with its
31
+ # directory made. A filename named for a test carries the test's subdirectories.
32
32
  def artifact_filepath(context, filename)
33
- path = File.join(PROJECT_BUILD_ARTIFACTS_ROOT, context.to_s)
34
- @file_wrapper.mkdir(path)
35
- File.join(path, filename)
33
+ @file_wrapper.mkdir_for( File.join(PROJECT_BUILD_ARTIFACTS_ROOT, context.to_s, filename) )
36
34
  end
37
35
 
38
36
  # Logs a heading, then either reports `empty_message` or runs the
@@ -6,7 +6,7 @@
6
6
  # =========================================================================
7
7
 
8
8
  require 'rake' # for ext() method
9
- require 'ceedling/file_wrapper'
9
+ require 'ceedling/filesystem/file_wrapper'
10
10
  require 'ceedling/encodinator'
11
11
  require 'ceedling/preprocess/c_preprocessor_conditionals'
12
12
 
@@ -6,7 +6,7 @@
6
6
  # =========================================================================
7
7
 
8
8
  require 'ceedling/includes/includes'
9
- require 'ceedling/path_matcher'
9
+ require 'ceedling/filesystem/path_matcher'
10
10
  require 'ceedling/preprocess/preprocessinator_bare_includes_extractor'
11
11
  require 'ceedling/preprocess/preprocessinator_line_marker_includes_extractor'
12
12
  require 'ceedling/preprocess/c_preprocessor_conditionals'
@@ -6,7 +6,7 @@
6
6
  # =========================================================================
7
7
 
8
8
  require 'ceedling/exceptions'
9
- require 'ceedling/path_matcher'
9
+ require 'ceedling/filesystem/path_matcher'
10
10
  require 'set'
11
11
  require 'stringio'
12
12
 
@@ -6,6 +6,7 @@
6
6
  # =========================================================================
7
7
 
8
8
  require 'ceedling/constants'
9
+ require 'ceedling/filesystem/project_paths'
9
10
 
10
11
  # Resolves the ordered list of .rake files Ceedling will load for a given project
11
12
  # configuration. Callable from both bin/ (early, before do_setup) and lib/ (within
@@ -39,8 +40,11 @@ module RakefileComponentResolver
39
40
  # plugins path last. This ordering lets user plugins shadow built-in plugins of the
40
41
  # same name. Used by both resolve() (bin/ CLI scope) and Configurator#prepare_plugins_load_paths
41
42
  # (application scope) so both scopes search plugins in the same priority order.
42
- def prepare_plugin_load_paths(config, ceedling_plugins_path)
43
- load_paths = Array( config.dig(:plugins, :load_paths) ).dup
43
+ #
44
+ # `expand` applies inline Ruby string expansion to each configured path, and each is then
45
+ # standardized as every other configured path is, so both scopes read them alike.
46
+ def prepare_plugin_load_paths(config, ceedling_plugins_path, expand: ->(path) { path })
47
+ load_paths = Array( config.dig(:plugins, :load_paths) ).compact.map { |path| ProjectPaths.standardize( expand.call( path ) ) }
44
48
  load_paths << ceedling_plugins_path
45
49
  load_paths.compact!
46
50
  load_paths.uniq!
@@ -51,7 +55,7 @@ module RakefileComponentResolver
51
55
  # - Stock files (always)
52
56
  # - Conditional stock files (rules_release, tasks_release if release_build is enabled)
53
57
  # - Plugin .rake files (first matching <root>/<plugin>/<plugin>.rake across load_paths)
54
- def resolve(config, ceedling_lib_path, ceedling_plugins_path)
58
+ def resolve(config, ceedling_lib_path, ceedling_plugins_path, expand: ->(path) { path })
55
59
  paths = base_rakefiles(ceedling_lib_path) + test_rakefiles(ceedling_lib_path)
56
60
 
57
61
  if config.dig(:project, :release_build)
@@ -59,7 +63,7 @@ module RakefileComponentResolver
59
63
  end
60
64
 
61
65
  paths.concat(
62
- plugin_rake_files( config, prepare_plugin_load_paths(config, ceedling_plugins_path) )
66
+ plugin_rake_files( config, prepare_plugin_load_paths(config, ceedling_plugins_path, expand: expand) )
63
67
  )
64
68
 
65
69
  return paths
@@ -70,20 +74,9 @@ module RakefileComponentResolver
70
74
  # Uses File.exist? directly (no DI needed) — same check as ConfiguratorPlugins#find_rake_plugins.
71
75
  # Array() wraps nil gracefully so missing config keys don't raise.
72
76
  def plugin_rake_files(config, load_paths)
73
- enabled = Array( config.dig(:plugins, :enabled) )
74
- results = []
75
-
76
- enabled.each do |plugin|
77
- load_paths.each do |load_path|
78
- candidate = File.join(load_path, plugin, "#{plugin}.rake")
79
- if File.exist?(candidate)
80
- results << candidate
81
- break
82
- end
83
- end
77
+ return Array( config.dig(:plugins, :enabled) ).filter_map do |plugin|
78
+ load_paths.lazy.map { |load_path| File.join(load_path, plugin, "#{plugin}.rake") }.find { |candidate| File.exist?(candidate) }
84
79
  end
85
-
86
- results
87
80
  end
88
81
 
89
82
  end
@@ -5,6 +5,18 @@
5
5
  # SPDX-License-Identifier: MIT
6
6
  # =========================================================================
7
7
 
8
+ # A launcher before 1.2.2 loads this application into its own process, where its code
9
+ # mixes with this installation's. Before anything else loads, restart in this
10
+ # installation's own process. require_relative, because such a launcher's load path
11
+ # cannot reach files newer than itself.
12
+ require_relative 'handoff'
13
+ Handoff.complete_in_process(
14
+ app_root: File.expand_path( File.join( __dir__, '..', '..' ) ),
15
+ app_cfg: CEEDLING_APPCFG,
16
+ handoff_objects: CEEDLING_HANDOFF_OBJECTS,
17
+ env: ENV
18
+ )
19
+
8
20
  require 'fileutils'
9
21
 
10
22
  # Add Unity and CMock's Ruby code paths to $LOAD_PATH for runner generation and mocking
@@ -49,7 +61,9 @@ begin
49
61
  # 1. Add full path to $LOAD_PATH to simplify objects.yml
50
62
  # 2. Perform object construction + dependency injection
51
63
  # 3. Remove full path from $LOAD_PATH
52
- $LOAD_PATH.unshift( CEEDLING_APPCFG[:ceedling_lib_path] )
64
+ # The full path comes last, behind its subdirectories, so a file an older Ceedling left
65
+ # directly in lib/ceedling/ never stands in for one that has moved below it
66
+ $LOAD_PATH.push( CEEDLING_APPCFG[:ceedling_lib_path] )
53
67
  objects_filepath = File.join( CEEDLING_APPCFG[:ceedling_lib_path], 'objects.yml' )
54
68
 
55
69
  # Create object hash and dependency injection context
@@ -6,7 +6,7 @@
6
6
  # =========================================================================
7
7
 
8
8
  require 'ceedling/constants'
9
- require 'ceedling/file_path_utils'
9
+ require 'ceedling/filesystem/file_path_utils'
10
10
 
11
11
  # Set Rake verbosity using global constant verbosity set before Rake is loaded
12
12
  if !!defined?(PROJECT_VERBOSITY)
@@ -21,11 +21,9 @@ namespace RELEASE_SYM do
21
21
  # Use rules to increase efficiency for large projects (instead of iterating through all sources and creating defined tasks)
22
22
 
23
23
  # Unadvertised Rake task to execute source file compilation in isolation.
24
- # A source extension can be configured as more than one string, so each one escaped
25
- # and joined into the pattern's alternation is what lets a task name ending in any of
26
- # them match, rather than only the first/primary one.
27
- release_source_extensions = EXTENSION_SOURCE.to_a.map { |ext| Regexp.escape(ext) }.join('|')
28
- rule(/^#{RELEASE_COMPILE_TASK_ROOT}\S+(?:#{release_source_extensions}|#{Regexp.escape(EXTENSION_CORE_SOURCE)})$/ => [ # compile task names by regex
24
+ # A source extension can be configured as more than one string, so a task name ending in
25
+ # any of them matches, rather than only the first/primary one.
26
+ rule(/^#{RELEASE_COMPILE_TASK_ROOT}\S+(?:#{EXTENSION_SOURCE.pattern}|#{Regexp.escape(EXTENSION_CORE_SOURCE)})$/ => [ # compile task names by regex
29
27
  proc do |task_name|
30
28
  source = task_name.sub(/#{RELEASE_COMPILE_TASK_ROOT}/, '')
31
29
  @ceedling[:file_finder].find_source_file(source)
@@ -37,8 +35,7 @@ namespace RELEASE_SYM do
37
35
 
38
36
  # Unadvertised Rake task to execute source file assembly in isolation
39
37
  if (RELEASE_BUILD_USE_ASSEMBLY)
40
- release_assembly_extensions = EXTENSION_ASSEMBLY.to_a.map { |ext| Regexp.escape(ext) }.join('|')
41
- rule(/^#{RELEASE_ASSEMBLE_TASK_ROOT}\S+(?:#{release_assembly_extensions})$/ => [ # assemble task names by regex
38
+ rule(/^#{RELEASE_ASSEMBLE_TASK_ROOT}\S+#{EXTENSION_ASSEMBLY.pattern}$/ => [ # assemble task names by regex
42
39
  proc do |task_name|
43
40
  source = task_name.sub(/#{RELEASE_ASSEMBLE_TASK_ROOT}/, '')
44
41
  @ceedling[:file_finder].find_assembly_file(source)
@@ -6,6 +6,7 @@
6
6
  # =========================================================================
7
7
 
8
8
  require 'ceedling/constants'
9
+ require 'ceedling/filename_extension'
9
10
  require 'ceedling/release_invoker/release_invoker_types'
10
11
 
11
12
  # Does the actual work of a release build: compiling objects, linking the
@@ -24,7 +25,8 @@ class ReleaseBuildExecutor
24
25
  :file_finder,
25
26
  :file_wrapper,
26
27
  :file_path_utils,
27
- :dependinator
28
+ :dependinator,
29
+ :library_resolver
28
30
  )
29
31
 
30
32
  def compile_objects(state)
@@ -44,17 +46,22 @@ class ReleaseBuildExecutor
44
46
  def link(state)
45
47
  objects, libraries = sort_objects_and_libraries( state.objects )
46
48
  lib_args = convert_libraries_to_arguments( libraries )
47
- lib_paths = get_library_paths_to_arguments()
49
+ lib_paths = @library_resolver.search_path_arguments()
50
+ library_files = @library_resolver.link_files( release_library_entries( libraries ) )
48
51
  target = @configurator.project_release_build_target
49
52
 
53
+ # As with a test link, the object and library lists as meta relink an artifact that a
54
+ # dropped source or library would otherwise leave holding its code
50
55
  @dependinator.register(
51
56
  target,
52
- files: objects,
53
- meta: { flags: state.link_flags, lib_args: lib_args, lib_paths: lib_paths, tools: [@configurator.tools_release_linker] }
57
+ files: objects + library_files,
58
+ meta: { flags: state.link_flags, lib_args: lib_args, lib_paths: lib_paths, tools: [@configurator.tools_release_linker], objects: objects, libraries: library_files }
54
59
  )
55
60
  stale = @dependinator.stale?( target )
56
61
 
57
62
  if stale
63
+ # A target named by an absolute :output may sit in a directory no build made
64
+ @file_wrapper.mkdir( File.dirname( target ) )
58
65
  @generator.generate_executable_file(
59
66
  @configurator.tools_release_linker,
60
67
  RELEASE_SYM,
@@ -192,13 +199,7 @@ class ReleaseBuildExecutor
192
199
  end
193
200
 
194
201
  def convert_libraries_to_arguments(libraries)
195
- args =
196
- (libraries || []) +
197
- ((defined? LIBRARIES_SYSTEM) ? LIBRARIES_SYSTEM : []) +
198
- ((defined? LIBRARIES_RELEASE) ? LIBRARIES_RELEASE : [])
199
-
200
- args.flatten!
201
- args.compact!
202
+ args = release_library_entries( libraries )
202
203
 
203
204
  if (defined? LIBRARIES_FLAG)
204
205
  args.map! { |v| LIBRARIES_FLAG.gsub(/\$\{1\}/, v) }
@@ -207,12 +208,15 @@ class ReleaseBuildExecutor
207
208
  return args
208
209
  end
209
210
 
210
- def get_library_paths_to_arguments()
211
- paths = (defined? PATHS_LIBRARIES) ? (PATHS_LIBRARIES || []).clone : []
212
- if (defined? LIBRARIES_PATH_FLAG)
213
- paths.map! { |v| LIBRARIES_PATH_FLAG.gsub(/\$\{1\}/, v) }
214
- end
215
- return paths
211
+ # The :libraries entries a release links, whatever form each takes, beside the library
212
+ # files its object list names
213
+ def release_library_entries(libraries)
214
+ entries =
215
+ (libraries || []) +
216
+ ((defined? LIBRARIES_SYSTEM) ? LIBRARIES_SYSTEM : []) +
217
+ ((defined? LIBRARIES_RELEASE) ? LIBRARIES_RELEASE : [])
218
+
219
+ return entries.flatten.compact
216
220
  end
217
221
 
218
222
  # A release's object list can include precompiled libraries/archives
@@ -221,35 +225,20 @@ class ReleaseBuildExecutor
221
225
  # arguments rather than as ordinary link inputs, so they're split out here
222
226
  # by file extension before linking.
223
227
  def sort_objects_and_libraries(both)
224
- # A library type may be named by several extensions at once (.a and .so, say), so its
225
- # regex alternation is built from however many are configured -- one entry needs no
226
- # alternation at all, several are joined into one. Enumerable is the test rather than
227
- # Array specifically, since a library-type extension is a FilenameExtension by the time
228
- # it reaches here, not a raw Array, even though both are iterable the same way.
229
- extension = if ((defined? EXTENSION_SUBPROJECTS) && (defined? EXTENSION_LIBRARIES))
230
- extension_libraries = if (EXTENSION_LIBRARIES.is_a? Enumerable)
231
- EXTENSION_LIBRARIES.to_a.join(")|(?:\\")
232
- else
233
- EXTENSION_LIBRARIES
234
- end
235
- "(?:\\#{EXTENSION_SUBPROJECTS})|(?:\\#{extension_libraries})"
236
- elsif (defined? EXTENSION_SUBPROJECTS)
237
- "\\#{EXTENSION_SUBPROJECTS}"
238
- elsif (defined? EXTENSION_LIBRARIES)
239
- if (EXTENSION_LIBRARIES.is_a? Enumerable)
240
- "(?:\\#{EXTENSION_LIBRARIES.to_a.join(")|(?:\\")})"
241
- else
242
- "\\#{EXTENSION_LIBRARIES}"
243
- end
244
- else
245
- "\\.LIBRARY"
246
- end
247
228
  # Compile once rather than once per entry in `both`
248
- library_pattern = /.+#{extension}$/
229
+ library_pattern = /.+#{library_endings()}$/
249
230
  sorted_objects = both.group_by { |v| v.match?( library_pattern ) ? :libraries : :objects }
250
- libraries = sorted_objects[:libraries] || []
251
- objects = sorted_objects[:objects] || []
252
- return objects, libraries
231
+ return (sorted_objects[:objects] || []), (sorted_objects[:libraries] || [])
232
+ end
233
+
234
+ # A library type may be named by several extensions at once (.a and .so, say), and a
235
+ # subproject's by its own
236
+ def library_endings
237
+ endings = []
238
+ endings << FilenameExtension.new( EXTENSION_SUBPROJECTS ).pattern if defined? EXTENSION_SUBPROJECTS
239
+ endings << FilenameExtension.new( EXTENSION_LIBRARIES ).pattern if defined? EXTENSION_LIBRARIES
240
+ endings << Regexp.escape( '.LIBRARY' ) if endings.empty?
241
+ return "(?:#{endings.join( '|' )})"
253
242
  end
254
243
 
255
244
  # States, in one line, how many targets a build step left untouched because nothing
@@ -34,15 +34,16 @@ class ReleaseBuildPlanner
34
34
  def plan(state, files: nil)
35
35
  tailor_release_tools()
36
36
 
37
- sources =
37
+ # A link-only name is no source of its own. Placed apart from the sources, one naming
38
+ # the object a source builds to joins it rather than colliding with it.
39
+ sources, link_only =
38
40
  if files.nil?
39
- @configurator.collection_release_build_input +
40
- @configurator.collection_release_artifact_extra_link_objects
41
+ [@configurator.collection_release_build_input, @configurator.collection_release_artifact_extra_link_objects]
41
42
  else
42
- files.dup
43
+ [files.dup, []]
43
44
  end
44
45
 
45
- state.objects = @file_path_utils.form_release_build_objects_filelist( sources )
46
+ state.objects = release_objects( sources, link_only )
46
47
  state.compile_flags = @flaginator.flag_down( context: RELEASE_SYM, operation: OPERATION_COMPILE_SYM )
47
48
  state.assemble_flags = @flaginator.flag_down( context: RELEASE_SYM, operation: OPERATION_ASSEMBLE_SYM )
48
49
  state.link_flags = @flaginator.flag_down( context: RELEASE_SYM, operation: OPERATION_LINK_SYM )
@@ -52,6 +53,13 @@ class ReleaseBuildPlanner
52
53
 
53
54
  private
54
55
 
56
+ def release_objects(sources, link_only)
57
+ objects = @file_path_utils.form_release_build_objects_filelist( sources ).to_a
58
+ return objects if link_only.empty?
59
+
60
+ return (objects + @file_path_utils.form_release_build_objects_filelist( link_only ).to_a).uniq
61
+ end
62
+
55
63
  # Shared libraries and static archives need compiler/linker argument tags
56
64
  # (-fPIC, -shared, or swapping the linker for `ar` outright) that a plain
57
65
  # executable target doesn't -- derived once per build from the release
@@ -73,7 +81,7 @@ class ReleaseBuildPlanner
73
81
  when '.a'
74
82
  compiler[:arguments] << '-fPIC' unless compiler[:arguments].include?('-fPIC')
75
83
  linker[:executable] = 'ar'
76
- linker[:arguments] = ['rcs', '${2}', '${1}']
84
+ linker[:arguments] = ['rcs', '"${2}"', '"${1}"']
77
85
  end
78
86
  end
79
87