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.
- checksums.yaml +4 -4
- data/GIT_COMMIT_SHA +1 -1
- data/README.md +11 -9
- data/Rakefile +2 -1
- data/assets/features/ceedling +1 -1
- data/assets/fixtures/test_source_file_duplicate_basenames/test_calc_typo.c +14 -0
- data/assets/fixtures/tests_with_duplicate_test_names/test_foo.c +21 -0
- data/bin/actionator.rb +6 -12
- data/bin/app_cfg.rb +14 -1
- data/bin/ceedling +13 -3
- data/bin/cli.rb +25 -6
- data/bin/cli_handler.rb +113 -73
- data/bin/cli_helper.rb +51 -78
- data/bin/installation_selector.rb +81 -0
- data/bin/launchinator.rb +69 -0
- data/bin/mixin_resolvinator.rb +26 -36
- data/bin/objects.yml +13 -0
- data/bin/path_validator.rb +25 -29
- data/bin/versionator.rb +47 -48
- data/ceedling.gemspec +19 -15
- data/docs/BreakingChanges.md +70 -1
- data/docs/Changelog.md +74 -3
- data/docs/KnownIssues.md +90 -4
- data/docs/ReleaseNotes.md +13 -0
- data/lib/ceedling/build_log_writer.rb +33 -0
- data/lib/ceedling/config/build_root_policy.rb +70 -0
- data/lib/ceedling/config/configurator.rb +14 -16
- data/lib/ceedling/config/configurator_builder.rb +58 -28
- data/lib/ceedling/config/configurator_paths_validator.rb +179 -0
- data/lib/ceedling/config/configurator_plugins.rb +1 -1
- data/lib/ceedling/config/configurator_setup.rb +15 -28
- data/lib/ceedling/config/configurator_validator.rb +1 -110
- data/lib/ceedling/defaults.rb +4 -4
- data/lib/ceedling/dependencies/README.md +18 -16
- data/lib/ceedling/dependencies/dependency_cache_store.rb +20 -3
- data/lib/ceedling/dependencies/dependency_hasher.rb +35 -1
- data/lib/ceedling/filename_extension.rb +7 -1
- data/lib/ceedling/filesystem/FILE_TREE.md +253 -0
- data/lib/ceedling/filesystem/PATH_HANDLING.md +304 -0
- data/lib/ceedling/filesystem/README.md +41 -0
- data/lib/ceedling/filesystem/build_subpath.rb +102 -0
- data/lib/ceedling/filesystem/destination_collision.rb +25 -0
- data/lib/ceedling/{file_finder.rb → filesystem/file_finder.rb} +159 -104
- data/lib/ceedling/{file_finder_helper.rb → filesystem/file_finder_helper.rb} +36 -31
- data/lib/ceedling/filesystem/file_path_collection_utils.rb +183 -0
- data/lib/ceedling/{file_path_utils.rb → filesystem/file_path_utils.rb} +108 -97
- data/lib/ceedling/{file_system_wrapper.rb → filesystem/file_system_wrapper.rb} +6 -5
- data/lib/ceedling/filesystem/file_tree.rb +241 -0
- data/lib/ceedling/{file_wrapper.rb → filesystem/file_wrapper.rb} +45 -38
- data/lib/ceedling/filesystem/path_exclusions.rb +30 -0
- data/lib/ceedling/{path_matcher.rb → filesystem/path_matcher.rb} +27 -23
- data/lib/ceedling/{path_mirror.rb → filesystem/path_mirror.rb} +19 -18
- data/lib/ceedling/filesystem/path_spellings.rb +55 -0
- data/lib/ceedling/filesystem/project_paths.rb +184 -0
- data/lib/ceedling/filesystem/working_dir.rb +40 -0
- data/lib/ceedling/generators/generator.rb +5 -6
- data/lib/ceedling/generators/generator_partials.rb +1 -1
- data/lib/ceedling/generators/generator_test_results_backtrace.rb +7 -7
- data/lib/ceedling/handoff.rb +121 -0
- data/lib/ceedling/includes/include_factory.rb +3 -3
- data/lib/ceedling/includes/include_pathinator.rb +67 -10
- data/lib/ceedling/includes/includes.rb +1 -1
- data/lib/ceedling/installation.rb +92 -0
- data/lib/ceedling/library_resolver.rb +99 -0
- data/lib/ceedling/objects.yml +23 -2
- data/lib/ceedling/partials/partializer.rb +4 -4
- data/lib/ceedling/partials/partializer_config.rb +3 -1
- data/lib/ceedling/plugins/report_log_writer_plugin.rb +3 -5
- data/lib/ceedling/preprocess/preprocessinator_file_assembler.rb +1 -1
- data/lib/ceedling/preprocess/preprocessinator_includes_handler.rb +1 -1
- data/lib/ceedling/preprocess/preprocessinator_line_marker_includes_extractor.rb +1 -1
- data/lib/ceedling/rake_app/rakefile_component_resolver.rb +10 -17
- data/lib/ceedling/rakefile.rb +15 -1
- data/lib/ceedling/rakefiles/base/tasks_base.rake +1 -1
- data/lib/ceedling/rakefiles/release/rules_release.rake +4 -7
- data/lib/ceedling/release_invoker/release_build_executor.rb +33 -44
- data/lib/ceedling/release_invoker/release_build_planner.rb +14 -6
- data/lib/ceedling/reportinator.rb +3 -4
- data/lib/ceedling/setupinator.rb +94 -90
- data/lib/ceedling/test_context_extractor.rb +5 -1
- data/lib/ceedling/test_invoker/PIPELINE.md +9 -7
- data/lib/ceedling/test_invoker/mock_locator.rb +60 -2
- data/lib/ceedling/test_invoker/partials_manager.rb +12 -0
- data/lib/ceedling/test_invoker/standin_headerinator.rb +22 -13
- data/lib/ceedling/test_invoker/test_build_executor.rb +24 -40
- data/lib/ceedling/test_invoker/test_build_planner.rb +37 -18
- data/lib/ceedling/test_invoker/test_build_setup.rb +17 -12
- data/lib/ceedling/test_invoker/test_build_validations.rb +16 -31
- data/lib/ceedling/test_invoker/test_file_preprocessinator.rb +1 -1
- data/lib/ceedling/test_invoker/test_invoker.rb +6 -1
- data/lib/ceedling/test_invoker/test_pipeline_helpers.rb +1 -1
- data/lib/ceedling/test_invoker/test_source_file_directive_resolver.rb +40 -20
- data/lib/ceedling/tool_executor.rb +9 -3
- data/lib/ceedling/yaml_wrapper.rb +3 -2
- data/lib/version.rb +1 -1
- data/plugins/bullseye/config/defaults_bullseye.rb +1 -1
- data/plugins/bullseye/lib/bullseye.rb +9 -7
- data/plugins/cppcheck/config/defaults_cppcheck.rb +1 -1
- data/plugins/dependencies/config/defaults.yml +6 -6
- data/plugins/dependencies/dependencies.rake +4 -1
- data/plugins/dependencies/lib/dependencies.rb +18 -8
- data/plugins/dependencies/lib/dependency_spec.rb +17 -5
- data/plugins/fff/lib/fff.rb +45 -39
- data/plugins/fff/spec/fff_cmock_wrapper_spec.rb +128 -0
- data/plugins/gcov/config/defaults_gcov.rb +1 -1
- data/plugins/gcov/lib/console_reportinator.rb +4 -18
- data/plugins/gcov/lib/gcov.rb +9 -2
- data/plugins/gcov/lib/gcovr_reportinator.rb +8 -40
- data/plugins/gcov/lib/gcovr_thresholds.rb +74 -0
- data/plugins/gcov/lib/reportgenerator_reportinator.rb +1 -1
- data/plugins/module_generator/lib/module_generator.rb +3 -3
- data/plugins/report_tests_teamcity_stdout/lib/report_tests_teamcity_stdout.rb +53 -80
- data/plugins/sanitize/lib/sanitize_records.rb +3 -6
- data/plugins/valgrind/lib/valgrind.rb +14 -7
- data/site-local/configuration/reference/cmock.html +2 -1
- data/site-local/configuration/reference/files.html +9 -3
- data/site-local/configuration/reference/libraries.html +21 -3
- data/site-local/configuration/reference/paths.html +29 -6
- data/site-local/configuration/reference/project.html +17 -7
- data/site-local/configuration/reference/release-build.html +2 -0
- data/site-local/configuration/reference/tools.html +9 -0
- data/site-local/configuration/which-ceedling.html +377 -17
- data/site-local/development/releases.html +5 -1
- data/site-local/development/workflow.html +48 -43
- data/site-local/getting-started/builds.html +2 -0
- data/site-local/getting-started/command-line.html +56 -4
- data/site-local/plugins/dependencies.html +6 -3
- data/site-local/plugins/fff.html +42 -11
- data/site-local/plugins/gcov/gcovr.html +15 -12
- data/site-local/plugins/gcov/setup.html +2 -1
- data/site-local/plugins/report-tests-raw-output-log.html +3 -1
- data/site-local/plugins/report-tests-teamcity-stdout.html +6 -3
- data/site-local/plugins/sanitize.html +7 -6
- data/site-local/plugins/valgrind.html +8 -7
- data/site-local/project/upgrade.html +3 -0
- data/site-local/reference/command-line.html +9 -2
- data/site-local/sitemap.xml.gz +0 -0
- data/site-local/snapshot/plugins/bullseye/config/defaults_bullseye.rb +1 -1
- data/site-local/snapshot/plugins/gcov/config/defaults_gcov.rb +1 -1
- data/site-local/testing-guide/conventions.html +53 -0
- data/spec/integration/actionator_file_operations_spec.rb +91 -1
- data/spec/integration/compile_commands_json_db_spec.rb +1 -1
- data/spec/integration/configurator_validation_spec.rb +1 -1
- data/spec/integration/dependencies/dependency_tracker_cache_hardening_spec.rb +26 -0
- data/spec/integration/dependencies/dependency_tracker_core_spec.rb +22 -0
- data/spec/integration/launcher_handoff_spec.rb +297 -0
- data/spec/integration/mixin_loading_spec.rb +1 -1
- data/spec/integration/module_generator_spec.rb +3 -3
- data/spec/integration/nested_test_artifacts_spec.rb +99 -0
- data/spec/integration/partials_module_resolution_spec.rb +7 -4
- data/spec/integration/path_intake_scenarios_spec.rb +101 -0
- data/spec/integration/path_scenarios_spec.rb +322 -0
- data/spec/integration/quote_include_isolator_spec.rb +1 -1
- data/spec/integration/report_build_warnings_log_spec.rb +1 -1
- data/spec/integration/report_tests_log_factory_spec.rb +1 -1
- data/spec/integration/test_executable_manifest_spec.rb +1 -1
- data/spec/integration/test_includes_extractinator_spec.rb +2 -1
- data/spec/integration/test_naming_spec.rb +198 -0
- data/spec/integration/test_source_file_checks_spec.rb +166 -0
- data/spec/integration/tool_validator_spec.rb +1 -1
- data/spec/integration/upgrade_docs_spec.rb +84 -0
- data/spec/integration/vendor_copy_transient_io_retry_spec.rb +74 -5
- data/spec/support/file_tree_helper.rb +88 -0
- data/spec/support/integration/configurator_integration_helper.rb +6 -6
- data/spec/support/integration/dependency_tracker_integration_helper.rb +1 -1
- data/spec/support/integration/partials_generation_integration_helper.rb +2 -2
- data/spec/support/integration/path_scenario_harness.rb +173 -0
- data/spec/support/integration/spec_integration_helper.rb +1 -1
- data/spec/support/integration/test_includes_harness.rb +13 -2
- data/spec/support/spec_helper.rb +2 -0
- data/spec/support/system/spec_system_helper.rb +7 -6
- data/spec/support/system/system_context.rb +21 -3
- data/spec/support/working_dir_tracking.rb +49 -0
- data/spec/system/build_output_mirroring_spec.rb +2 -2
- data/spec/system/conditional_mock_includes_spec.rb +1 -1
- data/spec/system/delta_builds_spec.rb +137 -11
- data/spec/system/gcov_deployment_spec.rb +1 -0
- data/spec/system/header_include_path_validation_spec.rb +4 -4
- data/spec/system/launcher_handoff_spec.rb +82 -0
- data/spec/system/mock_include_folders_spec.rb +91 -0
- data/spec/system/parent_directory_reference_spec.rb +5 -3
- data/spec/system/path_handling_probes_spec.rb +192 -0
- data/spec/system/preprocessing_parameterized_tests_spec.rb +1 -1
- data/spec/system/rake_release_task_path_disambiguation_spec.rb +6 -6
- data/spec/system/rake_test_task_path_disambiguation_spec.rb +85 -0
- data/spec/system/special_path_characters_spec.rb +86 -0
- data/spec/system/support/common_test_cases.rb +23 -21
- data/spec/system/support/gcov_common_test_cases.rb +37 -9
- data/spec/system/support/sanitize_common_test_cases.rb +2 -2
- data/spec/system/support/valgrind_common_test_cases.rb +12 -12
- data/spec/system/test_pipeline_stop_points_spec.rb +4 -4
- data/spec/system/test_runner_system_include_spec.rb +1 -1
- data/spec/system/test_source_file_directive_spec.rb +34 -5
- data/spec/units/bin/actionator_spec.rb +14 -17
- data/spec/units/bin/app_cfg_spec.rb +37 -0
- data/spec/units/bin/cli_handler_spec.rb +282 -6
- data/spec/units/bin/cli_helper_spec.rb +116 -45
- data/spec/units/bin/installation_selector_spec.rb +114 -0
- data/spec/units/bin/launchinator_spec.rb +171 -0
- data/spec/units/bin/path_validator_spec.rb +10 -0
- data/spec/units/bin/versionator_spec.rb +38 -6
- data/spec/units/build_log_writer_spec.rb +57 -0
- data/spec/units/config/build_root_policy_spec.rb +61 -0
- data/spec/units/config/configurator_builder_spec.rb +37 -6
- data/spec/units/config/configurator_paths_validator_spec.rb +292 -0
- data/spec/units/config/configurator_setup_spec.rb +9 -65
- data/spec/units/config/configurator_spec.rb +27 -5
- data/spec/units/config/configurator_validator_spec.rb +1 -172
- data/spec/units/default_tools_quoting_spec.rb +83 -0
- data/spec/units/dependencies/dependency_cache_store_spec.rb +33 -2
- data/spec/units/dependencies/dependency_debug_tree_spec.rb +1 -1
- data/spec/units/dependencies/dependency_hasher_spec.rb +42 -1
- data/spec/units/dependencies/dependency_path_normalizer_spec.rb +1 -1
- data/spec/units/dependencies/dependency_tracker_spec.rb +3 -1
- data/spec/units/filename_extension_spec.rb +11 -0
- data/spec/units/filesystem/build_subpath_spec.rb +122 -0
- data/spec/units/filesystem/destination_collision_spec.rb +30 -0
- data/spec/units/{file_finder_helper_spec.rb → filesystem/file_finder_helper_spec.rb} +46 -1
- data/spec/units/{file_finder_spec.rb → filesystem/file_finder_spec.rb} +188 -22
- data/spec/units/{file_path_collection_utils_spec.rb → filesystem/file_path_collection_utils_spec.rb} +181 -1
- data/spec/units/{file_path_utils_spec.rb → filesystem/file_path_utils_spec.rb} +204 -87
- data/spec/units/filesystem/file_system_wrapper_spec.rb +33 -0
- data/spec/units/filesystem/file_tree_spec.rb +511 -0
- data/spec/units/filesystem/file_tree_windows_spec.rb +78 -0
- data/spec/units/{file_wrapper_spec.rb → filesystem/file_wrapper_spec.rb} +95 -12
- data/spec/units/{path_matcher_spec.rb → filesystem/path_matcher_spec.rb} +12 -1
- data/spec/units/{path_mirror_spec.rb → filesystem/path_mirror_spec.rb} +32 -4
- data/spec/units/filesystem/project_paths_spec.rb +147 -0
- data/spec/units/filesystem/working_dir_spec.rb +71 -0
- data/spec/units/generators/generator_spec.rb +12 -6
- data/spec/units/generators/generator_test_results_backtrace_spec.rb +50 -46
- data/spec/units/generators/generator_test_results_spec.rb +1 -1
- data/spec/units/handoff_spec.rb +205 -0
- data/spec/units/includes/include_factory_spec.rb +3 -3
- data/spec/units/includes/include_pathinator_spec.rb +46 -0
- data/spec/units/installation_spec.rb +228 -0
- data/spec/units/library_resolver_spec.rb +157 -0
- data/spec/units/mock_locator_spec.rb +76 -1
- data/spec/units/partials/partializer_config_spec.rb +7 -0
- data/spec/units/partials/partializer_spec.rb +1 -0
- data/spec/units/partials_manager_spec.rb +17 -0
- data/spec/units/plugins/bullseye_spec.rb +15 -2
- data/spec/units/plugins/console_reportinator_spec.rb +18 -20
- data/spec/units/plugins/dependencies_spec.rb +33 -7
- data/spec/units/plugins/dependency_spec_spec.rb +17 -0
- data/spec/units/plugins/gcov_spec.rb +28 -3
- data/spec/units/plugins/gcovr_reportinator_spec.rb +19 -47
- data/spec/units/plugins/gcovr_thresholds_spec.rb +95 -0
- data/spec/units/plugins/module_generator_spec.rb +5 -5
- data/spec/units/plugins/report_build_warnings_log_spec.rb +3 -3
- data/spec/units/plugins/report_log_writer_plugin_spec.rb +10 -1
- data/spec/units/plugins/report_tests_raw_output_log_spec.rb +13 -2
- data/spec/units/plugins/report_tests_teamcity_stdout_spec.rb +77 -3
- data/spec/units/plugins/reportgenerator_reportinator_spec.rb +1 -1
- data/spec/units/plugins/sanitize_records_spec.rb +3 -1
- data/spec/units/plugins/sanitize_spec.rb +1 -1
- data/spec/units/plugins/valgrind_spec.rb +23 -8
- data/spec/units/preprocess/preprocessinator_comment_stripper_spec.rb +1 -1
- data/spec/units/rake_app/rakefile_component_resolver_spec.rb +21 -0
- data/spec/units/release_build_executor_spec.rb +53 -14
- data/spec/units/release_build_planner_spec.rb +15 -3
- data/spec/units/reportinator_spec.rb +5 -5
- data/spec/units/standin_headerinator_spec.rb +31 -1
- data/spec/units/test_build_executor_spec.rb +65 -3
- data/spec/units/test_build_planner_spec.rb +104 -2
- data/spec/units/test_build_setup_spec.rb +96 -12
- data/spec/units/test_context_extractor_spec.rb +25 -0
- data/spec/units/test_file_preprocessinator_spec.rb +17 -1
- data/spec/units/test_includes_extractinator_spec.rb +5 -1
- data/spec/units/test_invoker/objects_wiring_spec.rb +6 -3
- data/spec/units/test_invoker_spec.rb +14 -1
- data/spec/units/test_source_file_directive_resolver_spec.rb +38 -7
- data/spec/units/tool_executor_spec.rb +26 -0
- data/spec/units/yaml_wrapper_spec.rb +11 -0
- metadata +109 -25
- data/lib/ceedling/file_path_collection_utils.rb +0 -164
|
@@ -0,0 +1,183 @@
|
|
|
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 'set'
|
|
9
|
+
require 'pathname'
|
|
10
|
+
require 'fileutils'
|
|
11
|
+
require 'ceedling/filesystem/file_path_utils'
|
|
12
|
+
require 'ceedling/exceptions'
|
|
13
|
+
require 'ceedling/filesystem/project_paths'
|
|
14
|
+
require 'ceedling/filesystem/path_spellings'
|
|
15
|
+
require 'ceedling/filesystem/path_exclusions'
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class FilePathCollectionUtils
|
|
19
|
+
|
|
20
|
+
# One collected directory: its spelling through its entry's real base, both of its
|
|
21
|
+
# spellings, and how far below the nearest base of an entry naming it it lies
|
|
22
|
+
Named = Struct.new( :spelled, :spellings, :distance )
|
|
23
|
+
|
|
24
|
+
constructor :file_wrapper
|
|
25
|
+
|
|
26
|
+
# The directories one or more (+:/-:) paths and globs name, once each, spelled through
|
|
27
|
+
# each entry's base as the base really is, as an #include or a test name reaches them. A
|
|
28
|
+
# `-:` entry drops the directories it names and every directory below them, except one an
|
|
29
|
+
# additive entry naming it names nearer. Each path in `exclude` does so too, except where
|
|
30
|
+
# an entry naming a directory is based no farther away. Each path in `reserved`, and every
|
|
31
|
+
# directory below it, is never collected.
|
|
32
|
+
def collect_paths(paths, exclude: [], reserved: [])
|
|
33
|
+
# Nil input (e.g. unconfigured section) is treated as an empty list
|
|
34
|
+
return [] if paths.nil?
|
|
35
|
+
|
|
36
|
+
additive, subtractive = paths.partition { |entry| FilePathUtils.add_path?( entry ) }
|
|
37
|
+
minus = PathExclusions.of( directories_of( subtractive ), exclude, reserved )
|
|
38
|
+
|
|
39
|
+
kept = named_directories( additive ).select { |named| minus.keeps?( named.spellings, named.distance ) }
|
|
40
|
+
return kept.map { |named| ProjectPaths.relative( named.spelled ) }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Given a file list, add to it or remove from it considering (+:/-:) aggregation operators.
|
|
44
|
+
# Rake's FileList does not robustly handle relative filepaths and patterns.
|
|
45
|
+
# So, we rebuild the FileList ourselves and return it. A revision adds no file below a
|
|
46
|
+
# path in `exclude` unless the revision is based no farther away, and none below a path in
|
|
47
|
+
# `reserved` at all.
|
|
48
|
+
def revise_filelist(list, revisions, exclude: [], reserved: [])
|
|
49
|
+
# Nil list produces an empty FileList; nil revisions leaves the list unchanged
|
|
50
|
+
return FileList.new([]) if list.nil?
|
|
51
|
+
|
|
52
|
+
additive, subtractive = (revisions || []).partition { |revision| FilePathUtils.add_path?( revision ) }
|
|
53
|
+
kept = kept_files( list, additive, subtractive, PathExclusions.of( [], exclude, reserved ) )
|
|
54
|
+
|
|
55
|
+
# #104 -- every entry here is already a concrete, real file, but plain FileList.new or
|
|
56
|
+
# #include re-reads each as a glob, silently dropping a literal `[`/`]`.
|
|
57
|
+
# FileWrapper#instantiate_file_list_literal adds every entry as-is.
|
|
58
|
+
result = @file_wrapper.instantiate_file_list_literal( kept.map { |path| ProjectPaths.relative( path ) } )
|
|
59
|
+
result.resolve() # Force expansion to prevent race conditions in threaded context
|
|
60
|
+
return result
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# The places files below `entries` mirror below, each spelled canonically: the base of a
|
|
64
|
+
# `/**` or `/*` entry, every directory a glob within an entry names, or the entry itself.
|
|
65
|
+
# A `-:` exclusion roots nothing. A `[`/`]` is part of a name (#104), never a glob.
|
|
66
|
+
def collect_mirror_roots(entries)
|
|
67
|
+
return [] if entries.nil?
|
|
68
|
+
|
|
69
|
+
roots = entries.select { |entry| FilePathUtils.add_path?( entry ) }.flat_map { |entry| entry_roots( entry ) }
|
|
70
|
+
return roots.map { |root| ProjectPaths.canonical( root ) }.uniq
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# A path relative to the working directory, or absolute when it has no relative form
|
|
74
|
+
def shortest_path_from_working(path)
|
|
75
|
+
return ProjectPaths.relative( ProjectPaths.absolute( path ) )
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
private
|
|
79
|
+
|
|
80
|
+
# Every directory additive entries name, once each by its real spelling, with its distance
|
|
81
|
+
# below the nearest base of the entries naming it
|
|
82
|
+
def named_directories(entries)
|
|
83
|
+
named = {}
|
|
84
|
+
entries.each do |entry|
|
|
85
|
+
bases = PathSpellings.of( [FilePathUtils.no_decorators( entry )] )
|
|
86
|
+
entry_directories( entry ).each { |dir| name_directory( named, dir, bases ) }
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
return named.values
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Records `dir`, the first spelling of its real place kept, nearer if these bases are
|
|
93
|
+
def name_directory(named, dir, bases)
|
|
94
|
+
both = [dir, ProjectPaths.real_absolute( dir )]
|
|
95
|
+
found = (named[PathSpellings.key( both.last )] ||= Named.new( dir, both, Float::INFINITY ))
|
|
96
|
+
found.distance = [found.distance, PathSpellings.nearer( both, bases )].min
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# The listed files and those additive revisions name, less those `-:` revisions name, each
|
|
100
|
+
# compared through its directory as it really is
|
|
101
|
+
def kept_files(list, additive, subtractive, excluded)
|
|
102
|
+
real = {}
|
|
103
|
+
minus = Set.new( files_of( subtractive ).map { |path| real_key( path, real ) } )
|
|
104
|
+
added = added_files( additive, excluded, real )
|
|
105
|
+
return (list.map { |path| ProjectPaths.absolute( path ) } + added).uniq { |path| real_key( path, real ) }.reject { |path| minus.include?( real_key( path, real ) ) }
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# The files additive revisions name that `excluded` keeps, each judged by its distance
|
|
109
|
+
# below the base of the revision naming it
|
|
110
|
+
def added_files(additive, excluded, real)
|
|
111
|
+
return files_of( additive ) if excluded.none?
|
|
112
|
+
|
|
113
|
+
return additive.flat_map do |revision|
|
|
114
|
+
bases = PathSpellings.of( [FilePathUtils.no_decorators( revision )] )
|
|
115
|
+
revision_files( revision ).select do |file|
|
|
116
|
+
spellings = [file, real_spelling( file, real )]
|
|
117
|
+
excluded.keeps?( spellings, PathSpellings.nearer( spellings, bases ) )
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def directories_of(entries)
|
|
123
|
+
return entries.flat_map { |entry| entry_directories( entry ) }
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def files_of(revisions)
|
|
127
|
+
return revisions.flat_map { |revision| revision_files( revision ) }
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Every directory one entry names, absolute and spelled through its base as the base
|
|
131
|
+
# really is, so one place is never collected under two spellings of that base
|
|
132
|
+
def entry_directories(entry)
|
|
133
|
+
path = ProjectPaths.standardize( FilePathUtils.no_aggregation_decorators( entry ) )
|
|
134
|
+
dirs = listed_directories( path )
|
|
135
|
+
dirs = with_parents( dirs, path ) if path.end_with?( '/**', '/*' )
|
|
136
|
+
|
|
137
|
+
return PathSpellings.through_real( dirs.map { |dir| ProjectPaths.absolute( dir ) }, ProjectPaths.absolute( FilePathUtils.no_decorators( path ) ) )
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# #104 -- FilePathUtils.subdirectory_glob escapes a literal `[`/`]` in the path itself, so
|
|
141
|
+
# it's matched literally rather than read as glob syntax. Sorted for Github Issue #860.
|
|
142
|
+
def listed_directories(path)
|
|
143
|
+
entries = @file_wrapper.directory_listing( FilePathUtils.subdirectory_glob( path ) ).sort
|
|
144
|
+
return entries.select { |entry| @file_wrapper.directory?( entry ) }
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Ceedling's recursive glob convention includes the directory above each match, unlike
|
|
148
|
+
# Ruby's, and the base directory itself when nothing below it matches
|
|
149
|
+
def with_parents(dirs, path)
|
|
150
|
+
parents = dirs.map { |dir| File.join( dir, '..' ) }
|
|
151
|
+
parents << FilePathUtils.no_decorators( path ) if dirs.empty?
|
|
152
|
+
return parents + dirs
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def entry_roots(entry)
|
|
156
|
+
path = ProjectPaths.standardize( FilePathUtils.no_aggregation_decorators( entry ) ).sub( %r{(?:/\*\*?)+\z}, '' )
|
|
157
|
+
return [path] unless path.match?( /[*?{]/ )
|
|
158
|
+
|
|
159
|
+
listing = @file_wrapper.directory_listing( FilePathUtils.escape_glob_brackets( path ) )
|
|
160
|
+
return listing.sort.select { |listed| @file_wrapper.directory?( listed ) }
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# Every file one :files revision names, absolute and spelled through its base as the base
|
|
164
|
+
# really is. #104 -- a literal `[`/`]` is escaped as in `listed_directories`.
|
|
165
|
+
def revision_files(revision)
|
|
166
|
+
path = ProjectPaths.standardize( FilePathUtils.no_aggregation_decorators( revision ) )
|
|
167
|
+
files = @file_wrapper.directory_listing( FilePathUtils.escape_glob_brackets( path ) ).sort.reject { |entry| @file_wrapper.directory?( entry ) }
|
|
168
|
+
|
|
169
|
+
return PathSpellings.through_real( files.map { |file| ProjectPaths.absolute( file ) }, ProjectPaths.absolute( FilePathUtils.no_decorators( path ) ) )
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# A file compared through its directory as it really is, keeping its own name. Each
|
|
173
|
+
# directory is resolved once per call, in `real`.
|
|
174
|
+
def real_key(file, real)
|
|
175
|
+
return PathSpellings.key( real_spelling( file, real ) )
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def real_spelling(file, real)
|
|
179
|
+
dir = File.dirname( file )
|
|
180
|
+
return File.join( (real[dir] ||= ProjectPaths.real_absolute( dir )), File.basename( file ) )
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
end
|
|
@@ -8,10 +8,16 @@
|
|
|
8
8
|
require 'rubygems'
|
|
9
9
|
require 'rake' # for ext()
|
|
10
10
|
require 'fileutils'
|
|
11
|
+
require 'pathname'
|
|
11
12
|
require 'ceedling/exceptions'
|
|
13
|
+
require 'set'
|
|
12
14
|
require 'ceedling/system_wrapper'
|
|
13
15
|
require 'ceedling/constants'
|
|
14
|
-
require 'ceedling/path_mirror'
|
|
16
|
+
require 'ceedling/filesystem/path_mirror'
|
|
17
|
+
require 'ceedling/filesystem/path_matcher'
|
|
18
|
+
require 'ceedling/filesystem/project_paths'
|
|
19
|
+
require 'ceedling/filesystem/build_subpath'
|
|
20
|
+
require 'ceedling/filesystem/destination_collision'
|
|
15
21
|
|
|
16
22
|
# global utility methods (for plugins, project files, etc.)
|
|
17
23
|
def ceedling_form_filepath(destination_path, original_filepath, new_extension=nil)
|
|
@@ -24,6 +30,10 @@ class FilePathUtils
|
|
|
24
30
|
|
|
25
31
|
constructor :configurator, :file_wrapper
|
|
26
32
|
|
|
33
|
+
def setup
|
|
34
|
+
@formed_lock = Mutex.new
|
|
35
|
+
@formed_paths = Set.new
|
|
36
|
+
end
|
|
27
37
|
|
|
28
38
|
######### Class methods ##########
|
|
29
39
|
|
|
@@ -32,9 +42,7 @@ class FilePathUtils
|
|
|
32
42
|
def self.standardize_in_place(path)
|
|
33
43
|
if path.is_a? String
|
|
34
44
|
raise CeedlingException.new("Attempted to standardize path in frozen string ⏩️ #{path.inspect}") if path.frozen?
|
|
35
|
-
path.
|
|
36
|
-
path.tr!("\\", '/')
|
|
37
|
-
path.chomp!('/')
|
|
45
|
+
path.replace( ProjectPaths.standardize( path ) )
|
|
38
46
|
end
|
|
39
47
|
return path
|
|
40
48
|
end
|
|
@@ -149,31 +157,24 @@ class FilePathUtils
|
|
|
149
157
|
# ['a/b', 'a/c', 'a'] => ['a']
|
|
150
158
|
# ['src', 'lib/core', 'lib/utils'] => ['src', 'lib/core', 'lib/utils'] (unchanged)
|
|
151
159
|
#
|
|
152
|
-
#
|
|
153
|
-
#
|
|
160
|
+
# Paths compare as the directories they name (ProjectPaths.contains?), so `..` holds
|
|
161
|
+
# `../sib` but not `../../far/x`. Returned paths preserve the form the caller supplied.
|
|
154
162
|
def self.collapse_to_common_parents(paths)
|
|
155
163
|
return paths if paths.nil? || paths.length <= 1
|
|
156
164
|
|
|
157
|
-
#
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
# No tiebreaker for same-depth entries needed: Ruby's sort_by doesn't guarantee a
|
|
163
|
-
# stable order among ties, but two same-depth paths can never be each other's
|
|
164
|
-
# ancestor, so the ancestor-exclusion check below is correct regardless of which
|
|
165
|
-
# order same-depth ties come out in.
|
|
166
|
-
pairs.sort_by! { |_, normalized| normalized.count('/') }
|
|
167
|
-
|
|
168
|
-
kept = []
|
|
169
|
-
pairs.each do |original, normalized|
|
|
170
|
-
# Skip this path if any already-kept path is a proper ancestor of it.
|
|
171
|
-
next if kept.any? { |_, k| normalized.start_with?(k + '/') }
|
|
172
|
-
kept << [original, normalized]
|
|
173
|
-
end
|
|
165
|
+
# Shortest absolute spelling first, so an ancestor is always met before what it holds
|
|
166
|
+
pairs = paths.map { |path| [path, ProjectPaths.absolute( path )] }.sort_by { |_, absolute| absolute.length }
|
|
167
|
+
|
|
168
|
+
return outermost( pairs ).map { |original, _| original }
|
|
169
|
+
end
|
|
174
170
|
|
|
175
|
-
|
|
171
|
+
# The [original, absolute] pairs no earlier pair's directory holds
|
|
172
|
+
def self.outermost(pairs)
|
|
173
|
+
return pairs.each_with_object( [] ) do |pair, kept|
|
|
174
|
+
kept << pair unless kept.any? { |_, held| ProjectPaths.contains?( held, pair.last ) }
|
|
175
|
+
end
|
|
176
176
|
end
|
|
177
|
+
private_class_method :outermost
|
|
177
178
|
|
|
178
179
|
|
|
179
180
|
######### Instance methods ##########
|
|
@@ -192,12 +193,7 @@ class FilePathUtils
|
|
|
192
193
|
end
|
|
193
194
|
|
|
194
195
|
def form_release_build_objects_filelist(files)
|
|
195
|
-
return mirror_build_objects(
|
|
196
|
-
files,
|
|
197
|
-
root: @configurator.project_release_build_output_path,
|
|
198
|
-
ext: @configurator.extension_object.primary,
|
|
199
|
-
roots: @configurator.paths_source
|
|
200
|
-
)
|
|
196
|
+
return mirror_build_objects( files, root: @configurator.project_release_build_output_path, ext: @configurator.extension_object.primary )
|
|
201
197
|
end
|
|
202
198
|
|
|
203
199
|
# `filepath` is an already-mirrored object path -- its own directory is exactly where its
|
|
@@ -207,32 +203,34 @@ class FilePathUtils
|
|
|
207
203
|
end
|
|
208
204
|
|
|
209
205
|
def form_release_dependencies_filelist(files)
|
|
210
|
-
return mirror_build_objects(
|
|
211
|
-
files,
|
|
212
|
-
root: @configurator.project_release_dependencies_path,
|
|
213
|
-
ext: @configurator.extension_dependencies.primary,
|
|
214
|
-
roots: @configurator.paths_source
|
|
215
|
-
)
|
|
206
|
+
return mirror_build_objects( files, root: @configurator.project_release_dependencies_path, ext: @configurator.extension_dependencies.primary )
|
|
216
207
|
end
|
|
217
208
|
|
|
218
209
|
### Tests ###
|
|
219
210
|
|
|
211
|
+
# A test is named by its path from the project root, without its extension. The name places
|
|
212
|
+
# the test's build, results, runner, mock, preprocessing and log directories, and a path is
|
|
213
|
+
# unique, so no two tests share them. A test outside the project keeps its whole path too,
|
|
214
|
+
# with each `..` written as `_up_`, and a path with no relative form (another Windows drive)
|
|
215
|
+
# placed under `_abs_`, so no name reaches outside the build directory.
|
|
216
|
+
def form_test_name(filepath)
|
|
217
|
+
return BuildSubpath.encode( filepath.to_s.ext( '' ) )
|
|
218
|
+
end
|
|
219
|
+
|
|
220
220
|
def form_test_build_path(name, context: nil)
|
|
221
221
|
form_build_context_path(BUILD_OUT_DIR, name: name, context: context)
|
|
222
222
|
end
|
|
223
223
|
|
|
224
|
+
# Where a build places the object of `filepath`: below its test's build path, or below the
|
|
225
|
+
# context's own output root for a source compiled outside any test
|
|
224
226
|
def form_test_object_filepath(filepath, name: nil, context: nil)
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
File.basename(filepath).ext(@configurator.extension_object.primary)
|
|
228
|
-
)
|
|
227
|
+
root = form_build_context_path( BUILD_OUT_DIR, name: name, context: context )
|
|
228
|
+
return build_filepath( filepath, root: root, ext: @configurator.extension_object.primary, generated_roots: generated_roots( name ) )
|
|
229
229
|
end
|
|
230
230
|
|
|
231
|
-
# As `form_test_runners_path`: a
|
|
232
|
-
#
|
|
233
|
-
#
|
|
234
|
-
# results directories without perturbing the overwhelmingly common flat, non-duplicate
|
|
235
|
-
# case.
|
|
231
|
+
# As `form_test_runners_path`: a test's results sit beneath the directories of its name,
|
|
232
|
+
# so same-named tests in different directories keep apart. Only a test at the project
|
|
233
|
+
# root uses the results directory itself.
|
|
236
234
|
def form_test_results_path(name = nil, context: nil)
|
|
237
235
|
subdir = name.nil? ? '' : mirrored_test_subdir(name)
|
|
238
236
|
return form_build_context_path(BUILD_RESULTS_DIR, context: context) if subdir.empty?
|
|
@@ -253,51 +251,46 @@ class FilePathUtils
|
|
|
253
251
|
form_build_context_path(BUILD_DEPENDENCIES_DIR, name: name, context: context)
|
|
254
252
|
end
|
|
255
253
|
|
|
256
|
-
# `filepath` is an
|
|
257
|
-
#
|
|
258
|
-
#
|
|
259
|
-
# than colliding with another object's dependencies file at a shared flat basename.
|
|
260
|
-
# Without a test identity to recover that build root from, there's no mirroring to do --
|
|
261
|
-
# the dependencies file stays exactly where its context-level directory alone places it.
|
|
254
|
+
# `filepath` is an object path. Its dependencies file sits at the object's own place below
|
|
255
|
+
# the dependencies root, so it never collides with another same-named object's. Without a
|
|
256
|
+
# test, that place is below the context's own output root.
|
|
262
257
|
def form_test_dependencies_filepath(filepath, name: nil, context: nil)
|
|
263
258
|
root = form_build_context_path(BUILD_DEPENDENCIES_DIR, name: name, context: context)
|
|
264
259
|
basename = File.basename(filepath).ext(@configurator.extension_dependencies.primary)
|
|
265
260
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
subdir = PathMirror.relative_subdir( filepath, [form_test_build_path(name, context: context)] )
|
|
261
|
+
subdir = ProjectPaths.subdir_below( form_test_build_path( name, context: context ), File.dirname( filepath ) ) || ''
|
|
269
262
|
return subdir.empty? ? File.join(root, basename) : File.join(root, subdir, basename)
|
|
270
263
|
end
|
|
271
264
|
|
|
272
|
-
def form_test_mocks_path(name
|
|
265
|
+
def form_test_mocks_path(name)
|
|
273
266
|
form_named_path(@configurator.cmock_mock_path, name)
|
|
274
267
|
end
|
|
275
268
|
|
|
276
|
-
def form_test_partials_path(name
|
|
269
|
+
def form_test_partials_path(name)
|
|
277
270
|
form_named_path(@configurator.project_test_partials_path, name)
|
|
278
271
|
end
|
|
279
272
|
|
|
280
|
-
def form_test_preprocess_includes_path(name
|
|
273
|
+
def form_test_preprocess_includes_path(name)
|
|
281
274
|
form_named_path(@configurator.project_test_preprocess_includes_path, name)
|
|
282
275
|
end
|
|
283
276
|
|
|
284
|
-
def form_test_preprocess_files_path(name
|
|
277
|
+
def form_test_preprocess_files_path(name)
|
|
285
278
|
form_named_path(@configurator.project_test_preprocess_files_path, name)
|
|
286
279
|
end
|
|
287
280
|
|
|
288
|
-
def form_test_preprocess_files_full_expansion_path(name
|
|
281
|
+
def form_test_preprocess_files_full_expansion_path(name)
|
|
289
282
|
form_named_path(@configurator.project_test_preprocess_files_path, name, subdir: PREPROCESS_FULL_EXPANSION_DIR)
|
|
290
283
|
end
|
|
291
284
|
|
|
292
|
-
def form_test_preprocess_files_directives_only_path(name
|
|
285
|
+
def form_test_preprocess_files_directives_only_path(name)
|
|
293
286
|
form_named_path(@configurator.project_test_preprocess_files_path, name, subdir: PREPROCESS_DIRECTIVES_ONLY_DIR)
|
|
294
287
|
end
|
|
295
288
|
|
|
296
|
-
def form_test_preprocess_files_raw_directives_only_path(name
|
|
289
|
+
def form_test_preprocess_files_raw_directives_only_path(name)
|
|
297
290
|
form_named_path(@configurator.project_test_preprocess_files_path, name, subdir: PREPROCESS_RAW_DIRECTIVES_ONLY_DIR)
|
|
298
291
|
end
|
|
299
292
|
|
|
300
|
-
def form_test_preprocess_build_directives_path(name
|
|
293
|
+
def form_test_preprocess_build_directives_path(name)
|
|
301
294
|
form_named_path(@configurator.project_test_preprocess_build_directives_path, name)
|
|
302
295
|
end
|
|
303
296
|
|
|
@@ -323,12 +316,10 @@ class FilePathUtils
|
|
|
323
316
|
return File.join( build_output_path, File.basename(filepath).ext(@configurator.extension_testfail.primary) )
|
|
324
317
|
end
|
|
325
318
|
|
|
326
|
-
# A
|
|
327
|
-
#
|
|
328
|
-
#
|
|
329
|
-
|
|
330
|
-
# perturbing the overwhelmingly common flat, non-duplicate case.
|
|
331
|
-
def form_test_runners_path(name, context: nil)
|
|
319
|
+
# A test's runner sits beneath the directories of its name, so same-named tests in
|
|
320
|
+
# different directories keep apart. Only a test at the project root uses the runners
|
|
321
|
+
# directory itself.
|
|
322
|
+
def form_test_runners_path(name)
|
|
332
323
|
subdir = mirrored_test_subdir(name)
|
|
333
324
|
return @configurator.project_test_runners_path if subdir.empty?
|
|
334
325
|
return File.join(@configurator.project_test_runners_path, subdir)
|
|
@@ -373,13 +364,7 @@ class FilePathUtils
|
|
|
373
364
|
# the same filepath, so all of them agree on where the artifact lands without
|
|
374
365
|
# coordinating.
|
|
375
366
|
def preprocessed_file_subpath(filepath, test = nil)
|
|
376
|
-
|
|
377
|
-
@configurator.paths_support +
|
|
378
|
-
@configurator.paths_include +
|
|
379
|
-
@configurator.paths_test +
|
|
380
|
-
generated_roots( test )
|
|
381
|
-
|
|
382
|
-
subdir = PathMirror.relative_subdir( filepath, roots )
|
|
367
|
+
subdir = form_build_subpath( filepath, generated_roots: generated_roots( test ) )
|
|
383
368
|
basename = File.basename( filepath )
|
|
384
369
|
|
|
385
370
|
return basename if subdir.empty?
|
|
@@ -450,12 +435,7 @@ class FilePathUtils
|
|
|
450
435
|
# standard roots mirrors it. Without its root here, two modules sharing a basename
|
|
451
436
|
# collapse onto one object filename.
|
|
452
437
|
def form_test_build_objects_filelist(path, sources, extra_roots: [])
|
|
453
|
-
return mirror_build_objects(
|
|
454
|
-
sources,
|
|
455
|
-
root: path,
|
|
456
|
-
ext: @configurator.extension_object.primary,
|
|
457
|
-
roots: @configurator.paths_source + @configurator.paths_support + extra_roots
|
|
458
|
-
)
|
|
438
|
+
return mirror_build_objects( sources, root: path, ext: @configurator.extension_object.primary, generated_roots: extra_roots )
|
|
459
439
|
end
|
|
460
440
|
|
|
461
441
|
# Where one mock's generated files sit: its test's mock root, plus the subdirectory
|
|
@@ -546,13 +526,13 @@ class FilePathUtils
|
|
|
546
526
|
return form_partial_filename( _module, PARTIAL_SUFFIX_IMPL, EXTENSION_CORE_SOURCE )
|
|
547
527
|
end
|
|
548
528
|
|
|
529
|
+
# Each test's results file sits where `form_test_results_path` puts it, beneath its name
|
|
549
530
|
def form_pass_results_filelist(path, files)
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
)
|
|
531
|
+
results = @file_wrapper.instantiate_file_list_literal( files ).map do |file|
|
|
532
|
+
subdir = mirrored_test_subdir( form_test_name( file ) )
|
|
533
|
+
form_pass_results_filepath( subdir.empty? ? path : File.join( path, subdir ), file )
|
|
534
|
+
end
|
|
535
|
+
return @file_wrapper.instantiate_file_list_literal( results )
|
|
556
536
|
end
|
|
557
537
|
|
|
558
538
|
### Private ###
|
|
@@ -577,9 +557,8 @@ class FilePathUtils
|
|
|
577
557
|
return path
|
|
578
558
|
end
|
|
579
559
|
|
|
580
|
-
# The
|
|
581
|
-
#
|
|
582
|
-
# for a nested one (e.g. 'unit' for 'unit/test_foo').
|
|
560
|
+
# The directories of a test's name, excluding its basename: 'test/unit' for
|
|
561
|
+
# 'test/unit/test_foo', or '' for a test at the project root.
|
|
583
562
|
def mirrored_test_subdir(name)
|
|
584
563
|
dir = File.dirname(name)
|
|
585
564
|
return '' if dir == '.'
|
|
@@ -602,15 +581,47 @@ class FilePathUtils
|
|
|
602
581
|
# it contains a literal `[`/`]`. FileWrapper#instantiate_file_list_literal (`<<`, not
|
|
603
582
|
# `.new`/`.include`) adds every entry as-is, with no glob interpretation at all -- the
|
|
604
583
|
# correct construction for a list that's already the real, final answer.
|
|
605
|
-
def mirror_build_objects(files, root:, ext:,
|
|
606
|
-
|
|
584
|
+
def mirror_build_objects(files, root:, ext:, generated_roots: [])
|
|
585
|
+
sources = @file_wrapper.instantiate_file_list_literal( files ).to_a.uniq { |file| ProjectPaths.absolute( file ) }
|
|
586
|
+
objects = sources.map { |file| build_filepath( file, root: root, ext: ext, generated_roots: generated_roots ) }
|
|
607
587
|
|
|
608
|
-
objects
|
|
609
|
-
subdir = PathMirror.relative_subdir_from_clean_roots( file, clean_roots )
|
|
610
|
-
basename = File.basename(file).ext(ext)
|
|
611
|
-
subdir.empty? ? File.join(root, basename) : File.join(root, subdir, basename)
|
|
612
|
-
end
|
|
588
|
+
validate_distinct_build_files!( sources, objects )
|
|
613
589
|
return @file_wrapper.instantiate_file_list_literal( objects )
|
|
614
590
|
end
|
|
615
591
|
|
|
592
|
+
def build_filepath(file, root:, ext:, generated_roots:)
|
|
593
|
+
subdir = form_build_subpath( file, generated_roots: generated_roots )
|
|
594
|
+
basename = File.basename( file ).ext( ext )
|
|
595
|
+
path = subdir.empty? ? File.join( root, basename ) : File.join( root, subdir, basename )
|
|
596
|
+
|
|
597
|
+
# A source far from the project gains a directory for each level its path climbs. Each
|
|
598
|
+
# path is checked once, however often it is formed.
|
|
599
|
+
@file_wrapper.check_path_length( path, origin: 'FilePathUtils#build_filepath' ) if first_formed?( path )
|
|
600
|
+
return path
|
|
601
|
+
end
|
|
602
|
+
|
|
603
|
+
def first_formed?(path)
|
|
604
|
+
return @formed_lock.synchronize { @formed_paths.add?( path ) }
|
|
605
|
+
end
|
|
606
|
+
|
|
607
|
+
# Where below a build output root the build file of `file` lands. A file below one of the
|
|
608
|
+
# caller's generated roots (a test's mocks or Partials) mirrors below that root; one
|
|
609
|
+
# below the build root otherwise (vendored frameworks, runners) lands flat; and any other
|
|
610
|
+
# lands by its own path from the project root, so two sources never share one.
|
|
611
|
+
def form_build_subpath(file, generated_roots: [])
|
|
612
|
+
generated = PathMirror.relative_subdir_if_rooted_in_clean_roots( file, generated_roots )
|
|
613
|
+
return generated unless generated.nil?
|
|
614
|
+
return '' if ProjectPaths.contains?( @configurator.project_build_root, file )
|
|
615
|
+
|
|
616
|
+
return BuildSubpath.of_directory( File.dirname( file ) )
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
# Two sources building to one file would leave one of them out of the build
|
|
620
|
+
def validate_distinct_build_files!(sources, built)
|
|
621
|
+
built_from = sources.zip( built ).to_h
|
|
622
|
+
DestinationCollision.find( sources ) { |source| built_from[source] }.each do |file, sharing|
|
|
623
|
+
DestinationCollision.raise_for( file, sharing, 'would build to the same file' )
|
|
624
|
+
end
|
|
625
|
+
end
|
|
626
|
+
|
|
616
627
|
end
|
|
@@ -5,12 +5,13 @@
|
|
|
5
5
|
# SPDX-License-Identifier: MIT
|
|
6
6
|
# =========================================================================
|
|
7
7
|
|
|
8
|
+
require 'ceedling/filesystem/working_dir'
|
|
9
|
+
|
|
8
10
|
class FileSystemWrapper
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
end
|
|
12
|
+
# Through WorkingDir, so paths resolved inside the block resolve against `path`
|
|
13
|
+
def cd(path, &block)
|
|
14
|
+
return WorkingDir.chdir( path, &block )
|
|
14
15
|
end
|
|
15
16
|
|
|
16
|
-
end
|
|
17
|
+
end
|