Skip to content

Commit

Permalink
Merge branch 'test/0_32/built-in-mixins' into test/ceedling_0_32_rc
Browse files Browse the repository at this point in the history
  • Loading branch information
mkarlesky committed Apr 26, 2024
2 parents e8eda7d + b088f84 commit 1262199
Show file tree
Hide file tree
Showing 8 changed files with 107 additions and 72 deletions.
6 changes: 3 additions & 3 deletions bin/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,9 @@ module CeedlingTasks

LONGDOC_MIXIN_FLAG = "`--mixin` merges the specified configuration mixin. This
flag may be repeated for multiple mixins. A simple mixin name initiates a
lookup from within mixin load paths in your project file and internally. A
filepath and/or filename (with extension) will instead merge the specified
YAML file. See documentation for complete details.
lookup from within mixin load paths in your project file and among built-in
mixins. A filepath and/or filename (with extension) will instead merge the
specified YAML file. See documentation for complete details.
\x5> --mixin my_compiler --mixin my/path/mixin.yml"

class CLI < Thor
Expand Down
8 changes: 5 additions & 3 deletions bin/cli_handler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
# SPDX-License-Identifier: MIT
# =========================================================================

require 'mixins' # Built-in Mixins
require 'ceedling/constants' # From Ceedling application

class CliHandler
Expand Down Expand Up @@ -153,7 +154,7 @@ def build(env:, app_cfg:, options:{}, tasks:)

@path_validator.standardize_paths( options[:project], options[:logfile], *options[:mixin] )

_, config = @configinator.loadinate( filepath:options[:project], mixins:options[:mixin], env:env )
_, config = @configinator.loadinate( builtin_mixins:BUILTIN_MIXINS, filepath:options[:project], mixins:options[:mixin], env:env )

default_tasks = @configinator.default_tasks( config:config, default_tasks:app_cfg[:default_tasks] )

Expand Down Expand Up @@ -214,7 +215,7 @@ def dumpconfig(env, app_cfg, options, filepath, sections)

@path_validator.standardize_paths( filepath, options[:project], *options[:mixin] )

_, config = @configinator.loadinate( filepath:options[:project], mixins:options[:mixin], env:env )
_, config = @configinator.loadinate( builtin_mixins:BUILTIN_MIXINS, filepath:options[:project], mixins:options[:mixin], env:env )

# Exception handling to ensure we dump the configuration regardless of config validation errors
begin
Expand Down Expand Up @@ -247,7 +248,7 @@ def environment(env, app_cfg, options)

@path_validator.standardize_paths( options[:project], *options[:mixin] )

_, config = @configinator.loadinate( filepath:options[:project], mixins:options[:mixin], env:env )
_, config = @configinator.loadinate( builtin_mixins:BUILTIN_MIXINS, filepath:options[:project], mixins:options[:mixin], env:env )

# Save references
app_cfg.set_project_config( config )
Expand Down Expand Up @@ -360,6 +361,7 @@ def version()
def list_rake_tasks(env:, app_cfg:, filepath:nil, mixins:[])
_, config =
@configinator.loadinate(
builtin_mixins:BUILTIN_MIXINS,
filepath: filepath,
mixins: mixins,
env: env
Expand Down
24 changes: 11 additions & 13 deletions bin/configinator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,9 @@

class Configinator

# TODO: Temporary path until built-in mixins load path handling is replaced with internal hash
MIXINS_BASE_PATH = '.'

constructor :config_walkinator, :projectinator, :mixinator

def loadinate(filepath:nil, mixins:[], env:{})
def loadinate(builtin_mixins:, filepath:nil, mixins:[], env:{})
# Aliases for clarity
cmdline_filepath = filepath
cmdline_mixins = mixins || []
Expand All @@ -23,10 +20,7 @@ def loadinate(filepath:nil, mixins:[], env:{})
project_filepath, config = @projectinator.load( filepath:cmdline_filepath, env:env )

# Extract cfg_enabled_mixins mixins list plus load paths list from config
cfg_enabled_mixins, cfg_load_paths = @projectinator.extract_mixins(
config: config,
mixins_base_path: MIXINS_BASE_PATH
)
cfg_enabled_mixins, cfg_load_paths = @projectinator.extract_mixins( config: config )

# Get our YAML file extension
yaml_ext = @projectinator.lookup_yaml_extension( config:config )
Expand All @@ -44,6 +38,7 @@ def loadinate(filepath:nil, mixins:[], env:{})
if not @projectinator.validate_mixins(
mixins: cfg_enabled_mixins,
load_paths: cfg_load_paths,
builtins: builtin_mixins,
source: 'Config :mixins -> :enabled =>',
yaml_extension: yaml_ext
)
Expand All @@ -54,25 +49,28 @@ def loadinate(filepath:nil, mixins:[], env:{})
if not @projectinator.validate_mixins(
mixins: cmdline_mixins,
load_paths: cfg_load_paths,
builtins: builtin_mixins,
source: 'Mixin',
yaml_extension: yaml_ext
)
raise 'Command line failed validation'
end

# Find mixins from project file among load paths
# Return ordered list of filepaths
# Find mixins in project file among load paths or built-in mixins
# Return ordered list of filepaths or built-in mixin names
config_mixins = @projectinator.lookup_mixins(
mixins: cfg_enabled_mixins,
load_paths: cfg_load_paths,
builtins: builtin_mixins,
yaml_extension: yaml_ext
)

# Find mixins from command line among load paths
# Return ordered list of filepaths
# Find mixins from command line among load paths or built-in mixins
# Return ordered list of filepaths or built-in mixin names
cmdline_mixins = @projectinator.lookup_mixins(
mixins: cmdline_mixins,
load_paths: cfg_load_paths,
builtins: builtin_mixins,
yaml_extension: yaml_ext
)

Expand All @@ -90,7 +88,7 @@ def loadinate(filepath:nil, mixins:[], env:{})
)

# Merge mixins
@mixinator.merge( config:config, mixins:mixins_assembled )
@mixinator.merge( builtins:builtin_mixins, config:config, mixins:mixins_assembled )

return project_filepath, config
end
Expand Down
35 changes: 25 additions & 10 deletions bin/mixinator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,37 +74,52 @@ def assemble_mixins(config:, env:, cmdline:)
assembly = []

# Build list of hashses to facilitate deduplication
cmdline.each {|filepath| assembly << {'command line' => filepath}}
cmdline.each {|mixin| assembly << {'command line' => mixin}}
assembly += env
config.each {|filepath| assembly << {'project configuration' => filepath}}
config.each {|mixin| assembly << {'project configuration' => mixin}}

# Remove duplicates inline
# 1. Expand filepaths to absolute paths for correct deduplication
# 1. Expand filepaths to absolute paths for correct deduplication (skip expanding simple mixin names)
# 2. Remove duplicates
assembly.uniq! {|entry| File.expand_path( entry.values.first )}
assembly.uniq! do |entry|
# If entry is filepath, expand it, otherwise leave entry untouched (it's a mixin name only)
@path_validator.filepath?( entry ) ? File.expand_path( entry.values.first ) : entry
end

# Return the compacted list (in merge order)
return assembly
end

def merge(config:, mixins:)
def merge(builtins:, config:, mixins:)
mixins.each do |mixin|
source = mixin.keys.first
filepath = mixin.values.first

_mixin = @yaml_wrapper.load( filepath )
_mixin = {} # Empty initial value

# Load mixin from filepath if it is a filepath
if @path_validator.filepath?( filepath )
_mixin = @yaml_wrapper.load( filepath )

# Log what filepath we used for this mixin
@streaminator.stream_puts( " + Merging #{'(empty) ' if _mixin.nil?}#{source} mixin using #{filepath}", Verbosity::OBNOXIOUS )

# Hnadle an empty mixin (it's unlikely but logically coherent)
# Reference mixin from built-in hash-based mixins
else
_mixin = builtins[filepath.to_sym()]

# Log built-in mixin we used
@streaminator.stream_puts( " + Merging built-in mixin '#{filepath}' from #{source}", Verbosity::OBNOXIOUS )
end

# Hnadle an empty mixin (it's unlikely but logically coherent and a good safety check)
_mixin = {} if _mixin.nil?

# Sanitize the mixin config by removing any :mixins section (these should not end up in merges)
_mixin.delete(:mixins)

# Merge this bad boy
config.deep_merge( _mixin )

# Log what filepath we used for this mixin
@streaminator.stream_puts( " + Merged #{'(empty) ' if _mixin.empty?}#{source} mixin using #{filepath}", Verbosity::OBNOXIOUS )
end

# Validate final configuration
Expand Down
5 changes: 5 additions & 0 deletions bin/mixins.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

