Class: FilePathUtils

Inherits:
Object show all
Defined in:
lib/ceedling/file_path_utils.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.add_path?(path) ⇒ Boolean

Return whether the given path is to be aggregated (no aggregation modifier defaults to same as +:). nil is treated as an additive (non-excluding) path.

Returns:

  • (Boolean)


85
86
87
88
# File 'lib/ceedling/file_path_utils.rb', line 85

def self.add_path?(path)
  return true if path.nil?
  return !path.strip.start_with?('-:')
end

.collapse_to_common_parents(paths) ⇒ Object

Reduce a list of directory paths to the minimal ancestor set by removing any path that is already a descendant of another path in the list. This shortens command lines when a path list contains both a parent and a child directory.

Examples:

['src', 'src/platform', 'lib']   =>  ['src', 'lib']
['a/b', 'a/c', 'a']             =>  ['a']
['src', 'lib/core', 'lib/utils'] =>  ['src', 'lib/core', 'lib/utils']  (unchanged)

Comparison uses forward-slash-normalized copies; returned paths preserve the original form supplied by the caller.



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/ceedling/file_path_utils.rb', line 119

def self.collapse_to_common_parents(paths)
  return paths if paths.nil? || paths.length <= 1

  # Pair each original path with a normalized form used only for ancestry comparison.
  # Normalize to forward slashes and strip any trailing separator.
  pairs = paths.map { |p| [p, p.gsub('\\', '/').chomp('/')] }

  # Sort shallowest-first so ancestors are always encountered before their descendants.
  pairs.sort_by! { |_, normalized| normalized.count('/') }

  kept = []
  pairs.each do |original, normalized|
    # Skip this path if any already-kept path is a proper ancestor of it.
    next if kept.any? { |_, k| normalized.start_with?(k + '/') }
    kept << [original, normalized]
  end

  kept.map { |original, _| original }
end

.no_aggregation_decorators(path) ⇒ Object

Get path (and glob) stripping optional +: / -: prefixed aggregation modifiers. Strip surrounding whitespace before the regex so a decorator preceded by whitespace (e.g. ' -:foo') is handled consistently with add_path?, which also strips first.



93
94
95
96
# File 'lib/ceedling/file_path_utils.rb', line 93

def self.no_aggregation_decorators(path)
  return '' if path.nil?
  return path.strip.sub(/^(\+|-):/, '').strip()
end

.no_decorators(path) ⇒ Object

Extract path from between optional aggregation modifiers and up to last path separator before glob specifiers. Examples:

  • '+:foo/bar/baz/' => 'foo/bar/baz'
  • 'foo/bar/ba?' => 'foo/bar'
  • 'foo/bar/baz/' => 'foo/bar/baz'
  • 'foo/bar/baz/file.x' => 'foo/bar/baz/file.x'
  • 'foo/bar/baz/file*.x' => 'foo/bar/baz' NOTE: Input is assumed to use forward slashes; backslash paths must be normalized by caller.


55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/ceedling/file_path_utils.rb', line 55

def self.no_decorators(path)
  return '' if path.nil?

  path = self.no_aggregation_decorators(path)

  # Find first occurrence of glob specifier: *, ?, {, }, [, ]
  find_index = (path =~ PATTERNS::GLOB)

  # Return empty path if first character is part of a glob
  return '' if find_index == 0

  # If path contains no glob, clean it up and return whole path
  return path.chomp('/') if find_index.nil?

  # Extract up to first glob specifier
  path = path[0..(find_index-1)]

  # Keep everything up to and including the final path separator before the glob.
  # Three cases for the separator position:
  #   nil  — no separator at all (e.g. 'src*.c'): no usable directory prefix
  #   0    — separator is at position 0 (e.g. '/*.c'): root directory
  #   else — separator somewhere in the middle: slice off the trailing segment
  find_index = path.rindex('/')
  return '' if find_index.nil?
  return '/' if find_index == 0
  return path[0..(find_index-1)]
end

.os_executable_ext(executable) ⇒ Object



41
42
43
44
# File 'lib/ceedling/file_path_utils.rb', line 41

def self.os_executable_ext(executable)
  return executable.ext( EXTENSION_WIN_EXE ) if SystemWrapper.windows?
  return executable
end

.reform_subdirectory_glob(path) ⇒ Object

To recurse through all subdirectories, the Ruby glob is

//, but our paths use convenience convention of only /** at tail end of a path. Paths with ** at non-tail positions (e.g. foo/**/bar) are left unchanged by design.



101
102
103
104
105
106
# File 'lib/ceedling/file_path_utils.rb', line 101

def self.reform_subdirectory_glob(path)
  return '' if path.nil?
  return path if path.end_with?( '/**/**' )
  return path + '/**' if path.end_with?( '/**' )
  return path
end

.standardize_in_place(path) ⇒ Object

Standardize path to use '/' separator & have no trailing separator. Mutates in place. Frozen strings are a programming error at the call site — raises CeedlingException.



31
32
33
34
35
36
37
38
39
# File 'lib/ceedling/file_path_utils.rb', line 31

def self.standardize_in_place(path)
  if path.is_a? String
    raise CeedlingException.new("Attempted to standardize path in frozen string ⏩️ #{path.inspect}") if path.frozen?
    path.strip!
    path.gsub!(/\\/, '/')
    path.chomp!('/')
  end
  return path
end

Instance Method Details

#form_fail_results_filepath(build_output_path, filepath) ⇒ Object



237
238
239
# File 'lib/ceedling/file_path_utils.rb', line 237

def form_fail_results_filepath(build_output_path, filepath)
  return File.join( build_output_path, File.basename(filepath).ext(@configurator.extension_testfail) )
end

#form_mock_header_filepath(subdir, filename) ⇒ Object

Raises:



285
286
287
288
289
# File 'lib/ceedling/file_path_utils.rb', line 285

def form_mock_header_filepath(subdir, filename)
  # @configurator.cmock_mock_path accessor only exists if mocks are enabled
  raise CeedlingException.new('Mocks are not enabled, but an internal feature dependent on them was accessed.') unless @configurator.project_use_mocks
  return File.join(@configurator.cmock_mock_path, subdir, filename.ext(EXTENSION_CORE_HEADER))
end

#form_mock_partial_interface_header_filename(_module) ⇒ Object



306
307
308
# File 'lib/ceedling/file_path_utils.rb', line 306

def form_mock_partial_interface_header_filename(_module)
  return @configurator.cmock_mock_prefix + PARTIAL_FILENAME_PREFIX + _module + '_interface' + EXTENSION_CORE_HEADER
end

#form_mocks_source_filelist(path, mocks) ⇒ Object



291
292
293
294
# File 'lib/ceedling/file_path_utils.rb', line 291

def form_mocks_source_filelist(path, mocks)
  list = (@file_wrapper.instantiate_file_list(mocks))
  return list.map{ |file| File.join(path, File.basename(file).ext(EXTENSION_CORE_SOURCE)) }
end

#form_partial_header_filepath(subdir, filename) ⇒ Object

Raises:



296
297
298
299
300
# File 'lib/ceedling/file_path_utils.rb', line 296

def form_partial_header_filepath(subdir, filename)
  # @configurator.project_test_partials_path accessor only exists if partials are enabled
  raise CeedlingException.new('Partials are not enabled, but an internal feature dependent on them was accessed.') unless @configurator.project_use_partials
  return File.join( @configurator.project_test_partials_path, subdir, filename.ext(EXTENSION_CORE_HEADER) )
end

#form_partial_implementation_header_filename(_module) ⇒ Object



310
311
312
# File 'lib/ceedling/file_path_utils.rb', line 310

def form_partial_implementation_header_filename(_module)
  return PARTIAL_FILENAME_PREFIX + _module + '_impl' + EXTENSION_CORE_HEADER
end

#form_partial_implementation_source_filename(_module) ⇒ Object



314
315
316
# File 'lib/ceedling/file_path_utils.rb', line 314

def form_partial_implementation_source_filename(_module)
  return PARTIAL_FILENAME_PREFIX + _module + '_impl' + EXTENSION_CORE_SOURCE
end

#form_partial_interface_header_filename(_module) ⇒ Object



302
303
304
# File 'lib/ceedling/file_path_utils.rb', line 302

def form_partial_interface_header_filename(_module)
  return PARTIAL_FILENAME_PREFIX + _module + '_interface' + EXTENSION_CORE_HEADER
end

#form_pass_results_filelist(path, files) ⇒ Object



323
324
325
326
# File 'lib/ceedling/file_path_utils.rb', line 323

def form_pass_results_filelist(path, files)
  list = @file_wrapper.instantiate_file_list(files)
  return list.pathmap("#{path}/%n#{@configurator.extension_testpass}")
end

#form_pass_results_filepath(build_output_path, filepath) ⇒ Object



233
234
235
# File 'lib/ceedling/file_path_utils.rb', line 233

def form_pass_results_filepath(build_output_path, filepath)
  return File.join( build_output_path, File.basename(filepath).ext(@configurator.extension_testpass) )
end

#form_preprocessed_file_compacted_directives_only_filepath(filepath, subdir) ⇒ Object



277
278
279
# File 'lib/ceedling/file_path_utils.rb', line 277

def form_preprocessed_file_compacted_directives_only_filepath(filepath, subdir)
  return File.join( @configurator.project_test_preprocess_files_path, subdir, PREPROCESS_DIRECTIVES_ONLY_DIR, File.basename(filepath) )
end

#form_preprocessed_file_filepath(filepath, subdir) ⇒ Object



265
266
267
# File 'lib/ceedling/file_path_utils.rb', line 265

def form_preprocessed_file_filepath(filepath, subdir)
  return File.join( @configurator.project_test_preprocess_files_path, subdir, File.basename(filepath) )
end

#form_preprocessed_file_full_expansion_filepath(filepath, subdir) ⇒ Object



269
270
271
# File 'lib/ceedling/file_path_utils.rb', line 269

def form_preprocessed_file_full_expansion_filepath(filepath, subdir)
  return File.join( @configurator.project_test_preprocess_files_path, subdir, PREPROCESS_FULL_EXPANSION_DIR, File.basename(filepath) )
end

#form_preprocessed_file_raw_directives_only_filepath(filepath, subdir) ⇒ Object



273
274
275
# File 'lib/ceedling/file_path_utils.rb', line 273

def form_preprocessed_file_raw_directives_only_filepath(filepath, subdir)
  return File.join( @configurator.project_test_preprocess_files_path, subdir, PREPROCESS_RAW_DIRECTIVES_ONLY_DIR, File.basename(filepath) )
end

#form_preprocessed_includes_list_filepath(filepath, subdir) ⇒ Object



261
262
263
# File 'lib/ceedling/file_path_utils.rb', line 261

def form_preprocessed_includes_list_filepath(filepath, subdir)
  return File.join( @configurator.project_test_preprocess_includes_path, subdir, File.basename(filepath) + @configurator.extension_yaml )
end

#form_release_build_cache_path(filepath) ⇒ Object

Release ###



143
144
145
# File 'lib/ceedling/file_path_utils.rb', line 143

def form_release_build_cache_path(filepath)
  return File.join( @configurator.project_release_build_cache_path, File.basename(filepath) )
end

#form_release_build_list_filepath(filepath) ⇒ Object



155
156
157
# File 'lib/ceedling/file_path_utils.rb', line 155

def form_release_build_list_filepath(filepath)
  return File.join( @configurator.project_release_build_output_path, File.basename(filepath).ext(@configurator.extension_list) )
end

#form_release_build_objects_filelist(files) ⇒ Object



