Add raw configuration structures and enhance config parsing logic

main
Joca 2025-11-14 21:00:06 -03:00
parent 24b0d2f9da
commit d66b83c06d
Signed by: jocadbz
GPG Key ID: B1836DCE2F50BDF7
1 changed files with 424 additions and 260 deletions

View File

@ -87,6 +87,286 @@ __global:
build_directives []BuildDirective build_directives []BuildDirective
} }
struct RawGlobalConfig {
mut:
project_name string
src_dir string
build_dir string
bin_dir string
compiler string
toolchain string
debug_str string
optimize_str string
verbose_str string
parallel_str string
include_dirs []string
lib_search_paths []string
libraries []string
cflags []string
ldflags []string
dependencies_dir string
}
struct RawSharedLibConfig {
mut:
name string
output_dir string
sources []string
libraries []string
include_dirs []string
cflags []string
ldflags []string
debug_str string
optimize_str string
verbose_str string
}
struct RawToolConfig {
mut:
name string
output_dir string
sources []string
libraries []string
include_dirs []string
cflags []string
ldflags []string
debug_str string
optimize_str string
verbose_str string
}
struct RawDependencyConfig {
mut:
name string
url string
archive string
checksum string
extract_to string
build_cmds []string
}
struct RawBuildConfig {
mut:
global RawGlobalConfig
shared_libs []RawSharedLibConfig
tools []RawToolConfig
dependencies []RawDependencyConfig
}
fn parse_bool_str(value string) !bool {
lower := value.trim_space().to_lower()
return match lower {
'true', '1', 'yes', 'on' { true }
'false', '0', 'no', 'off' { false }
else { error('invalid boolean value: ${value}') }
}
}
fn parse_comma_list(value string) []string {
mut result := []string{}
if value.trim_space() == '' {
return result
}
for item in value.split(',') {
trimmed := item.trim_space()
if trimmed != '' {
result << trimmed
}
}
return result
}
fn parse_space_list(value string) []string {
mut result := []string{}
mut fields := value.split_any(' \t')
if fields.len == 0 && value.trim_space() != '' {
fields = [value.trim_space()]
}
for item in fields {
trimmed := item.trim_space()
if trimmed != '' {
result << trimmed
}
}
return result
}
fn merge_unique(mut target []string, additions []string) {
for item in additions {
trimmed := item.trim_space()
if trimmed == '' {
continue
}
if trimmed !in target {
target << trimmed
}
}
}
fn normalize_raw_config(raw RawBuildConfig, mut warnings []string) BuildConfig {
mut cfg := default_config
default_shared := SharedLibConfig{}
default_tool := ToolConfig{}
if raw.global.project_name != '' {
cfg.project_name = raw.global.project_name
}
if raw.global.src_dir != '' {
cfg.src_dir = raw.global.src_dir
}
if raw.global.build_dir != '' {
cfg.build_dir = raw.global.build_dir
}
if raw.global.bin_dir != '' {
cfg.bin_dir = raw.global.bin_dir
}
if raw.global.compiler != '' {
cfg.compiler = raw.global.compiler
}
if raw.global.toolchain != '' {
cfg.toolchain = raw.global.toolchain
}
if raw.global.dependencies_dir != '' {
cfg.dependencies_dir = raw.global.dependencies_dir
}
if raw.global.debug_str != '' {
cfg.debug = parse_bool_str(raw.global.debug_str) or {
warnings << 'Invalid boolean value for global.debug: ${raw.global.debug_str}'
cfg.debug
}
}
if raw.global.optimize_str != '' {
cfg.optimize = parse_bool_str(raw.global.optimize_str) or {
warnings << 'Invalid boolean value for global.optimize: ${raw.global.optimize_str}'
cfg.optimize
}
}
if raw.global.verbose_str != '' {
cfg.verbose = parse_bool_str(raw.global.verbose_str) or {
warnings << 'Invalid boolean value for global.verbose: ${raw.global.verbose_str}'
cfg.verbose
}
}
if raw.global.parallel_str != '' {
cfg.parallel_compilation = parse_bool_str(raw.global.parallel_str) or {
warnings << 'Invalid boolean value for global.parallel_compilation: ${raw.global.parallel_str}'
cfg.parallel_compilation
}
}
cfg.include_dirs << raw.global.include_dirs
cfg.lib_search_paths << raw.global.lib_search_paths
cfg.libraries << raw.global.libraries
cfg.cflags << raw.global.cflags
cfg.ldflags << raw.global.ldflags
for raw_lib in raw.shared_libs {
mut lib := SharedLibConfig{
name: raw_lib.name
output_dir: if raw_lib.output_dir != '' { raw_lib.output_dir } else { default_shared.output_dir }
sources: raw_lib.sources.clone()
libraries: raw_lib.libraries.clone()
debug: cfg.debug
optimize: cfg.optimize
verbose: cfg.verbose
}
lib.include_dirs = raw_lib.include_dirs.clone()
lib.cflags = raw_lib.cflags.clone()
lib.ldflags = raw_lib.ldflags.clone()
if raw_lib.debug_str != '' {
lib.debug = parse_bool_str(raw_lib.debug_str) or {
warnings << 'Invalid boolean value for shared_lib ${raw_lib.name} debug: ${raw_lib.debug_str}'
lib.debug
}
}
if raw_lib.optimize_str != '' {
lib.optimize = parse_bool_str(raw_lib.optimize_str) or {
warnings << 'Invalid boolean value for shared_lib ${raw_lib.name} optimize: ${raw_lib.optimize_str}'
lib.optimize
}
}
if raw_lib.verbose_str != '' {
lib.verbose = parse_bool_str(raw_lib.verbose_str) or {
warnings << 'Invalid boolean value for shared_lib ${raw_lib.name} verbose: ${raw_lib.verbose_str}'
lib.verbose
}
}
merge_unique(mut lib.include_dirs, cfg.include_dirs)
merge_unique(mut lib.cflags, cfg.cflags)
merge_unique(mut lib.ldflags, cfg.ldflags)
cfg.shared_libs << lib
}
for raw_tool in raw.tools {
mut tool := ToolConfig{
name: raw_tool.name
output_dir: if raw_tool.output_dir != '' { raw_tool.output_dir } else { default_tool.output_dir }
sources: raw_tool.sources.clone()
libraries: raw_tool.libraries.clone()
debug: cfg.debug
optimize: cfg.optimize
verbose: cfg.verbose
}
tool.include_dirs = raw_tool.include_dirs.clone()
tool.cflags = raw_tool.cflags.clone()
tool.ldflags = raw_tool.ldflags.clone()
if raw_tool.debug_str != '' {
tool.debug = parse_bool_str(raw_tool.debug_str) or {
warnings << 'Invalid boolean value for tool ${raw_tool.name} debug: ${raw_tool.debug_str}'
tool.debug
}
}
if raw_tool.optimize_str != '' {
tool.optimize = parse_bool_str(raw_tool.optimize_str) or {
warnings << 'Invalid boolean value for tool ${raw_tool.name} optimize: ${raw_tool.optimize_str}'
tool.optimize
}
}
if raw_tool.verbose_str != '' {
tool.verbose = parse_bool_str(raw_tool.verbose_str) or {
warnings << 'Invalid boolean value for tool ${raw_tool.name} verbose: ${raw_tool.verbose_str}'
tool.verbose
}
}
merge_unique(mut tool.include_dirs, cfg.include_dirs)
merge_unique(mut tool.cflags, cfg.cflags)
merge_unique(mut tool.ldflags, cfg.ldflags)
cfg.tools << tool
}
for raw_dep in raw.dependencies {
cfg.dependencies << Dependency{
name: raw_dep.name
url: raw_dep.url
archive: raw_dep.archive
checksum: raw_dep.checksum
extract_to: raw_dep.extract_to
build_cmds: raw_dep.build_cmds.clone()
}
}
for i in 0 .. cfg.shared_libs.len {
if cfg.shared_libs[i].name == '' {
cfg.shared_libs[i].name = 'lib${i}'
}
}
for i in 0 .. cfg.tools.len {
if cfg.tools[i].name == '' {
cfg.tools[i].name = 'tool${i}'
}
}
return cfg
}
// default config // default config
pub const default_config = BuildConfig{ pub const default_config = BuildConfig{
project_name: '' project_name: ''
@ -339,276 +619,161 @@ pub fn parse_args() !BuildConfig {
pub fn parse_config_file(filename string) !BuildConfig { pub fn parse_config_file(filename string) !BuildConfig {
content := os.read_file(filename) or { return error('Cannot read config file: ${filename}') } content := os.read_file(filename) or { return error('Cannot read config file: ${filename}') }
mut build_config := default_config mut raw := RawBuildConfig{}
lines := content.split_into_lines() mut warnings := []string{}
mut current_section := '' mut current_section := 'global'
mut current_lib_index := 0 mut current_shared_idx := -1
mut current_tool_index := 0 mut current_tool_idx := -1
mut current_dep_index := 0 mut current_dep_idx := -1
for line in lines { for line in content.split_into_lines() {
if line.starts_with('#') || line.trim_space() == '' { continue } trimmed := line.trim_space()
if trimmed == '' || trimmed.starts_with('#') {
if line.starts_with('[') && line.ends_with(']') { continue
// keep the brackets in current_section to match existing match arms }
current_section = '[' + line[1..line.len - 1] + ']'
// Point indices to the next entry index for repeated sections if trimmed.starts_with('[') && trimmed.ends_with(']') {
if current_section == '[shared_libs]' { section := trimmed[1..trimmed.len - 1].trim_space().to_lower()
current_lib_index = build_config.shared_libs.len match section {
} else if current_section == '[tools]' { 'global' {
current_tool_index = build_config.tools.len current_section = 'global'
} else if current_section == '[dependencies]' { current_shared_idx = -1
current_dep_index = build_config.dependencies.len current_tool_idx = -1
current_dep_idx = -1
}
'shared_libs' {
raw.shared_libs << RawSharedLibConfig{}
current_shared_idx = raw.shared_libs.len - 1
current_section = 'shared_libs'
}
'tools' {
raw.tools << RawToolConfig{}
current_tool_idx = raw.tools.len - 1
current_section = 'tools'
}
'dependencies' {
raw.dependencies << RawDependencyConfig{}
current_dep_idx = raw.dependencies.len - 1
current_section = 'dependencies'
}
else {
warnings << 'Unknown config section: ${section}'
current_section = 'global'
current_shared_idx = -1
current_tool_idx = -1
current_dep_idx = -1
}
} }
continue continue
} }
parts := line.split('=')
if parts.len == 2 {
key := parts[0].trim_space()
value := parts[1].trim_space().trim('"\'')
match current_section {
'' {
match key {
'project_name' { build_config.project_name = value }
'src_dir' { build_config.src_dir = value }
'build_dir' { build_config.build_dir = value }
'bin_dir' { build_config.bin_dir = value }
'compiler' { build_config.compiler = value }
'toolchain' { build_config.toolchain = value }
'debug' { build_config.debug = value == 'true' }
'optimize' { build_config.optimize = value == 'true' }
'verbose' { build_config.verbose = value == 'true' }
'parallel_compilation' { build_config.parallel_compilation = value == 'true' }
'include_dirs' {
dirs := value.split(',')
for dir in dirs { build_config.include_dirs << dir.trim_space() }
}
'lib_search_paths' {
paths := value.split(',')
for path in paths { build_config.lib_search_paths << path.trim_space() }
}
'libraries' {
libs := value.split(',')
for lib in libs { build_config.libraries << lib.trim_space() }
}
'cflags' {
flags := value.split(' ')
for flag in flags { build_config.cflags << flag.trim_space() }
}
'ldflags' {
flags := value.split(' ')
for flag in flags { build_config.ldflags << flag.trim_space() }
}
'dependencies_dir' { build_config.dependencies_dir = value }
else {}
}
}
'[shared_libs]' {
// Ensure we have a lib config to modify
if current_lib_index >= build_config.shared_libs.len {
build_config.shared_libs << SharedLibConfig{}
}
mut lib_config := &build_config.shared_libs[current_lib_index]
match key {
'name' { lib_config.name = value }
'sources' {
sources := value.split(',')
for src in sources {
lib_config.sources << src.trim_space()
}
}
'libraries' {
libs := value.split(',')
for lib in libs {
lib_config.libraries << lib.trim_space()
}
}
'include_dirs' {
dirs := value.split(',')
for dir in dirs {
lib_config.include_dirs << dir.trim_space()
}
}
'cflags' {
flags := value.split(' ')
for flag in flags {
lib_config.cflags << flag.trim_space()
}
}
'ldflags' {
flags := value.split(' ')
for flag in flags {
lib_config.ldflags << flag.trim_space()
}
}
'debug' { lib_config.debug = value == 'true' }
'optimize' { lib_config.optimize = value == 'true' }
'verbose' { lib_config.verbose = value == 'true' }
'output_dir' { lib_config.output_dir = value }
else {
if build_config.verbose {
println('Warning: Unknown shared lib config key: ${key}')
}
}
}
// keys for this shared_lib section are populated into the same struct
}
'[tools]' {
// Ensure we have a tool config to modify
if current_tool_index >= build_config.tools.len {
build_config.tools << ToolConfig{}
}
mut tool_config := &build_config.tools[current_tool_index]
match key {
'name' { tool_config.name = value }
'sources' {
sources := value.split(',')
for src in sources {
tool_config.sources << src.trim_space()
}
}
'libraries' {
libs := value.split(',')
for lib in libs {
tool_config.libraries << lib.trim_space()
}
}
'include_dirs' {
dirs := value.split(',')
for dir in dirs {
tool_config.include_dirs << dir.trim_space()
}
}
'cflags' {
flags := value.split(' ')
for flag in flags {
tool_config.cflags << flag.trim_space()
}
}
'ldflags' {
flags := value.split(' ')
for flag in flags {
tool_config.ldflags << flag.trim_space()
}
}
'debug' { tool_config.debug = value == 'true' }
'optimize' { tool_config.optimize = value == 'true' }
'verbose' { tool_config.verbose = value == 'true' }
'output_dir' { tool_config.output_dir = value }
else {
if build_config.verbose {
println('Warning: Unknown tool config key: ${key}')
}
}
}
// keys for this tool section are populated into the same struct
}
'[dependencies]' {
// Ensure we have a dependency entry to modify
if current_dep_index >= build_config.dependencies.len {
build_config.dependencies << Dependency{}
}
mut dep := &build_config.dependencies[current_dep_index]
match key { eq_index := trimmed.index('=') or {
'name' { dep.name = value } warnings << 'Invalid config line (missing =): ${trimmed}'
'url' { dep.url = value } continue
'archive' { dep.archive = value } }
'checksum' { dep.checksum = value }
'build_cmds' { key := trimmed[..eq_index].trim_space()
cmds := value.split(';') mut value := trimmed[eq_index + 1..].trim_space()
for c in cmds { value = value.trim('"\'')
dep.build_cmds << c.trim_space()
} match current_section {
} 'global' {
'extract_to' { dep.extract_to = value } match key {
else { 'project_name' { raw.global.project_name = value }
if build_config.verbose { 'src_dir' { raw.global.src_dir = value }
println('Warning: Unknown dependency config key: ${key}') 'build_dir' { raw.global.build_dir = value }
} 'bin_dir' { raw.global.bin_dir = value }
} 'compiler' { raw.global.compiler = value }
} 'toolchain' { raw.global.toolchain = value }
// keys for this dependency section are populated into the same struct 'debug' { raw.global.debug_str = value }
} 'optimize' { raw.global.optimize_str = value }
else { 'verbose' { raw.global.verbose_str = value }
if build_config.verbose { 'parallel_compilation' { raw.global.parallel_str = value }
println('Warning: Unknown config section: ${current_section}') 'include_dirs' { raw.global.include_dirs << parse_comma_list(value) }
} 'lib_search_paths' { raw.global.lib_search_paths << parse_comma_list(value) }
'libraries' { raw.global.libraries << parse_comma_list(value) }
'cflags' { raw.global.cflags << parse_space_list(value) }
'ldflags' { raw.global.ldflags << parse_space_list(value) }
'dependencies_dir' { raw.global.dependencies_dir = value }
else { warnings << 'Unknown global config key: ${key}' }
} }
} }
'shared_libs' {
if current_shared_idx < 0 {
raw.shared_libs << RawSharedLibConfig{}
current_shared_idx = raw.shared_libs.len - 1
}
mut lib := &raw.shared_libs[current_shared_idx]
match key {
'name' { lib.name = value }
'sources' { lib.sources << parse_comma_list(value) }
'libraries' { lib.libraries << parse_comma_list(value) }
'include_dirs' { lib.include_dirs << parse_comma_list(value) }
'cflags' { lib.cflags << parse_space_list(value) }
'ldflags' { lib.ldflags << parse_space_list(value) }
'debug' { lib.debug_str = value }
'optimize' { lib.optimize_str = value }
'verbose' { lib.verbose_str = value }
'output_dir' { lib.output_dir = value }
else { warnings << 'Unknown shared_libs key: ${key}' }
}
}
'tools' {
if current_tool_idx < 0 {
raw.tools << RawToolConfig{}
current_tool_idx = raw.tools.len - 1
}
mut tool := &raw.tools[current_tool_idx]
match key {
'name' { tool.name = value }
'sources' { tool.sources << parse_comma_list(value) }
'libraries' { tool.libraries << parse_comma_list(value) }
'include_dirs' { tool.include_dirs << parse_comma_list(value) }
'cflags' { tool.cflags << parse_space_list(value) }
'ldflags' { tool.ldflags << parse_space_list(value) }
'debug' { tool.debug_str = value }
'optimize' { tool.optimize_str = value }
'verbose' { tool.verbose_str = value }
'output_dir' { tool.output_dir = value }
else { warnings << 'Unknown tools key: ${key}' }
}
}
'dependencies' {
if current_dep_idx < 0 {
raw.dependencies << RawDependencyConfig{}
current_dep_idx = raw.dependencies.len - 1
}
mut dep := &raw.dependencies[current_dep_idx]
match key {
'name' { dep.name = value }
'url' { dep.url = value }
'archive' { dep.archive = value }
'checksum' { dep.checksum = value }
'extract_to' { dep.extract_to = value }
'build_cmds' {
for cmd in value.split(';') {
trimmed_cmd := cmd.trim_space()
if trimmed_cmd != '' {
dep.build_cmds << trimmed_cmd
}
}
}
else { warnings << 'Unknown dependencies key: ${key}' }
}
}
else {
warnings << 'Key outside known section: ${key}'
}
} }
} }
// Set default values for shared libs and tools from global config if not explicitly set
for mut lib in build_config.shared_libs {
if lib.name == '' {
lib.name = 'lib${build_config.shared_libs.index(lib)}'
}
if !lib.debug { lib.debug = build_config.debug }
if !lib.optimize { lib.optimize = build_config.optimize }
if !lib.verbose { lib.verbose = build_config.verbose }
}
for mut tool in build_config.tools {
if tool.name == '' {
tool.name = 'tool${build_config.tools.index(tool)}'
}
if !tool.debug { tool.debug = build_config.debug }
if !tool.optimize { tool.optimize = build_config.optimize }
if !tool.verbose { tool.verbose = build_config.verbose }
}
// Merge global flags and includes into individual shared libs and tools mut build_config := normalize_raw_config(raw, mut warnings)
for mut lib in build_config.shared_libs { if build_config.verbose {
// merge include dirs for warning in warnings {
for inc in build_config.include_dirs { println('Warning: ${warning}')
if inc != '' && inc !in lib.include_dirs {
lib.include_dirs << inc
}
} }
// merge cflags
for flag in build_config.cflags {
if flag != '' && flag !in lib.cflags {
lib.cflags << flag
}
}
// merge ldflags
for flag in build_config.ldflags {
if flag != '' && flag !in lib.ldflags {
lib.ldflags << flag
}
}
// Do NOT merge global libraries into shared lib 'libraries' here.
// Global libraries should be linked globally during linker command assembly, not as per-target shared lib dependencies.
} }
for mut tool in build_config.tools {
// merge include dirs
for inc in build_config.include_dirs {
if inc != '' && inc !in tool.include_dirs {
tool.include_dirs << inc
}
}
// merge cflags
for flag in build_config.cflags {
if flag != '' && flag !in tool.cflags {
tool.cflags << flag
}
}
// merge ldflags
for flag in build_config.ldflags {
if flag != '' && flag !in tool.ldflags {
tool.ldflags << flag
}
}
// Do NOT merge global libraries into tool 'libraries' here.
// Keep per-tool libraries strictly those declared for the tool; global libraries will be added at link time.
}
return build_config return build_config
} }
@ -642,7 +807,6 @@ pub fn get_target_config_values(target_config TargetConfig) (bool, bool, bool, b
return is_shared_lib, use_debug, use_optimize, use_verbose, target_includes, target_cflags return is_shared_lib, use_debug, use_optimize, use_verbose, target_includes, target_cflags
} }
// Toolchain defines how compiler and linker commands are assembled for a target. // Toolchain defines how compiler and linker commands are assembled for a target.
pub interface Toolchain { pub interface Toolchain {
compile_command(source_file string, object_file string, build_config &BuildConfig, target_config TargetConfig) string compile_command(source_file string, object_file string, build_config &BuildConfig, target_config TargetConfig) string