BUILTIN_MIXINS = {
# Mixin name as symbol => mixin config hash
# :mixin => {}
}
6 changes: 6 additions & 0 deletions bin/path_validator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,10 @@ def standardize_paths( *paths )
end
end


def filepath?(str)
# If argument includes a file extension or a path separator, it's a filepath
return (!File.extname( str ).empty?) || (str.include?( File::SEPARATOR ))
end

end
60 changes: 33 additions & 27 deletions bin/projectinator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -85,22 +85,18 @@ def lookup_yaml_extension(config:)

# Pick apart a :mixins projcet configuration section and return components
# Layout mirrors :plugins section
def extract_mixins(config:, mixins_base_path:)
# Check if our base path exists
mixins_base_path = nil unless File.directory?(mixins_base_path)

def extract_mixins(config:)
# Get mixins config hash
_mixins = config[:mixins]

# If no :mixins section, return:
# - Empty enabled list
# - Load paths with only the built-in Ceedling mixins/ path
return [], [mixins_base_path].compact if _mixins.nil?
# - Empty load paths
return [], [] if _mixins.nil?

# Build list of load paths
# Configured load paths are higher in search path ordering
load_paths = _mixins[:load_paths] || []
load_paths += [mixins_base_path].compact # += forces a copy of configuration section

# Get list of mixins
enabled = _mixins[:enabled] || []
Expand Down Expand Up @@ -128,29 +124,32 @@ def validate_mixin_load_paths(load_paths)


# Validate mixins list
def validate_mixins(mixins:, load_paths:, source:, yaml_extension:)
def validate_mixins(mixins:, load_paths:, builtins:, source:, yaml_extension:)
validated = true

mixins.each do |mixin|
# Validate mixin filepaths
if !File.extname( mixin ).empty? or mixin.include?( File::SEPARATOR )
if @path_validator.filepath?( mixin )
if !@file_wrapper.exist?( mixin )
@streaminator.stream_puts( "ERROR: Cannot find mixin at #{mixin}" )
validated = false
end

# Otherwise, validate that mixin name can be found among the load paths
# Otherwise, validate that mixin name can be found in load paths or builtins
else
found = false
load_paths.each do |path|
if @file_wrapper.exist?( File.join( path, mixin + yaml_extension) )
if @file_wrapper.exist?( File.join( path, mixin + yaml_extension ) )
found = true
break
end
end

builtins.keys.each {|key| found = true if (mixin == key.to_s)}

if !found
@streaminator.stream_puts( "ERROR: #{source} '#{mixin}' cannot be found in the mixin load paths as '#{mixin + yaml_extension}'", Verbosity::ERRORS )
msg = "ERROR: #{source} '#{mixin}' cannot be found in mixin load paths as '#{mixin + yaml_extension}' or among built-in mixins"
@streaminator.stream_puts( msg, Verbosity::ERRORS )
validated = false
end
end
Expand All @@ -160,30 +159,37 @@ def validate_mixins(mixins:, load_paths:, source:, yaml_extension:)
end


# Yield ordered list of filepaths
def lookup_mixins(mixins:, load_paths:, yaml_extension:)
filepaths = []
# Yield ordered list of filepaths or built-in mixin names
def lookup_mixins(mixins:, load_paths:, builtins:, yaml_extension:)
_mixins = []

# Already validated, so we know:
# 1. Any mixin filepaths exists
# 2. Built-in mixin names exist in the internal hash

# Fill results hash with mixin name => mixin filepath
# Already validated, so we know the mixin filepath exists
# Fill filepaths array with filepaths or builtin names
mixins.each do |mixin|
# Handle explicit filepaths
if !File.extname( mixin ).empty? or mixin.include?( File::SEPARATOR )
filepaths << mixin
if !@path_validator.filepath?( mixin )
_mixins << mixin
next # Success, move on
end

# Find name in load_paths (we already know it exists from previous validation)
else
load_paths.each do |path|
filepath = File.join( path, mixin + yaml_extension )
if @file_wrapper.exist?( filepath )
filepaths << filepath
break
end
load_paths.each do |path|
filepath = File.join( path, mixin + yaml_extension )
if @file_wrapper.exist?( filepath )
_mixins << filepath
next # Success, move on
end
end

# Finally, just add the unmodified name to the list
# It's a built-in mixin
_mixins << mixin
end

return filepaths
return _mixins
end

### Private ###
Expand Down
Loading

0 comments on commit 1262199

Please sign in to comment.