151
152
153
# File 'lib/ceedling/file_path_utils.rb', line 151

def form_release_build_objects_filelist(files)
  return (@file_wrapper.instantiate_file_list(files)).pathmap("#{@configurator.project_release_build_output_path}/%n#{@configurator.extension_object}")
end

#form_release_dependencies_filelist(files) ⇒ Object



159
160
161
# File 'lib/ceedling/file_path_utils.rb', line 159

def form_release_dependencies_filelist(files)
  return (@file_wrapper.instantiate_file_list(files)).pathmap("#{@configurator.project_release_dependencies_path}/%n#{@configurator.extension_dependencies}")
end

#form_release_dependencies_filepath(filepath) ⇒ Object



147
148
149
# File 'lib/ceedling/file_path_utils.rb', line 147

def form_release_dependencies_filepath(filepath)
  return File.join( @configurator.project_release_dependencies_path, File.basename(filepath).ext(@configurator.extension_dependencies) )
end

#form_runner_filepath_from_test(filepath) ⇒ Object



241
242
243
# File 'lib/ceedling/file_path_utils.rb', line 241

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 + EXTENSION_CORE_SOURCE
end

#form_test_build_cache_path(filepath) ⇒ Object



229
230
231
# File 'lib/ceedling/file_path_utils.rb', line 229

def form_test_build_cache_path(filepath)
  return File.join( @configurator.project_test_build_cache_path, File.basename(filepath) )
end

#form_test_build_list_filepath(filepath) ⇒ Object



257
258
259
# File 'lib/ceedling/file_path_utils.rb', line 257

def form_test_build_list_filepath(filepath)
  return File.join( @configurator.project_test_build_output_path, File.basename(filepath).ext(@configurator.extension_list) )
end

#form_test_build_map_filepath(build_output_path, filepath) ⇒ Object



253
254
255
# File 'lib/ceedling/file_path_utils.rb', line 253

def form_test_build_map_filepath(build_output_path, filepath)
  return File.join( build_output_path, File.basename(filepath).ext(@configurator.extension_map) )
end

#form_test_build_objects_filelist(path, sources) ⇒ Object



281
282
283
# File 'lib/ceedling/file_path_utils.rb', line 281

def form_test_build_objects_filelist(path, sources)
  return (@file_wrapper.instantiate_file_list(sources)).pathmap("#{path}/%n#{@configurator.extension_object}")
end

#form_test_build_path(name, context: nil) ⇒ Object

Tests ###



165
166
167
# File 'lib/ceedling/file_path_utils.rb', line 165

def form_test_build_path(name, context: nil)
  form_build_context_path(BUILD_OUT_DIR, name: name, context: context)
end

#form_test_dependencies_filelist(files) ⇒ Object



318
319
320
321
# File 'lib/ceedling/file_path_utils.rb', line 318

def form_test_dependencies_filelist(files)
  list = @file_wrapper.instantiate_file_list(files)
  return list.pathmap("#{@configurator.project_test_dependencies_path}/%n#{@configurator.extension_dependencies}")
end

#form_test_dependencies_filepath(filepath, name: nil, context: nil) ⇒ Object



194
195
196
197
198
199
# File 'lib/ceedling/file_path_utils.rb', line 194

def form_test_dependencies_filepath(filepath, name: nil, context: nil)
  File.join(
    form_build_context_path(BUILD_DEPENDENCIES_DIR, name: name, context: context),
    File.basename(filepath).ext(@configurator.extension_dependencies)
  )
end

#form_test_dependencies_path(name, context: nil) ⇒ Object



190
191
192
# File 'lib/ceedling/file_path_utils.rb', line 190

def form_test_dependencies_path(name, context: nil)
  form_build_context_path(BUILD_DEPENDENCIES_DIR, name: name, context: context)
end

#form_test_executable_filepath(build_output_path, filepath) ⇒ Object



249
250
251
# File 'lib/ceedling/file_path_utils.rb', line 249

