Initial commit
commit
efb70b3662
|
@ -0,0 +1,161 @@
|
|||
module builder
|
||||
|
||||
import os
|
||||
import config
|
||||
import deps
|
||||
|
||||
pub fn build(mut build_config config.BuildConfig) ! {
|
||||
println('Building ${build_config.project_name}...')
|
||||
|
||||
// Create directories
|
||||
os.mkdir_all(build_config.build_dir) or { return error('Failed to create build directory') }
|
||||
os.mkdir_all(build_config.bin_dir) or { return error('Failed to create bin directory') }
|
||||
|
||||
// Find all source files
|
||||
source_files := find_source_files(build_config.src_dir) or {
|
||||
return error('Failed to find source files in ${build_config.src_dir}')
|
||||
}
|
||||
|
||||
if source_files.len == 0 {
|
||||
return error('No source files found in ${build_config.src_dir}')
|
||||
}
|
||||
|
||||
mut object_files := []string{}
|
||||
|
||||
// Compile each source file
|
||||
for src_file in source_files {
|
||||
obj_file := src_file.replace(build_config.src_dir, build_config.build_dir).replace('.cpp', '.o')
|
||||
|
||||
// Create object directory if needed
|
||||
obj_dir := os.dir(obj_file)
|
||||
os.mkdir_all(obj_dir) or { return error('Failed to create object directory: ${obj_dir}') }
|
||||
|
||||
// Check if we need to recompile
|
||||
if needs_recompile(src_file, obj_file) {
|
||||
println('Compiling ${src_file}...')
|
||||
object_files << compile_file(src_file, obj_file, build_config) or {
|
||||
return error('Failed to compile ${src_file}')
|
||||
}
|
||||
} else {
|
||||
if build_config.verbose {
|
||||
println('Using cached ${obj_file}')
|
||||
}
|
||||
object_files << obj_file
|
||||
}
|
||||
}
|
||||
|
||||
// Link object files
|
||||
executable := os.join_path(build_config.bin_dir, build_config.project_name)
|
||||
println('Linking ${executable}...')
|
||||
link_objects(object_files, executable, build_config) or { return error('Failed to link executable') }
|
||||
|
||||
println('Build completed successfully!')
|
||||
}
|
||||
|
||||
pub fn clean(build_config config.BuildConfig) {
|
||||
println('Cleaning build files...')
|
||||
|
||||
// Remove build directory
|
||||
if os.is_dir(build_config.build_dir) {
|
||||
os.rmdir_all(build_config.build_dir) or {
|
||||
println('Warning: Failed to remove ${build_config.build_dir}: ${err}')
|
||||
}
|
||||
println('Removed ${build_config.build_dir}')
|
||||
}
|
||||
|
||||
// Remove executable
|
||||
executable := os.join_path(build_config.bin_dir, build_config.project_name)
|
||||
if os.is_file(executable) {
|
||||
os.rm(executable) or {
|
||||
println('Warning: Failed to remove ${executable}: ${err}')
|
||||
}
|
||||
println('Removed ${executable}')
|
||||
}
|
||||
|
||||
println('Clean completed!')
|
||||
}
|
||||
|
||||
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 {
|
||||
src_mtime := os.file_last_mod_unix(source_file)
|
||||
obj_mtime := if os.is_file(object_file) {
|
||||
os.file_last_mod_unix(object_file)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
|
||||
// Source is newer than object
|
||||
if src_mtime > obj_mtime {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check dependencies
|
||||
dependencies := deps.extract_dependencies(source_file) or { return true }
|
||||
for dep in dependencies {
|
||||
if !os.is_file(dep) {
|
||||
return true
|
||||
}
|
||||
dep_mtime := os.file_last_mod_unix(dep)
|
||||
if dep_mtime > obj_mtime {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
fn compile_file(source_file string, object_file string, build_config config.BuildConfig) !string {
|
||||
cmd := config.build_compiler_command(source_file, object_file, build_config)
|
||||
|
||||
if build_config.verbose {
|
||||
println('Compile command: ${cmd}')
|
||||
}
|
||||
|
||||
res := os.execute(cmd)
|
||||
if res.exit_code != 0 {
|
||||
return error('Compilation failed with exit code ${res.exit_code}:\n${res.output}')
|
||||
}
|
||||
|
||||
// Generate dependency file
|
||||
dep_file := object_file.replace('.o', '.d')
|
||||
deps.generate_dependency_file(source_file, object_file, dep_file)
|
||||
|
||||
return object_file
|
||||
}
|
||||
|
||||
fn link_objects(object_files []string, executable string, build_config config.BuildConfig) ! {
|
||||
cmd := config.build_linker_command(object_files, executable, build_config)
|
||||
|
||||
if build_config.verbose {
|
||||
println('Link command: ${cmd}')
|
||||
}
|
||||
|
||||
res := os.execute(cmd)
|
||||
if res.exit_code != 0 {
|
||||
return error('Linking failed with exit code ${res.exit_code}:\n${res.output}')
|
||||
}
|
||||
}
|
|
@ -0,0 +1,181 @@
|
|||
module config
|
||||
|
||||
import os
|
||||
|
||||
// BuildConfig holds the configuration for the project
|
||||
pub struct BuildConfig {
|
||||
pub mut:
|
||||
project_name string
|
||||
src_dir string
|
||||
build_dir string
|
||||
bin_dir string
|
||||
include_dirs []string
|
||||
libraries []string
|
||||
cflags []string
|
||||
ldflags []string
|
||||
debug bool
|
||||
optimize bool
|
||||
verbose bool
|
||||
}
|
||||
|
||||
// default config
|
||||
pub const default_config = BuildConfig{
|
||||
project_name: 'project'
|
||||
src_dir: 'src'
|
||||
build_dir: 'build'
|
||||
bin_dir: 'bin'
|
||||
include_dirs: []
|
||||
libraries: []
|
||||
cflags: []
|
||||
ldflags: []
|
||||
debug: true
|
||||
optimize: false
|
||||
verbose: false
|
||||
}
|
||||
|
||||
pub fn parse_args() !BuildConfig {
|
||||
mut build_config := default_config
|
||||
mut i := 2 // Skip program name and command
|
||||
|
||||
for i < os.args.len {
|
||||
arg := os.args[i]
|
||||
match arg {
|
||||
'-d', '--debug' { build_config.debug = true; build_config.optimize = false }
|
||||
'-O', '--optimize' { build_config.optimize = true; build_config.debug = false }
|
||||
'-v', '--verbose' { build_config.verbose = true }
|
||||
'-o', '--output' {
|
||||
if i + 1 < os.args.len {
|
||||
build_config.project_name = os.args[i + 1]
|
||||
i++
|
||||
}
|
||||
}
|
||||
'-I' {
|
||||
if i + 1 < os.args.len {
|
||||
build_config.include_dirs << os.args[i + 1]
|
||||
i++
|
||||
}
|
||||
}
|
||||
'-l' {
|
||||
if i + 1 < os.args.len {
|
||||
build_config.libraries << os.args[i + 1]
|
||||
i++
|
||||
}
|
||||
}
|
||||
'--config' {
|
||||
if i + 1 < os.args.len {
|
||||
build_config = parse_config_file(os.args[i + 1])!
|
||||
i++
|
||||
}
|
||||
}
|
||||
else {
|
||||
if !arg.starts_with('-') {
|
||||
build_config.project_name = arg
|
||||
}
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
return build_config
|
||||
}
|
||||
|
||||
fn parse_config_file(filename string) !BuildConfig {
|
||||
content := os.read_file(filename) or { return error('Cannot read config file: ${filename}') }
|
||||
mut build_config := default_config
|
||||
lines := content.split_into_lines()
|
||||
|
||||
for line in lines {
|
||||
if line.starts_with('#') || line.trim_space() == '' { continue }
|
||||
|
||||
parts := line.split('=')
|
||||
if parts.len == 2 {
|
||||
key := parts[0].trim_space()
|
||||
value := parts[1].trim_space().trim('"\'')
|
||||
|
||||
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 }
|
||||
'debug' { build_config.debug = value == 'true' }
|
||||
'optimize' { build_config.optimize = value == 'true' }
|
||||
'verbose' { build_config.verbose = value == 'true' }
|
||||
'include_dirs' {
|
||||
dirs := value.split(',')
|
||||
for dir in dirs { build_config.include_dirs << dir.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() }
|
||||
}
|
||||
else {}
|
||||
}
|
||||
}
|
||||
}
|
||||
return build_config
|
||||
}
|
||||
|
||||
// Utility function to build compiler command
|
||||
pub fn build_compiler_command(source_file string, object_file string, build_config BuildConfig) string {
|
||||
mut cmd := 'g++ -c'
|
||||
|
||||
// Add include directories
|
||||
for include_dir in build_config.include_dirs {
|
||||
cmd += ' -I${include_dir}'
|
||||
}
|
||||
|
||||
// Add debug/optimization flags
|
||||
if build_config.debug {
|
||||
cmd += ' -g -O0'
|
||||
} else if build_config.optimize {
|
||||
cmd += ' -O3'
|
||||
} else {
|
||||
cmd += ' -O2'
|
||||
}
|
||||
|
||||
// Add standard flags
|
||||
cmd += ' -Wall -Wextra -std=c++17'
|
||||
|
||||
// Add custom CFLAGS
|
||||
for flag in build_config.cflags {
|
||||
cmd += ' ${flag}'
|
||||
}
|
||||
|
||||
cmd += ' ${source_file} -o ${object_file}'
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Utility function to build linker command
|
||||
pub fn build_linker_command(object_files []string, executable string, build_config BuildConfig) string {
|
||||
mut cmd := 'g++'
|
||||
|
||||
// Add debug flags for linking
|
||||
if build_config.debug {
|
||||
cmd += ' -g'
|
||||
}
|
||||
|
||||
// Add object files
|
||||
for obj_file in object_files {
|
||||
cmd += ' ${obj_file}'
|
||||
}
|
||||
|
||||
// Add libraries
|
||||
for library in build_config.libraries {
|
||||
cmd += ' -l${library}'
|
||||
}
|
||||
|
||||
// Add custom LDFLAGS
|
||||
for flag in build_config.ldflags {
|
||||
cmd += ' ${flag}'
|
||||
}
|
||||
|
||||
cmd += ' -o ${executable}'
|
||||
return cmd
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
module deps
|
||||
|
||||
import os
|
||||
|
||||
pub fn extract_dependencies(source_file string) ![]string {
|
||||
mut dependencies := []string{}
|
||||
content := os.read_file(source_file) or { return []string{} }
|
||||
|
||||
mut in_string := false
|
||||
mut current_string_char := rune(0)
|
||||
mut i := 0
|
||||
|
||||
for i < content.len {
|
||||
c := content[i]
|
||||
|
||||
// Handle string literals
|
||||
if (c == `"` || c == `'`) && !in_string {
|
||||
in_string = true
|
||||
current_string_char = c
|
||||
} else if c == current_string_char && in_string {
|
||||
in_string = false
|
||||
current_string_char = rune(0)
|
||||
} else if !in_string {
|
||||
if c == `#` && i + 1 < content.len && content[i + 1] == `i` {
|
||||
// Found #include
|
||||
i += 7 // skip "#include"
|
||||
for i < content.len && content[i].is_space() {
|
||||
i++
|
||||
}
|
||||
|
||||
if i < content.len && (content[i] == `"` || content[i] == `<`) {
|
||||
mut quote_char := content[i]
|
||||
i++
|
||||
mut include_path := []u8{}
|
||||
|
||||
for i < content.len && content[i] != quote_char {
|
||||
include_path << content[i]
|
||||
i++
|
||||
}
|
||||
|
||||
if include_path.len > 0 {
|
||||
include_name := include_path.bytestr()
|
||||
if include_name.contains('/') || include_name.contains('\\') {
|
||||
// Relative path
|
||||
dependencies << include_name
|
||||
} else {
|
||||
// System include - we could search standard paths
|
||||
// but for now just add the name
|
||||
dependencies << include_name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i++
|
||||
}
|
||||
|
||||
return dependencies
|
||||
}
|
||||
|
||||
pub fn generate_dependency_file(source_file string, object_file string, dep_file string) {
|
||||
dependencies := extract_dependencies(source_file) or { return }
|
||||
|
||||
mut content := '${object_file}: ${source_file}\n'
|
||||
for dep in dependencies {
|
||||
content += '\t${dep}\n'
|
||||
}
|
||||
|
||||
os.write_file(dep_file, content) or { }
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
module help
|
||||
|
||||
pub fn show_help() {
|
||||
println('lana - Vlang C++ Build System')
|
||||
println('Usage: lana [command] [options]')
|
||||
println('')
|
||||
println('Commands:')
|
||||
println(' build Build the project')
|
||||
println(' clean Clean build files')
|
||||
println(' run Build and run the project')
|
||||
println(' init <name> Initialize new project')
|
||||
println('')
|
||||
println('Options:')
|
||||
println(' -d, --debug Enable debug mode')
|
||||
println(' -O, --optimize Enable optimization')
|
||||
println(' -v, --verbose Verbose output')
|
||||
println(' -o, --output Set output name')
|
||||
println(' -I <dir> Add include directory')
|
||||
println(' -l <lib> Add library')
|
||||
println(' --config <file> Use config file')
|
||||
println('')
|
||||
println('Examples:')
|
||||
println(' lana build -d -I include/mylib')
|
||||
println(' lana run')
|
||||
println(' lana init myproject')
|
||||
println('')
|
||||
println('Project Structure:')
|
||||
println(' src/ - Source files (.cpp, .cc, .cxx)')
|
||||
println(' include/ - Header files (.h, .hpp)')
|
||||
println(' build/ - Object files and intermediates')
|
||||
println(' bin/ - Executable output')
|
||||
println(' config.ini - Build configuration')
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
module initializer
|
||||
|
||||
import os
|
||||
|
||||
pub fn init_project(project_name string) {
|
||||
println('Initializing C++ project: ${project_name}')
|
||||
|
||||
// Create directory structure
|
||||
dirs := ['src', 'include', 'build', 'bin']
|
||||
for dir in dirs {
|
||||
full_path := os.join_path(project_name, dir)
|
||||
os.mkdir_all(full_path) or {
|
||||
println('Warning: Failed to create ${full_path}: ${err}')
|
||||
}
|
||||
}
|
||||
|
||||
// Create basic CMakeLists.txt
|
||||
cmake_content := r'
|
||||
# CMakeLists.txt
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(${project_name})
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
# Add executable
|
||||
add_executable(${project_name} src/main.cpp)
|
||||
|
||||
# Add include directories
|
||||
target_include_directories(${project_name} PRIVATE include)
|
||||
'
|
||||
os.write_file(os.join_path(project_name, 'CMakeLists.txt'), cmake_content) or { }
|
||||
|
||||
// Create main.cpp
|
||||
main_content := r'
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
std::cout << "Hello, ${project_name}!" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
'
|
||||
os.write_file(os.join_path(project_name, 'src', 'main.cpp'), main_content) or { }
|
||||
|
||||
// Create .gitignore
|
||||
gitignore_content := r'
|
||||
# Build files
|
||||
build/
|
||||
bin/
|
||||
*.o
|
||||
*.exe
|
||||
*.dSYM
|
||||
*.d
|
||||
|
||||
# IDE files
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# OS files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
'
|
||||
os.write_file(os.join_path(project_name, '.gitignore'), gitignore_content) or { }
|
||||
|
||||
// Create config.ini
|
||||
config_content := r'
|
||||
# ${project_name} lana build configuration
|
||||
project_name = ${project_name}
|
||||
src_dir = src
|
||||
build_dir = build
|
||||
bin_dir = bin
|
||||
debug = true
|
||||
optimize = false
|
||||
verbose = false
|
||||
include_dirs = include
|
||||
'
|
||||
os.write_file(os.join_path(project_name, 'config.ini'), config_content) or { }
|
||||
|
||||
// Create README.md
|
||||
readme_content := r'
|
||||
# ${project_name}
|
||||
|
||||
A C++ project built with lana (Vlang C++ Build System)
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Build the project
|
||||
```bash
|
||||
lana build
|
||||
```
|
||||
|
||||
### Run the project
|
||||
```bash
|
||||
lana run
|
||||
```
|
||||
|
||||
### Clean build files
|
||||
```bash
|
||||
lana clean
|
||||
```
|
||||
|
||||
## Project Structure
|
||||
- `src/` - Source files
|
||||
- `include/` - Header files
|
||||
- `build/` - Object files and intermediate build files
|
||||
- `bin/` - Executable output
|
||||
|
||||
## Configuration
|
||||
Edit `config.ini` to customize build settings:
|
||||
- `debug` - Enable/disable debug mode
|
||||
- `optimize` - Enable/disable optimization
|
||||
- `include_dirs` - Additional include directories
|
||||
- `libraries` - Linker libraries to include
|
||||
|
||||
## Command Line Options
|
||||
```bash
|
||||
lana build [options]
|
||||
-d, --debug Enable debug mode
|
||||
-O, --optimize Enable optimization
|
||||
-v, --verbose Verbose output
|
||||
-I <dir> Add include directory
|
||||
-l <lib> Add library
|
||||
-o <name> Set output name
|
||||
--config <file> Use custom config file
|
||||
```
|
||||
'
|
||||
os.write_file(os.join_path(project_name, 'README.md'), readme_content) or { }
|
||||
|
||||
println('Project initialized successfully!')
|
||||
println('Created directory structure and template files')
|
||||
println('')
|
||||
println('Usage:')
|
||||
println(' cd ${project_name}')
|
||||
println(' lana build')
|
||||
println(' lana run')
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
module main
|
||||
|
||||
import os
|
||||
import config
|
||||
import builder
|
||||
import runner
|
||||
import initializer
|
||||
import help
|
||||
|
||||
fn main() {
|
||||
mut config_data := config.parse_args() or { config.default_config }
|
||||
|
||||
if os.args.len < 2 {
|
||||
help.show_help()
|
||||
return
|
||||
}
|
||||
|
||||
match os.args[1] {
|
||||
'build' { builder.build(mut config_data) or { return } }
|
||||
'clean' { builder.clean(config_data) }
|
||||
'run' {
|
||||
builder.build(mut config_data) or { return }
|
||||
runner.run_executable(config_data)
|
||||
}
|
||||
'init' { initializer.init_project(os.args[2] or { 'myproject' }) }
|
||||
else { help.show_help() }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
module runner
|
||||
|
||||
import os
|
||||
import config
|
||||
|
||||
pub fn run_executable(build_config config.BuildConfig) {
|
||||
executable := os.join_path(build_config.bin_dir, build_config.project_name)
|
||||
|
||||
if !os.is_file(executable) {
|
||||
println('Executable not found: ${executable}')
|
||||
println('Please run "lana build" first')
|
||||
return
|
||||
}
|
||||
|
||||
println('Running ${executable}...')
|
||||
res := os.execute('${executable}')
|
||||
if res.exit_code != 0 {
|
||||
println('Failed to execute: ${res.output}')
|
||||
return
|
||||
}
|
||||
|
||||
if res.exit_code == 0 {
|
||||
println('Execution completed successfully!')
|
||||
if res.output.len > 0 {
|
||||
println(res.output)
|
||||
}
|
||||
} else {
|
||||
println('Execution failed with exit code ${res.exit_code}')
|
||||
if res.output.len > 0 {
|
||||
println('Output:\n${res.output}')
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue