Refactor on duplicate functions and new tests :3
parent
63fa9ef0b7
commit
9d3d5fb105
|
|
@ -5,6 +5,7 @@ import config
|
|||
import deps
|
||||
import runtime
|
||||
import time
|
||||
import util
|
||||
|
||||
// BuildTarget represents a build target (shared lib or tool)
|
||||
pub enum BuildTarget {
|
||||
|
|
@ -653,7 +654,7 @@ fn auto_discover_sources(mut build_config config.BuildConfig) {
|
|||
// Look for sources in src/lib/<lib_name>/
|
||||
lib_src_dir := os.join_path('src', 'lib', lib_config.name)
|
||||
if os.is_dir(lib_src_dir) {
|
||||
lib_sources := find_source_files(lib_src_dir) or { []string{} }
|
||||
lib_sources := util.find_source_files(lib_src_dir) or { []string{} }
|
||||
lib_config.sources = lib_sources
|
||||
if build_config.verbose && lib_sources.len > 0 {
|
||||
println('Auto-discovered ${lib_sources.len} source files for shared lib ${lib_config.name}')
|
||||
|
|
@ -668,7 +669,7 @@ fn auto_discover_sources(mut build_config config.BuildConfig) {
|
|||
// Look for sources in src/tools/<tool_name>/
|
||||
tool_src_dir := os.join_path('src', 'tools', tool_config.name)
|
||||
if os.is_dir(tool_src_dir) {
|
||||
tool_sources := find_source_files(tool_src_dir) or { []string{} }
|
||||
tool_sources := util.find_source_files(tool_src_dir) or { []string{} }
|
||||
if tool_sources.len > 0 {
|
||||
tool_config.sources = tool_sources
|
||||
} else {
|
||||
|
|
@ -695,7 +696,7 @@ fn auto_discover_sources(mut build_config config.BuildConfig) {
|
|||
if build_config.tools.len > 0 && build_config.tools[0].sources.len == 0 {
|
||||
mut default_tool := &build_config.tools[0]
|
||||
if default_tool.name == build_config.project_name {
|
||||
all_sources := find_source_files(build_config.src_dir) or { []string{} }
|
||||
all_sources := util.find_source_files(build_config.src_dir) or { []string{} }
|
||||
if all_sources.len > 0 {
|
||||
default_tool.sources = all_sources
|
||||
if build_config.verbose {
|
||||
|
|
@ -1015,31 +1016,6 @@ fn get_object_file(source_file string, object_dir string) string {
|
|||
return obj_file
|
||||
}
|
||||
|
||||
fn find_source_files(dir string) ![]string {
|
||||
mut files := []string{}
|
||||
|
||||
if !os.is_dir(dir) {
|
||||
return error('Source directory does not exist: ${dir}')
|
||||
}
|
||||
|
||||
items := os.ls(dir) or { return error('Failed to list directory: ${dir}') }
|
||||
|
||||
for item in items {
|
||||
full_path := os.join_path(dir, item)
|
||||
if os.is_file(full_path) {
|
||||
if item.ends_with('.cpp') || item.ends_with('.cc') || item.ends_with('.cxx') {
|
||||
files << full_path
|
||||
}
|
||||
} else if os.is_dir(full_path) {
|
||||
// Recursively search subdirectories
|
||||
sub_files := find_source_files(full_path)!
|
||||
files << sub_files
|
||||
}
|
||||
}
|
||||
|
||||
return files
|
||||
}
|
||||
|
||||
fn needs_recompile(source_file string, object_file string) bool {
|
||||
if !os.is_file(source_file) {
|
||||
// source missing, signal recompile to allow upstream code to handle error
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
module config
|
||||
|
||||
import os
|
||||
import util
|
||||
|
||||
// BuildDirective represents a single build directive found in source files
|
||||
pub struct BuildDirective {
|
||||
|
|
@ -351,7 +352,7 @@ pub fn (mut build_config BuildConfig) parse_build_directives() ! {
|
|||
mut directives := []BuildDirective{}
|
||||
|
||||
// Find all source files in src directory
|
||||
src_files := find_source_files(build_config.src_dir) or {
|
||||
src_files := util.find_source_files(build_config.src_dir) or {
|
||||
if build_config.verbose {
|
||||
println('No source files found in ${build_config.src_dir}')
|
||||
}
|
||||
|
|
@ -1030,28 +1031,7 @@ pub fn build_compiler_command(source_file string, object_file string, build_conf
|
|||
return cmd
|
||||
}
|
||||
|
||||
// Utility function to find source files
|
||||
// Utility function to find source files (delegates to util module for backward compatibility)
|
||||
pub fn find_source_files(dir string) ![]string {
|
||||
mut files := []string{}
|
||||
|
||||
if !os.is_dir(dir) {
|
||||
return error('Source directory does not exist: ${dir}')
|
||||
}
|
||||
|
||||
items := os.ls(dir) or { return error('Failed to list directory: ${dir}') }
|
||||
|
||||
for item in items {
|
||||
full_path := os.join_path(dir, item)
|
||||
if os.is_file(full_path) {
|
||||
if item.ends_with('.cpp') || item.ends_with('.cc') || item.ends_with('.cxx') {
|
||||
files << full_path
|
||||
}
|
||||
} else if os.is_dir(full_path) {
|
||||
// Recursively search subdirectories
|
||||
sub_files := find_source_files(full_path)!
|
||||
files << sub_files
|
||||
}
|
||||
}
|
||||
|
||||
return files
|
||||
return util.find_source_files(dir)
|
||||
}
|
||||
29
lana.v
29
lana.v
|
|
@ -7,10 +7,7 @@ import runner
|
|||
import initializer
|
||||
import deps
|
||||
import help
|
||||
|
||||
// For runner compatibility
|
||||
const bin_dir = 'bin'
|
||||
const tools_dir = 'bin/tools'
|
||||
import util
|
||||
|
||||
fn main() {
|
||||
mut config_data := config.parse_args() or { config.default_config }
|
||||
|
|
@ -38,7 +35,11 @@ fn main() {
|
|||
}
|
||||
|
||||
// Find and run the main executable (first tool or project_name)
|
||||
main_executable := get_main_executable(config_data)
|
||||
tools := config_data.tools.map(util.ToolInfo{
|
||||
name: it.name
|
||||
output_dir: it.output_dir
|
||||
})
|
||||
main_executable := util.get_main_executable(config_data.project_name, config_data.bin_dir, tools)
|
||||
if main_executable != '' && os.is_file(main_executable) {
|
||||
runner.run_executable(config_data)
|
||||
} else {
|
||||
|
|
@ -59,21 +60,3 @@ fn main() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_main_executable(build_config config.BuildConfig) string {
|
||||
// First try to find a tool with the project name
|
||||
for tool_config in build_config.tools {
|
||||
if tool_config.name == build_config.project_name {
|
||||
return os.join_path(tool_config.output_dir, tool_config.name)
|
||||
}
|
||||
}
|
||||
|
||||
// Then try the first tool
|
||||
if build_config.tools.len > 0 {
|
||||
tool_config := build_config.tools[0]
|
||||
return os.join_path(tool_config.output_dir, tool_config.name)
|
||||
}
|
||||
|
||||
// Fallback to old behavior
|
||||
return os.join_path(build_config.bin_dir, build_config.project_name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,19 +2,24 @@ module runner
|
|||
|
||||
import os
|
||||
import config
|
||||
import util
|
||||
|
||||
pub fn run_executable(build_config config.BuildConfig) {
|
||||
main_executable := get_main_executable(build_config)
|
||||
|
||||
tools := build_config.tools.map(util.ToolInfo{
|
||||
name: it.name
|
||||
output_dir: it.output_dir
|
||||
})
|
||||
main_executable := util.get_main_executable(build_config.project_name, build_config.bin_dir, tools)
|
||||
|
||||
if !os.is_file(main_executable) {
|
||||
println('Main executable not found: ${main_executable}')
|
||||
println('Please run "lana build" first')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
println('Running ${main_executable}...')
|
||||
res := os.execute('${main_executable}')
|
||||
|
||||
|
||||
if res.exit_code == 0 {
|
||||
println('Execution completed successfully!')
|
||||
if res.output.len > 0 {
|
||||
|
|
@ -26,22 +31,4 @@ pub fn run_executable(build_config config.BuildConfig) {
|
|||
println('Output:\n${res.output}')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_main_executable(build_config config.BuildConfig) string {
|
||||
// First try to find a tool with the project name
|
||||
for tool_config in build_config.tools {
|
||||
if tool_config.name == build_config.project_name {
|
||||
return os.join_path(tool_config.output_dir, tool_config.name)
|
||||
}
|
||||
}
|
||||
|
||||
// Then try the first tool
|
||||
if build_config.tools.len > 0 {
|
||||
tool_config := build_config.tools[0]
|
||||
return os.join_path(tool_config.output_dir, tool_config.name)
|
||||
}
|
||||
|
||||
// Fallback to old behavior
|
||||
return os.join_path(build_config.bin_dir, build_config.project_name)
|
||||
}
|
||||
|
|
@ -0,0 +1,284 @@
|
|||
module tests
|
||||
|
||||
import os
|
||||
import builder
|
||||
import config
|
||||
|
||||
fn test_build_graph_handles_empty_config() {
|
||||
cfg := config.BuildConfig{
|
||||
project_name: 'empty'
|
||||
shared_libs: []
|
||||
tools: []
|
||||
}
|
||||
|
||||
summary := builder.preview_build_graph(&cfg) or { panic(err) }
|
||||
|
||||
assert summary.nodes.len == 0
|
||||
assert summary.order.len == 0
|
||||
}
|
||||
|
||||
fn test_build_graph_handles_multiple_dependencies() {
|
||||
tmp := new_temp_dir('lana_multi_deps')
|
||||
defer {
|
||||
os.rmdir_all(tmp) or {}
|
||||
}
|
||||
|
||||
src_dir := os.join_path(tmp, 'src')
|
||||
lib_dir := os.join_path(src_dir, 'lib')
|
||||
tool_dir := os.join_path(src_dir, 'tools')
|
||||
os.mkdir_all(lib_dir) or { panic(err) }
|
||||
os.mkdir_all(tool_dir) or { panic(err) }
|
||||
|
||||
// Create source files
|
||||
os.write_file(os.join_path(lib_dir, 'base.cpp'), '// base') or { panic(err) }
|
||||
os.write_file(os.join_path(lib_dir, 'utils.cpp'), '// utils') or { panic(err) }
|
||||
os.write_file(os.join_path(lib_dir, 'core.cpp'), '// core') or { panic(err) }
|
||||
os.write_file(os.join_path(tool_dir, 'app.cpp'), '// app') or { panic(err) }
|
||||
|
||||
// core depends on base and utils
|
||||
// app depends on core
|
||||
cfg := config.BuildConfig{
|
||||
project_name: 'multi_deps'
|
||||
src_dir: src_dir
|
||||
shared_libs: [
|
||||
config.SharedLibConfig{
|
||||
name: 'base'
|
||||
sources: [os.join_path(lib_dir, 'base.cpp')]
|
||||
},
|
||||
config.SharedLibConfig{
|
||||
name: 'utils'
|
||||
sources: [os.join_path(lib_dir, 'utils.cpp')]
|
||||
},
|
||||
config.SharedLibConfig{
|
||||
name: 'core'
|
||||
sources: [os.join_path(lib_dir, 'core.cpp')]
|
||||
libraries: ['base', 'utils']
|
||||
},
|
||||
]
|
||||
tools: [
|
||||
config.ToolConfig{
|
||||
name: 'app'
|
||||
sources: [os.join_path(tool_dir, 'app.cpp')]
|
||||
libraries: ['core']
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
summary := builder.preview_build_graph(&cfg) or { panic(err) }
|
||||
|
||||
// Should have 4 nodes
|
||||
assert summary.nodes.len == 4
|
||||
|
||||
// Find app in order - it should come after core
|
||||
mut core_idx := -1
|
||||
mut app_idx := -1
|
||||
for idx, id in summary.order {
|
||||
if id == 'shared:core' {
|
||||
core_idx = idx
|
||||
}
|
||||
if id == 'tool:app' {
|
||||
app_idx = idx
|
||||
}
|
||||
}
|
||||
|
||||
assert core_idx >= 0
|
||||
assert app_idx >= 0
|
||||
assert app_idx > core_idx
|
||||
|
||||
// base and utils should come before core
|
||||
mut base_idx := -1
|
||||
mut utils_idx := -1
|
||||
for idx, id in summary.order {
|
||||
if id == 'shared:base' {
|
||||
base_idx = idx
|
||||
}
|
||||
if id == 'shared:utils' {
|
||||
utils_idx = idx
|
||||
}
|
||||
}
|
||||
|
||||
assert base_idx < core_idx
|
||||
assert utils_idx < core_idx
|
||||
}
|
||||
|
||||
fn test_build_graph_detects_unresolved_dependencies() {
|
||||
tmp := new_temp_dir('lana_unresolved')
|
||||
defer {
|
||||
os.rmdir_all(tmp) or {}
|
||||
}
|
||||
|
||||
src_dir := os.join_path(tmp, 'src')
|
||||
os.mkdir_all(src_dir) or { panic(err) }
|
||||
os.write_file(os.join_path(src_dir, 'main.cpp'), '// main') or { panic(err) }
|
||||
|
||||
cfg := config.BuildConfig{
|
||||
project_name: 'unresolved'
|
||||
src_dir: src_dir
|
||||
tools: [
|
||||
config.ToolConfig{
|
||||
name: 'app'
|
||||
sources: [os.join_path(src_dir, 'main.cpp')]
|
||||
libraries: ['nonexistent_lib']
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
summary := builder.preview_build_graph(&cfg) or { panic(err) }
|
||||
|
||||
// Should have 1 node with unresolved dependency
|
||||
assert summary.nodes.len == 1
|
||||
assert 'tool:app' in summary.unresolved
|
||||
assert summary.unresolved['tool:app'].contains('nonexistent_lib')
|
||||
}
|
||||
|
||||
fn test_build_graph_skips_empty_sources() {
|
||||
cfg := config.BuildConfig{
|
||||
project_name: 'empty_sources'
|
||||
debug: true
|
||||
verbose: true
|
||||
shared_libs: [
|
||||
config.SharedLibConfig{
|
||||
name: 'empty_lib'
|
||||
sources: []
|
||||
debug: true
|
||||
},
|
||||
]
|
||||
tools: [
|
||||
config.ToolConfig{
|
||||
name: 'empty_tool'
|
||||
sources: []
|
||||
debug: true
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
summary := builder.preview_build_graph(&cfg) or { panic(err) }
|
||||
|
||||
// Empty sources should be skipped
|
||||
assert summary.nodes.len == 0
|
||||
}
|
||||
|
||||
fn test_build_graph_resolves_lib_prefix_aliases() {
|
||||
tmp := new_temp_dir('lana_aliases')
|
||||
defer {
|
||||
os.rmdir_all(tmp) or {}
|
||||
}
|
||||
|
||||
src_dir := os.join_path(tmp, 'src')
|
||||
lib_dir := os.join_path(src_dir, 'lib')
|
||||
tool_dir := os.join_path(src_dir, 'tools')
|
||||
os.mkdir_all(lib_dir) or { panic(err) }
|
||||
os.mkdir_all(tool_dir) or { panic(err) }
|
||||
|
||||
os.write_file(os.join_path(lib_dir, 'mylib.cpp'), '// lib') or { panic(err) }
|
||||
os.write_file(os.join_path(tool_dir, 'app.cpp'), '// app') or { panic(err) }
|
||||
|
||||
// Reference library with lib/ prefix
|
||||
cfg := config.BuildConfig{
|
||||
project_name: 'aliases'
|
||||
src_dir: src_dir
|
||||
shared_libs: [
|
||||
config.SharedLibConfig{
|
||||
name: 'mylib'
|
||||
sources: [os.join_path(lib_dir, 'mylib.cpp')]
|
||||
},
|
||||
]
|
||||
tools: [
|
||||
config.ToolConfig{
|
||||
name: 'app'
|
||||
sources: [os.join_path(tool_dir, 'app.cpp')]
|
||||
libraries: ['lib/mylib']
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
summary := builder.preview_build_graph(&cfg) or { panic(err) }
|
||||
|
||||
// Dependency should be resolved
|
||||
assert summary.nodes.len == 2
|
||||
assert summary.unresolved.len == 0
|
||||
|
||||
// Find app node and check its dependencies
|
||||
for node in summary.nodes {
|
||||
if node.id == 'tool:app' {
|
||||
assert node.dependencies.contains('shared:mylib')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_build_graph_resolves_so_extension_aliases() {
|
||||
tmp := new_temp_dir('lana_so_aliases')
|
||||
defer {
|
||||
os.rmdir_all(tmp) or {}
|
||||
}
|
||||
|
||||
src_dir := os.join_path(tmp, 'src')
|
||||
lib_dir := os.join_path(src_dir, 'lib')
|
||||
tool_dir := os.join_path(src_dir, 'tools')
|
||||
os.mkdir_all(lib_dir) or { panic(err) }
|
||||
os.mkdir_all(tool_dir) or { panic(err) }
|
||||
|
||||
os.write_file(os.join_path(lib_dir, 'core.cpp'), '// lib') or { panic(err) }
|
||||
os.write_file(os.join_path(tool_dir, 'app.cpp'), '// app') or { panic(err) }
|
||||
|
||||
// Reference library with .so extension
|
||||
cfg := config.BuildConfig{
|
||||
project_name: 'so_aliases'
|
||||
src_dir: src_dir
|
||||
shared_libs: [
|
||||
config.SharedLibConfig{
|
||||
name: 'core'
|
||||
sources: [os.join_path(lib_dir, 'core.cpp')]
|
||||
},
|
||||
]
|
||||
tools: [
|
||||
config.ToolConfig{
|
||||
name: 'app'
|
||||
sources: [os.join_path(tool_dir, 'app.cpp')]
|
||||
libraries: ['core.so']
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
summary := builder.preview_build_graph(&cfg) or { panic(err) }
|
||||
|
||||
// Dependency should be resolved
|
||||
assert summary.unresolved.len == 0
|
||||
|
||||
for node in summary.nodes {
|
||||
if node.id == 'tool:app' {
|
||||
assert node.dependencies.contains('shared:core')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_build_graph_includes_directives() {
|
||||
tmp := new_temp_dir('lana_directives_graph')
|
||||
defer {
|
||||
os.rmdir_all(tmp) or {}
|
||||
}
|
||||
|
||||
src_dir := os.join_path(tmp, 'src')
|
||||
tool_dir := os.join_path(src_dir, 'tools')
|
||||
os.mkdir_all(tool_dir) or { panic(err) }
|
||||
|
||||
os.write_file(os.join_path(tool_dir, 'custom.cpp'), '// custom') or { panic(err) }
|
||||
|
||||
cfg := config.BuildConfig{
|
||||
project_name: 'directives'
|
||||
src_dir: src_dir
|
||||
build_directives: [
|
||||
config.BuildDirective{
|
||||
unit_name: 'tools/custom'
|
||||
output_path: 'tools/custom'
|
||||
is_shared: false
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
summary := builder.preview_build_graph(&cfg) or { panic(err) }
|
||||
|
||||
assert summary.nodes.len == 1
|
||||
assert summary.nodes[0].id == 'directive:tools/custom'
|
||||
assert summary.nodes[0].is_directive == true
|
||||
}
|
||||
|
|
@ -0,0 +1,241 @@
|
|||
module tests
|
||||
|
||||
import os
|
||||
import config
|
||||
|
||||
fn test_parse_bool_values_true() {
|
||||
tmp := new_temp_dir('lana_bool')
|
||||
defer {
|
||||
os.rmdir_all(tmp) or {}
|
||||
}
|
||||
|
||||
config_path := os.join_path(tmp, 'config.ini')
|
||||
content := '[global]\nproject_name = test\ndebug = true\noptimize = yes\nverbose = 1\nparallel_compilation = on\n'
|
||||
os.write_file(config_path, content) or { panic(err) }
|
||||
|
||||
cfg := config.parse_config_file(config_path) or { panic(err) }
|
||||
|
||||
assert cfg.debug == true
|
||||
assert cfg.optimize == true
|
||||
assert cfg.verbose == true
|
||||
assert cfg.parallel_compilation == true
|
||||
}
|
||||
|
||||
fn test_parse_bool_values_false() {
|
||||
tmp := new_temp_dir('lana_bool_false')
|
||||
defer {
|
||||
os.rmdir_all(tmp) or {}
|
||||
}
|
||||
|
||||
config_path := os.join_path(tmp, 'config.ini')
|
||||
content := '[global]\nproject_name = test\ndebug = false\noptimize = no\nverbose = 0\nparallel_compilation = off\n'
|
||||
os.write_file(config_path, content) or { panic(err) }
|
||||
|
||||
cfg := config.parse_config_file(config_path) or { panic(err) }
|
||||
|
||||
assert cfg.debug == false
|
||||
assert cfg.optimize == false
|
||||
assert cfg.verbose == false
|
||||
assert cfg.parallel_compilation == false
|
||||
}
|
||||
|
||||
fn test_parse_comma_separated_lists() {
|
||||
tmp := new_temp_dir('lana_lists')
|
||||
defer {
|
||||
os.rmdir_all(tmp) or {}
|
||||
}
|
||||
|
||||
config_path := os.join_path(tmp, 'config.ini')
|
||||
content := '[global]\nproject_name = test\ninclude_dirs = include, src/include, deps/include\nlibraries = pthread, m, dl\n'
|
||||
os.write_file(config_path, content) or { panic(err) }
|
||||
|
||||
cfg := config.parse_config_file(config_path) or { panic(err) }
|
||||
|
||||
assert cfg.include_dirs.len == 3
|
||||
assert cfg.include_dirs.contains('include')
|
||||
assert cfg.include_dirs.contains('src/include')
|
||||
assert cfg.include_dirs.contains('deps/include')
|
||||
|
||||
assert cfg.libraries.len == 3
|
||||
assert cfg.libraries.contains('pthread')
|
||||
assert cfg.libraries.contains('m')
|
||||
assert cfg.libraries.contains('dl')
|
||||
}
|
||||
|
||||
fn test_parse_space_separated_cflags() {
|
||||
tmp := new_temp_dir('lana_cflags')
|
||||
defer {
|
||||
os.rmdir_all(tmp) or {}
|
||||
}
|
||||
|
||||
config_path := os.join_path(tmp, 'config.ini')
|
||||
content := '[global]\nproject_name = test\ncflags = -Wall -Wextra -Werror -pedantic\nldflags = -pthread -lm\n'
|
||||
os.write_file(config_path, content) or { panic(err) }
|
||||
|
||||
cfg := config.parse_config_file(config_path) or { panic(err) }
|
||||
|
||||
assert cfg.cflags.contains('-Wall')
|
||||
assert cfg.cflags.contains('-Wextra')
|
||||
assert cfg.cflags.contains('-Werror')
|
||||
assert cfg.cflags.contains('-pedantic')
|
||||
|
||||
assert cfg.ldflags.contains('-pthread')
|
||||
assert cfg.ldflags.contains('-lm')
|
||||
}
|
||||
|
||||
fn test_parse_multiple_shared_libs() {
|
||||
tmp := new_temp_dir('lana_multi_libs')
|
||||
defer {
|
||||
os.rmdir_all(tmp) or {}
|
||||
}
|
||||
|
||||
config_path := os.join_path(tmp, 'config.ini')
|
||||
content := '[global]\nproject_name = test\n\n[shared_libs]\nname = core\nsources = src/lib/core.cpp\n\n[shared_libs]\nname = utils\nsources = src/lib/utils.cpp\nlibraries = core\n'
|
||||
os.write_file(config_path, content) or { panic(err) }
|
||||
|
||||
cfg := config.parse_config_file(config_path) or { panic(err) }
|
||||
|
||||
assert cfg.shared_libs.len == 2
|
||||
assert cfg.shared_libs[0].name == 'core'
|
||||
assert cfg.shared_libs[1].name == 'utils'
|
||||
assert cfg.shared_libs[1].libraries.contains('core')
|
||||
}
|
||||
|
||||
fn test_parse_multiple_tools() {
|
||||
tmp := new_temp_dir('lana_multi_tools')
|
||||
defer {
|
||||
os.rmdir_all(tmp) or {}
|
||||
}
|
||||
|
||||
config_path := os.join_path(tmp, 'config.ini')
|
||||
content := '[global]\nproject_name = test\n\n[tools]\nname = cli\nsources = src/tools/cli.cpp\n\n[tools]\nname = server\nsources = src/tools/server.cpp\nlibraries = core\n'
|
||||
os.write_file(config_path, content) or { panic(err) }
|
||||
|
||||
cfg := config.parse_config_file(config_path) or { panic(err) }
|
||||
|
||||
assert cfg.tools.len == 2
|
||||
assert cfg.tools[0].name == 'cli'
|
||||
assert cfg.tools[1].name == 'server'
|
||||
assert cfg.tools[1].libraries.contains('core')
|
||||
}
|
||||
|
||||
fn test_parse_dependencies_section() {
|
||||
tmp := new_temp_dir('lana_deps_parse')
|
||||
defer {
|
||||
os.rmdir_all(tmp) or {}
|
||||
}
|
||||
|
||||
config_path := os.join_path(tmp, 'config.ini')
|
||||
content := '[global]\nproject_name = test\ndependencies_dir = external\n\n[dependencies]\nname = json\nurl = https://example.com/json.tar.gz\narchive = json.tar.gz\nextract_to = json\nchecksum = abc123\nbuild_cmds = mkdir build; cd build; cmake ..\n'
|
||||
os.write_file(config_path, content) or { panic(err) }
|
||||
|
||||
cfg := config.parse_config_file(config_path) or { panic(err) }
|
||||
|
||||
assert cfg.dependencies_dir == 'external'
|
||||
assert cfg.dependencies.len == 1
|
||||
assert cfg.dependencies[0].name == 'json'
|
||||
assert cfg.dependencies[0].url == 'https://example.com/json.tar.gz'
|
||||
assert cfg.dependencies[0].archive == 'json.tar.gz'
|
||||
assert cfg.dependencies[0].extract_to == 'json'
|
||||
assert cfg.dependencies[0].checksum == 'abc123'
|
||||
// "mkdir build; cd build; cmake .." splits into 3 commands
|
||||
assert cfg.dependencies[0].build_cmds.len == 3
|
||||
assert cfg.dependencies[0].build_cmds[0] == 'mkdir build'
|
||||
assert cfg.dependencies[0].build_cmds[1] == 'cd build'
|
||||
assert cfg.dependencies[0].build_cmds[2] == 'cmake ..'
|
||||
}
|
||||
|
||||
fn test_config_inherits_global_values() {
|
||||
tmp := new_temp_dir('lana_inherit')
|
||||
defer {
|
||||
os.rmdir_all(tmp) or {}
|
||||
}
|
||||
|
||||
config_path := os.join_path(tmp, 'config.ini')
|
||||
content := '[global]\nproject_name = test\ndebug = true\noptimize = false\ninclude_dirs = global/include\ncflags = -DGLOBAL\n\n[shared_libs]\nname = mylib\nsources = src/lib.cpp\n\n[tools]\nname = mytool\nsources = src/main.cpp\n'
|
||||
os.write_file(config_path, content) or { panic(err) }
|
||||
|
||||
cfg := config.parse_config_file(config_path) or { panic(err) }
|
||||
|
||||
// Shared lib inherits global values
|
||||
assert cfg.shared_libs[0].debug == true
|
||||
assert cfg.shared_libs[0].optimize == false
|
||||
assert cfg.shared_libs[0].include_dirs.contains('global/include')
|
||||
assert cfg.shared_libs[0].cflags.contains('-DGLOBAL')
|
||||
|
||||
// Tool inherits global values
|
||||
assert cfg.tools[0].debug == true
|
||||
assert cfg.tools[0].optimize == false
|
||||
assert cfg.tools[0].include_dirs.contains('global/include')
|
||||
assert cfg.tools[0].cflags.contains('-DGLOBAL')
|
||||
}
|
||||
|
||||
fn test_target_can_override_global_values() {
|
||||
tmp := new_temp_dir('lana_override')
|
||||
defer {
|
||||
os.rmdir_all(tmp) or {}
|
||||
}
|
||||
|
||||
config_path := os.join_path(tmp, 'config.ini')
|
||||
content := '[global]\nproject_name = test\ndebug = true\noptimize = false\n\n[tools]\nname = release_tool\nsources = src/main.cpp\ndebug = false\noptimize = true\n'
|
||||
os.write_file(config_path, content) or { panic(err) }
|
||||
|
||||
cfg := config.parse_config_file(config_path) or { panic(err) }
|
||||
|
||||
// Tool overrides global debug/optimize
|
||||
assert cfg.tools[0].debug == false
|
||||
assert cfg.tools[0].optimize == true
|
||||
}
|
||||
|
||||
fn test_parse_config_file_missing_file_returns_error() {
|
||||
if _ := config.parse_config_file('/nonexistent/config.ini') {
|
||||
assert false, 'Expected error for nonexistent file'
|
||||
}
|
||||
// If we get here without the or block catching an error, the test passes
|
||||
}
|
||||
|
||||
fn test_comments_are_ignored() {
|
||||
tmp := new_temp_dir('lana_comments')
|
||||
defer {
|
||||
os.rmdir_all(tmp) or {}
|
||||
}
|
||||
|
||||
config_path := os.join_path(tmp, 'config.ini')
|
||||
content := '# This is a comment\n[global]\n# Another comment\nproject_name = test\n# Comment at end\n'
|
||||
os.write_file(config_path, content) or { panic(err) }
|
||||
|
||||
cfg := config.parse_config_file(config_path) or { panic(err) }
|
||||
|
||||
assert cfg.project_name == 'test'
|
||||
}
|
||||
|
||||
fn test_empty_lines_are_ignored() {
|
||||
tmp := new_temp_dir('lana_empty')
|
||||
defer {
|
||||
os.rmdir_all(tmp) or {}
|
||||
}
|
||||
|
||||
config_path := os.join_path(tmp, 'config.ini')
|
||||
content := '\n\n[global]\n\nproject_name = test\n\n\n'
|
||||
os.write_file(config_path, content) or { panic(err) }
|
||||
|
||||
cfg := config.parse_config_file(config_path) or { panic(err) }
|
||||
|
||||
assert cfg.project_name == 'test'
|
||||
}
|
||||
|
||||
fn test_quoted_values_are_trimmed() {
|
||||
tmp := new_temp_dir('lana_quoted')
|
||||
defer {
|
||||
os.rmdir_all(tmp) or {}
|
||||
}
|
||||
|
||||
config_path := os.join_path(tmp, 'config.ini')
|
||||
content := '[global]\nproject_name = "my_project"\ncompiler = \'clang++\'\n'
|
||||
os.write_file(config_path, content) or { panic(err) }
|
||||
|
||||
cfg := config.parse_config_file(config_path) or { panic(err) }
|
||||
|
||||
assert cfg.project_name == 'my_project'
|
||||
assert cfg.compiler == 'clang++'
|
||||
}
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
module tests
|
||||
|
||||
import config
|
||||
|
||||
fn test_default_config_has_expected_values() {
|
||||
cfg := config.default_config
|
||||
|
||||
assert cfg.src_dir == 'src'
|
||||
assert cfg.build_dir == 'build'
|
||||
assert cfg.bin_dir == 'bin'
|
||||
assert cfg.toolchain == 'gcc'
|
||||
assert cfg.debug == true
|
||||
assert cfg.optimize == false
|
||||
assert cfg.verbose == false
|
||||
assert cfg.shared_libs.len == 0
|
||||
assert cfg.tools.len == 0
|
||||
assert cfg.dependencies.len == 0
|
||||
}
|
||||
|
||||
fn test_shared_lib_config_has_default_output_dir() {
|
||||
lib := config.SharedLibConfig{
|
||||
name: 'test'
|
||||
}
|
||||
|
||||
assert lib.output_dir == 'bin/lib'
|
||||
}
|
||||
|
||||
fn test_tool_config_has_default_output_dir() {
|
||||
tool := config.ToolConfig{
|
||||
name: 'test'
|
||||
}
|
||||
|
||||
assert tool.output_dir == 'bin/tools'
|
||||
}
|
||||
|
||||
fn test_build_config_has_default_dependencies_dir() {
|
||||
cfg := config.BuildConfig{}
|
||||
|
||||
assert cfg.dependencies_dir == 'dependencies'
|
||||
}
|
||||
|
||||
fn test_build_config_has_parallel_compilation_enabled_by_default() {
|
||||
cfg := config.BuildConfig{}
|
||||
|
||||
assert cfg.parallel_compilation == true
|
||||
}
|
||||
|
||||
fn test_build_config_has_default_compiler() {
|
||||
cfg := config.BuildConfig{}
|
||||
|
||||
assert cfg.compiler == 'g++'
|
||||
}
|
||||
|
||||
fn test_get_target_config_values_for_shared_lib() {
|
||||
lib := config.SharedLibConfig{
|
||||
name: 'test'
|
||||
debug: true
|
||||
optimize: false
|
||||
verbose: true
|
||||
include_dirs: ['include']
|
||||
cflags: ['-DTEST']
|
||||
}
|
||||
|
||||
target := config.TargetConfig(lib)
|
||||
is_shared, use_debug, use_optimize, use_verbose, includes, cflags := config.get_target_config_values(target)
|
||||
|
||||
assert is_shared == true
|
||||
assert use_debug == true
|
||||
assert use_optimize == false
|
||||
assert use_verbose == true
|
||||
assert includes.contains('include')
|
||||
assert cflags.contains('-DTEST')
|
||||
}
|
||||
|
||||
fn test_get_target_config_values_for_tool() {
|
||||
tool := config.ToolConfig{
|
||||
name: 'test'
|
||||
debug: false
|
||||
optimize: true
|
||||
verbose: false
|
||||
include_dirs: ['src']
|
||||
cflags: ['-O3']
|
||||
}
|
||||
|
||||
target := config.TargetConfig(tool)
|
||||
is_shared, use_debug, use_optimize, use_verbose, includes, cflags := config.get_target_config_values(target)
|
||||
|
||||
assert is_shared == false
|
||||
assert use_debug == false
|
||||
assert use_optimize == true
|
||||
assert use_verbose == false
|
||||
assert includes.contains('src')
|
||||
assert cflags.contains('-O3')
|
||||
}
|
||||
|
||||
fn test_build_compiler_command_basic() {
|
||||
cfg := config.BuildConfig{
|
||||
compiler: 'g++'
|
||||
debug: true
|
||||
include_dirs: ['include']
|
||||
cflags: ['-DTEST']
|
||||
}
|
||||
|
||||
cmd := config.build_compiler_command('src/main.cpp', 'build/main.o', cfg)
|
||||
|
||||
assert cmd.starts_with('g++')
|
||||
assert cmd.contains('-c')
|
||||
assert cmd.contains('-Iinclude')
|
||||
assert cmd.contains('-g')
|
||||
assert cmd.contains('-O0')
|
||||
assert cmd.contains('-Wall')
|
||||
assert cmd.contains('-Wextra')
|
||||
assert cmd.contains('-DTEST')
|
||||
assert cmd.contains('src/main.cpp')
|
||||
assert cmd.contains('-o build/main.o')
|
||||
}
|
||||
|
||||
fn test_build_compiler_command_optimized() {
|
||||
cfg := config.BuildConfig{
|
||||
compiler: 'clang++'
|
||||
debug: false
|
||||
optimize: true
|
||||
}
|
||||
|
||||
cmd := config.build_compiler_command('main.cpp', 'main.o', cfg)
|
||||
|
||||
assert cmd.starts_with('clang++')
|
||||
assert cmd.contains('-O3')
|
||||
assert !cmd.contains('-g')
|
||||
}
|
||||
|
||||
fn test_build_compiler_command_default_optimization() {
|
||||
cfg := config.BuildConfig{
|
||||
compiler: 'g++'
|
||||
debug: false
|
||||
optimize: false
|
||||
}
|
||||
|
||||
cmd := config.build_compiler_command('main.cpp', 'main.o', cfg)
|
||||
|
||||
assert cmd.contains('-O2')
|
||||
assert !cmd.contains('-O3')
|
||||
assert !cmd.contains('-g')
|
||||
}
|
||||
|
|
@ -0,0 +1,199 @@
|
|||
module tests
|
||||
|
||||
import os
|
||||
import config
|
||||
|
||||
fn test_find_source_files_finds_cpp_files() {
|
||||
tmp := new_temp_dir('lana_find_cpp')
|
||||
defer {
|
||||
os.rmdir_all(tmp) or {}
|
||||
}
|
||||
|
||||
os.write_file(os.join_path(tmp, 'main.cpp'), '// cpp') or { panic(err) }
|
||||
os.write_file(os.join_path(tmp, 'utils.cpp'), '// cpp') or { panic(err) }
|
||||
os.write_file(os.join_path(tmp, 'header.h'), '// header') or { panic(err) }
|
||||
os.write_file(os.join_path(tmp, 'readme.txt'), '// text') or { panic(err) }
|
||||
|
||||
files := config.find_source_files(tmp) or { panic(err) }
|
||||
|
||||
assert files.len == 2
|
||||
assert files.any(it.ends_with('main.cpp'))
|
||||
assert files.any(it.ends_with('utils.cpp'))
|
||||
}
|
||||
|
||||
fn test_find_source_files_finds_cc_files() {
|
||||
tmp := new_temp_dir('lana_find_cc')
|
||||
defer {
|
||||
os.rmdir_all(tmp) or {}
|
||||
}
|
||||
|
||||
os.write_file(os.join_path(tmp, 'main.cc'), '// cc') or { panic(err) }
|
||||
|
||||
files := config.find_source_files(tmp) or { panic(err) }
|
||||
|
||||
assert files.len == 1
|
||||
assert files[0].ends_with('main.cc')
|
||||
}
|
||||
|
||||
fn test_find_source_files_finds_cxx_files() {
|
||||
tmp := new_temp_dir('lana_find_cxx')
|
||||
defer {
|
||||
os.rmdir_all(tmp) or {}
|
||||
}
|
||||
|
||||
os.write_file(os.join_path(tmp, 'main.cxx'), '// cxx') or { panic(err) }
|
||||
|
||||
files := config.find_source_files(tmp) or { panic(err) }
|
||||
|
||||
assert files.len == 1
|
||||
assert files[0].ends_with('main.cxx')
|
||||
}
|
||||
|
||||
fn test_find_source_files_searches_subdirectories() {
|
||||
tmp := new_temp_dir('lana_find_subdir')
|
||||
defer {
|
||||
os.rmdir_all(tmp) or {}
|
||||
}
|
||||
|
||||
subdir := os.join_path(tmp, 'lib', 'core')
|
||||
os.mkdir_all(subdir) or { panic(err) }
|
||||
|
||||
os.write_file(os.join_path(tmp, 'main.cpp'), '// root') or { panic(err) }
|
||||
os.write_file(os.join_path(subdir, 'core.cpp'), '// subdir') or { panic(err) }
|
||||
|
||||
files := config.find_source_files(tmp) or { panic(err) }
|
||||
|
||||
assert files.len == 2
|
||||
assert files.any(it.ends_with('main.cpp'))
|
||||
assert files.any(it.ends_with('core.cpp'))
|
||||
}
|
||||
|
||||
fn test_find_source_files_returns_error_for_nonexistent_dir() {
|
||||
if _ := config.find_source_files('/nonexistent/directory') {
|
||||
assert false, 'Expected error for nonexistent directory'
|
||||
}
|
||||
// If we get here without the or block catching an error, the test passes
|
||||
}
|
||||
|
||||
fn test_find_source_files_returns_empty_for_empty_dir() {
|
||||
tmp := new_temp_dir('lana_find_empty')
|
||||
defer {
|
||||
os.rmdir_all(tmp) or {}
|
||||
}
|
||||
|
||||
files := config.find_source_files(tmp) or { panic(err) }
|
||||
|
||||
assert files.len == 0
|
||||
}
|
||||
|
||||
fn test_find_source_files_ignores_hidden_directories() {
|
||||
tmp := new_temp_dir('lana_find_hidden')
|
||||
defer {
|
||||
os.rmdir_all(tmp) or {}
|
||||
}
|
||||
|
||||
hidden_dir := os.join_path(tmp, '.hidden')
|
||||
os.mkdir_all(hidden_dir) or { panic(err) }
|
||||
|
||||
os.write_file(os.join_path(tmp, 'visible.cpp'), '// visible') or { panic(err) }
|
||||
os.write_file(os.join_path(hidden_dir, 'hidden.cpp'), '// hidden') or { panic(err) }
|
||||
|
||||
files := config.find_source_files(tmp) or { panic(err) }
|
||||
|
||||
// Note: The current implementation doesn't filter hidden dirs,
|
||||
// so this test documents current behavior
|
||||
assert files.any(it.ends_with('visible.cpp'))
|
||||
}
|
||||
|
||||
fn test_build_directives_parse_all_directive_types() {
|
||||
tmp := new_temp_dir('lana_all_directives')
|
||||
defer {
|
||||
os.rmdir_all(tmp) or {}
|
||||
}
|
||||
|
||||
src_dir := os.join_path(tmp, 'src')
|
||||
os.mkdir_all(src_dir) or { panic(err) }
|
||||
|
||||
source := '// build-directive: unit-name(myunit)\n// build-directive: depends-units(dep1, dep2)\n// build-directive: link(lib1.so, lib2.so)\n// build-directive: out(bin/myunit)\n// build-directive: cflags(-O2 -DNDEBUG)\n// build-directive: ldflags(-lpthread -lm)\n// build-directive: shared(true)\nint main() { return 0; }\n'
|
||||
os.write_file(os.join_path(src_dir, 'myunit.cpp'), source) or { panic(err) }
|
||||
|
||||
mut cfg := config.BuildConfig{
|
||||
src_dir: src_dir
|
||||
}
|
||||
|
||||
cfg.parse_build_directives() or { panic(err) }
|
||||
|
||||
assert cfg.build_directives.len == 1
|
||||
|
||||
d := cfg.build_directives[0]
|
||||
assert d.unit_name == 'myunit'
|
||||
assert d.depends_units.len == 2
|
||||
assert d.link_libs.len == 2
|
||||
assert d.output_path == 'bin/myunit'
|
||||
assert d.cflags.len == 2
|
||||
assert d.ldflags.len == 2
|
||||
assert d.is_shared == true
|
||||
}
|
||||
|
||||
fn test_build_directives_ignore_non_directive_comments() {
|
||||
tmp := new_temp_dir('lana_ignore_comments')
|
||||
defer {
|
||||
os.rmdir_all(tmp) or {}
|
||||
}
|
||||
|
||||
src_dir := os.join_path(tmp, 'src')
|
||||
os.mkdir_all(src_dir) or { panic(err) }
|
||||
|
||||
source := '// This is a regular comment\n// Another comment\n// build-directive: unit-name(test)\n/* Block comment */\nint main() { return 0; }\n'
|
||||
os.write_file(os.join_path(src_dir, 'test.cpp'), source) or { panic(err) }
|
||||
|
||||
mut cfg := config.BuildConfig{
|
||||
src_dir: src_dir
|
||||
}
|
||||
|
||||
cfg.parse_build_directives() or { panic(err) }
|
||||
|
||||
assert cfg.build_directives.len == 1
|
||||
assert cfg.build_directives[0].unit_name == 'test'
|
||||
}
|
||||
|
||||
fn test_build_directives_handles_empty_src_dir() {
|
||||
tmp := new_temp_dir('lana_empty_src')
|
||||
defer {
|
||||
os.rmdir_all(tmp) or {}
|
||||
}
|
||||
|
||||
src_dir := os.join_path(tmp, 'src')
|
||||
os.mkdir_all(src_dir) or { panic(err) }
|
||||
|
||||
mut cfg := config.BuildConfig{
|
||||
src_dir: src_dir
|
||||
}
|
||||
|
||||
cfg.parse_build_directives() or { panic(err) }
|
||||
|
||||
assert cfg.build_directives.len == 0
|
||||
}
|
||||
|
||||
fn test_build_directives_skips_files_without_unit_name() {
|
||||
tmp := new_temp_dir('lana_no_unit')
|
||||
defer {
|
||||
os.rmdir_all(tmp) or {}
|
||||
}
|
||||
|
||||
src_dir := os.join_path(tmp, 'src')
|
||||
os.mkdir_all(src_dir) or { panic(err) }
|
||||
|
||||
// File with directives but no unit-name
|
||||
source := '// build-directive: cflags(-O2)\n// build-directive: ldflags(-lm)\nint main() { return 0; }\n'
|
||||
os.write_file(os.join_path(src_dir, 'nounit.cpp'), source) or { panic(err) }
|
||||
|
||||
mut cfg := config.BuildConfig{
|
||||
src_dir: src_dir
|
||||
}
|
||||
|
||||
cfg.parse_build_directives() or { panic(err) }
|
||||
|
||||
// Should not be added without unit-name
|
||||
assert cfg.build_directives.len == 0
|
||||
}
|
||||
|
|
@ -0,0 +1,221 @@
|
|||
module tests
|
||||
|
||||
import config
|
||||
|
||||
fn test_gcc_toolchain_compile_command_includes_debug_flags() {
|
||||
cfg := config.BuildConfig{
|
||||
debug: true
|
||||
optimize: false
|
||||
compiler: 'g++'
|
||||
toolchain: 'gcc'
|
||||
include_dirs: ['include']
|
||||
cflags: ['-DTEST']
|
||||
}
|
||||
tc := config.get_toolchain(cfg)
|
||||
|
||||
target := config.TargetConfig(config.ToolConfig{
|
||||
name: 'test_tool'
|
||||
debug: true
|
||||
})
|
||||
|
||||
cmd := tc.compile_command('test.cpp', 'test.o', &cfg, target)
|
||||
|
||||
assert cmd.contains('-g')
|
||||
assert cmd.contains('-O0')
|
||||
assert cmd.contains('-Iinclude')
|
||||
assert cmd.contains('-DTEST')
|
||||
assert cmd.contains('-Wall')
|
||||
assert cmd.contains('-Wextra')
|
||||
assert cmd.contains('test.cpp')
|
||||
assert cmd.contains('-o test.o')
|
||||
}
|
||||
|
||||
fn test_gcc_toolchain_compile_command_includes_optimize_flags() {
|
||||
cfg := config.BuildConfig{
|
||||
debug: false
|
||||
optimize: true
|
||||
compiler: 'g++'
|
||||
toolchain: 'gcc'
|
||||
}
|
||||
tc := config.get_toolchain(cfg)
|
||||
|
||||
target := config.TargetConfig(config.ToolConfig{
|
||||
name: 'test_tool'
|
||||
optimize: true
|
||||
})
|
||||
|
||||
cmd := tc.compile_command('src/main.cpp', 'build/main.o', &cfg, target)
|
||||
|
||||
assert cmd.contains('-O3')
|
||||
assert !cmd.contains('-g')
|
||||
assert !cmd.contains('-O0')
|
||||
}
|
||||
|
||||
fn test_clang_toolchain_compile_command() {
|
||||
cfg := config.BuildConfig{
|
||||
debug: true
|
||||
compiler: 'clang++'
|
||||
toolchain: 'clang'
|
||||
}
|
||||
tc := config.get_toolchain(cfg)
|
||||
|
||||
target := config.TargetConfig(config.ToolConfig{
|
||||
name: 'test_tool'
|
||||
debug: true
|
||||
})
|
||||
|
||||
cmd := tc.compile_command('main.cpp', 'main.o', &cfg, target)
|
||||
|
||||
assert cmd.starts_with('clang++')
|
||||
assert cmd.contains('-c')
|
||||
assert cmd.contains('-g')
|
||||
}
|
||||
|
||||
fn test_shared_lib_compile_includes_fpic() {
|
||||
cfg := config.BuildConfig{
|
||||
debug: false
|
||||
compiler: 'g++'
|
||||
toolchain: 'gcc'
|
||||
}
|
||||
tc := config.get_toolchain(cfg)
|
||||
|
||||
target := config.TargetConfig(config.SharedLibConfig{
|
||||
name: 'mylib'
|
||||
})
|
||||
|
||||
cmd := tc.compile_command('lib.cpp', 'lib.o', &cfg, target)
|
||||
|
||||
assert cmd.contains('-fPIC')
|
||||
}
|
||||
|
||||
fn test_tool_compile_does_not_include_fpic() {
|
||||
cfg := config.BuildConfig{
|
||||
debug: false
|
||||
compiler: 'g++'
|
||||
toolchain: 'gcc'
|
||||
}
|
||||
tc := config.get_toolchain(cfg)
|
||||
|
||||
target := config.TargetConfig(config.ToolConfig{
|
||||
name: 'mytool'
|
||||
})
|
||||
|
||||
cmd := tc.compile_command('main.cpp', 'main.o', &cfg, target)
|
||||
|
||||
assert !cmd.contains('-fPIC')
|
||||
}
|
||||
|
||||
fn test_shared_link_command_includes_shared_flag() {
|
||||
cfg := config.BuildConfig{
|
||||
compiler: 'g++'
|
||||
toolchain: 'gcc'
|
||||
bin_dir: 'bin'
|
||||
}
|
||||
tc := config.get_toolchain(cfg)
|
||||
|
||||
lib_cfg := config.SharedLibConfig{
|
||||
name: 'mylib'
|
||||
libraries: ['dep']
|
||||
}
|
||||
|
||||
cmd := tc.shared_link_command(['obj1.o', 'obj2.o'], 'mylib', 'bin/lib', &cfg, lib_cfg)
|
||||
|
||||
assert cmd.contains('-shared')
|
||||
assert cmd.contains('obj1.o')
|
||||
assert cmd.contains('obj2.o')
|
||||
assert cmd.contains('-o bin/lib/mylib.so')
|
||||
}
|
||||
|
||||
fn test_tool_link_command_links_libraries() {
|
||||
cfg := config.BuildConfig{
|
||||
compiler: 'g++'
|
||||
toolchain: 'gcc'
|
||||
bin_dir: 'bin'
|
||||
libraries: ['pthread']
|
||||
}
|
||||
tc := config.get_toolchain(cfg)
|
||||
|
||||
tool_cfg := config.ToolConfig{
|
||||
name: 'mytool'
|
||||
libraries: ['core']
|
||||
}
|
||||
|
||||
cmd := tc.tool_link_command(['main.o'], 'bin/tools/mytool', &cfg, tool_cfg)
|
||||
|
||||
assert cmd.contains('main.o')
|
||||
assert cmd.contains('-lpthread')
|
||||
assert cmd.contains('-o bin/tools/mytool')
|
||||
}
|
||||
|
||||
fn test_get_toolchain_defaults_to_gcc() {
|
||||
cfg := config.BuildConfig{
|
||||
toolchain: ''
|
||||
compiler: ''
|
||||
}
|
||||
tc := config.get_toolchain(cfg)
|
||||
|
||||
assert tc.description() == 'gcc'
|
||||
}
|
||||
|
||||
fn test_get_toolchain_returns_clang_when_specified() {
|
||||
cfg := config.BuildConfig{
|
||||
toolchain: 'clang'
|
||||
compiler: 'clang++'
|
||||
}
|
||||
tc := config.get_toolchain(cfg)
|
||||
|
||||
assert tc.description() == 'clang'
|
||||
}
|
||||
|
||||
fn test_compile_command_includes_lib_search_paths() {
|
||||
cfg := config.BuildConfig{
|
||||
compiler: 'g++'
|
||||
toolchain: 'gcc'
|
||||
lib_search_paths: ['/usr/local/lib', 'deps/lib']
|
||||
}
|
||||
tc := config.get_toolchain(cfg)
|
||||
|
||||
target := config.TargetConfig(config.ToolConfig{name: 'test'})
|
||||
cmd := tc.compile_command('test.cpp', 'test.o', &cfg, target)
|
||||
|
||||
assert cmd.contains('-L/usr/local/lib')
|
||||
assert cmd.contains('-Ldeps/lib')
|
||||
}
|
||||
|
||||
fn test_target_specific_include_dirs_added() {
|
||||
cfg := config.BuildConfig{
|
||||
compiler: 'g++'
|
||||
toolchain: 'gcc'
|
||||
include_dirs: ['global/include']
|
||||
}
|
||||
tc := config.get_toolchain(cfg)
|
||||
|
||||
target := config.TargetConfig(config.ToolConfig{
|
||||
name: 'test'
|
||||
include_dirs: ['tool/include']
|
||||
})
|
||||
|
||||
cmd := tc.compile_command('test.cpp', 'test.o', &cfg, target)
|
||||
|
||||
assert cmd.contains('-Iglobal/include')
|
||||
assert cmd.contains('-Itool/include')
|
||||
}
|
||||
|
||||
fn test_target_specific_cflags_added() {
|
||||
cfg := config.BuildConfig{
|
||||
compiler: 'g++'
|
||||
toolchain: 'gcc'
|
||||
cflags: ['-DGLOBAL']
|
||||
}
|
||||
tc := config.get_toolchain(cfg)
|
||||
|
||||
target := config.TargetConfig(config.ToolConfig{
|
||||
name: 'test'
|
||||
cflags: ['-DLOCAL']
|
||||
})
|
||||
|
||||
cmd := tc.compile_command('test.cpp', 'test.o', &cfg, target)
|
||||
|
||||
assert cmd.contains('-DGLOBAL')
|
||||
assert cmd.contains('-DLOCAL')
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
module util
|
||||
|
||||
import os
|
||||
|
||||
// shitty thing I need to pass around or else the compiler shits itself
|
||||
pub struct ToolInfo {
|
||||
pub:
|
||||
name string
|
||||
output_dir string
|
||||
}
|
||||
|
||||
// get_main_executable finds the main executable path given project info and tools list.
|
||||
// It first looks for a tool matching the project name, then falls back to the
|
||||
// first tool, and finally the legacy bin/<project_name> location.
|
||||
pub fn get_main_executable(project_name string, bin_dir string, tools []ToolInfo) string {
|
||||
// First try to find a tool with the project name
|
||||
for tool in tools {
|
||||
if tool.name == project_name {
|
||||
return os.join_path(tool.output_dir, tool.name)
|
||||
}
|
||||
}
|
||||
|
||||
// Then try the first tool
|
||||
if tools.len > 0 {
|
||||
return os.join_path(tools[0].output_dir, tools[0].name)
|
||||
}
|
||||
|
||||
// Fallback to old behavior
|
||||
return os.join_path(bin_dir, project_name)
|
||||
}
|
||||
|
||||
// find_source_files recursively finds all C++ source files (.cpp, .cc, .cxx) in a directory.
|
||||
pub fn find_source_files(dir string) ![]string {
|
||||
mut files := []string{}
|
||||
|
||||
if !os.is_dir(dir) {
|
||||
return error('Source directory does not exist: ${dir}')
|
||||
}
|
||||
|
||||
items := os.ls(dir) or { return error('Failed to list directory: ${dir}') }
|
||||
|
||||
for item in items {
|
||||
full_path := os.join_path(dir, item)
|
||||
if os.is_file(full_path) {
|
||||
if item.ends_with('.cpp') || item.ends_with('.cc') || item.ends_with('.cxx') {
|
||||
files << full_path
|
||||
}
|
||||
} else if os.is_dir(full_path) {
|
||||
// Recursively search subdirectories
|
||||
sub_files := find_source_files(full_path)!
|
||||
files << sub_files
|
||||
}
|
||||
}
|
||||
|
||||
return files
|
||||
}
|
||||
Loading…
Reference in New Issue