def form_test_executable_filepath(build_output_path, filepath)
  return File.join( build_output_path, File.basename(filepath).ext(@configurator.extension_executable) )
end

#form_test_filepath_from_runner(filepath) ⇒ Object



245
246
247
# File 'lib/ceedling/file_path_utils.rb', line 245

def form_test_filepath_from_runner(filepath)
  return filepath.sub(/#{Regexp.escape(TEST_RUNNER_FILE_SUFFIX)}/, '')
end

#form_test_gdb_log(test, context:, name:) ⇒ Object

Forms the filepath for the gdb backtrace log for a given test case. Produces: <project_log_path>///.gdb.log



182
183
184
185
186
187
188
# File 'lib/ceedling/file_path_utils.rb', line 182

def form_test_gdb_log(test, context:, name:)
  parts = [@configurator.project_log_path]
  parts << context.to_s
  parts << test
  parts << "#{name}.gdb.log"
  File.join( *parts )
end

#form_test_mocks_path(name, context: nil) ⇒ Object



201
202
203
# File 'lib/ceedling/file_path_utils.rb', line 201

def form_test_mocks_path(name, context: nil)
  form_named_path(@configurator.cmock_mock_path, name)
end

#form_test_object_filepath(filepath, name: nil, context: nil) ⇒ Object



169
170
171
172
173
174
# File 'lib/ceedling/file_path_utils.rb', line 169

def form_test_object_filepath(filepath, name: nil, context: nil)
  File.join(
    form_build_context_path(BUILD_OUT_DIR, name: name, context: context),
    File.basename(filepath).ext(@configurator.extension_object)
  )
end

#form_test_partials_path(name, context: nil) ⇒ Object



205
206
207
# File 'lib/ceedling/file_path_utils.rb', line 205

def form_test_partials_path(name, context: nil)
  form_named_path(@configurator.project_test_partials_path, name)
end

#form_test_preprocess_files_directives_only_path(name, context: nil) ⇒ Object



221
222
223
# File 'lib/ceedling/file_path_utils.rb', line 221

def form_test_preprocess_files_directives_only_path(name, context: nil)
  form_named_path(@configurator.project_test_preprocess_files_path, name, subdir: PREPROCESS_DIRECTIVES_ONLY_DIR)
end

#form_test_preprocess_files_full_expansion_path(name, context: nil) ⇒ Object



217
218
219
# File 'lib/ceedling/file_path_utils.rb', line 217

def form_test_preprocess_files_full_expansion_path(name, context: nil)
  form_named_path(@configurator.project_test_preprocess_files_path, name, subdir: PREPROCESS_FULL_EXPANSION_DIR)
end

#form_test_preprocess_files_path(name, context: nil) ⇒ Object



213
214
215
# File 'lib/ceedling/file_path_utils.rb', line 213

def form_test_preprocess_files_path(name, context: nil)
  form_named_path(@configurator.project_test_preprocess_files_path, name)
end

#form_test_preprocess_files_raw_directives_only_path(name, context: nil) ⇒ Object



225
226
227
# File 'lib/ceedling/file_path_utils.rb', line 225

def form_test_preprocess_files_raw_directives_only_path(name, context: nil)
  form_named_path(@configurator.project_test_preprocess_files_path, name, subdir: PREPROCESS_RAW_DIRECTIVES_ONLY_DIR)
end

#form_test_preprocess_includes_path(name, context: nil) ⇒ Object



209
210
211
# File 'lib/ceedling/file_path_utils.rb', line 209

def form_test_preprocess_includes_path(name, context: nil)
  form_named_path(@configurator.project_test_preprocess_includes_path, name)
end

#form_test_results_path(name = nil, context: nil) ⇒ Object



176
177
178
# File 'lib/ceedling/file_path_utils.rb', line 176

def form_test_results_path(name = nil, context: nil)
  form_build_context_path(BUILD_RESULTS_DIR, context: context)
end