Add build directives

main
Joca 2025-09-17 22:52:12 -03:00
parent 99dc627c8c
commit 3dcef4035b
Signed by: jocadbz
GPG Key ID: B1836DCE2F50BDF7
11 changed files with 1942 additions and 584 deletions

8
.editorconfig Normal file
View File

@ -0,0 +1,8 @@
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.v]
indent_style = tab

8
.gitattributes vendored Normal file
View File

@ -0,0 +1,8 @@
* text=auto eol=lf
*.bat eol=crlf
*.v linguist-language=V
*.vv linguist-language=V
*.vsh linguist-language=V
v.mod linguist-language=V
.vdocignore linguist-language=ignore

24
.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# Binaries for programs and plugins
main
lana
*.exe
*.exe~
*.so
*.dylib
*.dll
# Ignore binary output folders
bin/
# Ignore common editor/system specific metadata
.DS_Store
.idea/
.vscode/
*.iml
# ENV
.env
# vweb and database
*.db
*.js

633
DOCS.md
View File

@ -7,37 +7,43 @@
3. [Project Structure](#project-structure)
4. [Commands](#commands)
5. [Configuration](#configuration)
6. [Build Process](#build-process)
7. [Dependency Management](#dependency-management)
8. [Command Line Options](#command-line-options)
9. [Configuration File Format](#configuration-file-format)
10. [Troubleshooting](#troubleshooting)
11. [Development](#development)
6. [Build Directives](#build-directives)
7. [Build Process](#build-process)
8. [Dependency Management](#dependency-management)
9. [Command Line Options](#command-line-options)
10. [Configuration File Format](#configuration-file-format)
11. [Troubleshooting](#troubleshooting)
12. [Development](#development)
## Overview
Lana is a lightweight C++ build system. It provides a simple alternative to complex build systems like CMake, focusing on speed, simplicity, and modern C++ development workflows.
Lana is a lightweight C++ build system designed for modern C++ development. It provides a simple, fast alternative to complex tools like CMake or Make, emphasizing speed, simplicity, and self-contained project management.
Key features:
- Automatic source file discovery
- Dependency tracking via `#include` analysis
- Incremental builds with timestamp checking
- Support for debug/optimized builds
- Simple configuration via INI files
- Cross-platform (Linux, macOS, Windows)
- **Automatic source discovery** in `src/` directories
- **Build directives** embedded directly in C++ source files for per-file configuration (dependencies, linking, output, flags)
- **Dependency tracking** via `#include` analysis and timestamp checking
- **Incremental builds** to recompile only changed files
- **Support for shared libraries, tools/executables, and GLSL shaders**
- **Simple global configuration** via `config.ini`
- **Cross-platform** (Linux, macOS, Windows) with parallel compilation
- **No external scripts needed**—everything is in your source files
Lana parses `// build-directive:` comments in your C++ files to handle project-specific details, while `config.ini` manages globals like compiler flags and paths.
## Installation
### Prerequisites
- V compiler (version 0.3.0 or later)
- GCC/G++ compiler (version 7+ recommended)
- GCC/G++ (version 7+ recommended)
- Standard C++ library
- For shaders: Vulkan SDK or shaderc (includes `glslc`)
### Building Lana
1. Clone the repository:
```bash
git clone https://github.com/yourusername/lana.git
git clone https://github.com/yourusername/lana.git # Replace with actual repo
cd lana
```
@ -58,32 +64,32 @@ Key features:
## Project Structure
Lana expects a specific directory layout:
Lana expects this layout (created by `lana init`):
```
project/
├── src/ # Source files (.cpp, .cc, .cxx)
│ ├── main.cpp
│ └── utils/
│ └── helper.cpp
├── src/ # Source files (.cpp, .cc, .cxx) with build directives
│ ├── main.cpp # Example main tool (directives at top)
│ ├── lib/ # Shared library sources
│ │ └── cli.cpp # Example shared lib (directives at top)
│ ├── tools/ # Tool/executable sources
│ │ └── example_tool.cpp # Example tool depending on lib/cli
│ └── shaders/ # GLSL shaders (.vsh, .fsh)
├── include/ # Header files (.h, .hpp)
│ ├── utils.hpp
│ └── config.hpp
│ └── cli.h # Example header
├── build/ # Generated: Object files (*.o), dependencies (*.d)
├── bin/ # Generated: Executable
├── config.ini # Build configuration
├── README.md # Project documentation
└── .gitignore # Git configuration
├── bin/ # Generated: Executables and libs
│ ├── lib/ # Shared libraries (.so/.dll)
│ ├── tools/ # Tool executables
│ └── shaders/ # Compiled shaders (.spv)
├── config.ini # Global build configuration
├── README.md # Project docs (auto-generated with directive examples)
└── .gitignore # Ignores build artifacts
```
### Source Files
- Lana automatically finds `.cpp`, `.cc`, and `.cxx` files
- Supports nested directories (recursive search)
- Header files should be in `include/` or subdirectories
### Build Directories
- `build/` - Contains object files (`.o`) and dependency files (`.d`)
- `bin/` - Contains the final executable
- **Build Directives**: Add `// build-directive:` comments at the top of C++ files for per-file settings (see [Build Directives](#build-directives)).
- **Auto-Discovery**: If no directives, Lana treats files as simple tools using global config.
- **Shaders**: Place `.vsh` (vertex) and `.fsh` (fragment) files in `src/shaders/`; set `shaders_dir` in config to enable compilation.
## Commands
@ -91,63 +97,46 @@ project/
```bash
lana init <project_name>
```
Creates a new project with:
- Directory structure (`src/`, `include/`, `build/`, `bin/`)
- Template `main.cpp`
- `config.ini` configuration file
- `.gitignore`
- `README.md`
Creates the structure above, including template C++ files **with build directives by default** (e.g., in `src/main.cpp`, `src/lib/cli.cpp`). Includes `config.ini`, `README.md` (with directive docs), and examples for shared libs/tools.
**Example:**
```bash
lana init myapp
cd myapp
# Files like src/main.cpp now have directives like:
# // build-directive: unit-name(tools/main)
# // build-directive: out(tools/main)
```
### Build Project
```bash
lana build [options]
```
Compiles sources, processes directives, builds dependency graph, and links outputs. Incremental: only rebuilds changed files.
Compiles all source files and links the executable. Only rebuilds files that have changed or have newer dependencies.
**Options:**
- `-d, --debug` - Enable debug mode (default)
- `-O, --optimize` - Enable optimization
- `-v, --verbose` - Show detailed build information
- `-o <name>` - Set output executable name
- `-I <dir>` - Add include directory
- `-l <lib>` - Link library
- `--config <file>` - Use custom config file
**Options:** See [Command Line Options](#command-line-options).
**Example:**
```bash
lana build -d -v
lana build -O -I external/lib/include -l pthread
lana build -d -v # Debug build with verbose output (shows directive parsing)
```
### Run Project
```bash
lana run [options]
```
Builds the project (if needed) and executes the binary.
Builds (if needed) and runs the main executable (first tool or `project_name` from config/directives).
**Example:**
```bash
lana run
lana run -O # Run with optimizations
lana run -O # Optimized run
```
### Clean Project
```bash
lana clean
```
Removes all generated files:
- `build/` directory (object files, dependencies)
- `bin/` executable
Removes `build/`, `bin/`, and intermediates.
**Example:**
```bash
@ -157,18 +146,14 @@ lana clean
### Help
```bash
lana --help
# or
lana -h
```
Shows available commands and options.
Shows commands, options, and config examples.
## Configuration
Lana uses a simple INI-style configuration file (`config.ini`) in the project root.
`config.ini` handles **global** settings (overridden by directives for per-file needs). Edit it in your project root.
### Basic Configuration
```ini
# Project identification
project_name = myapp
@ -178,401 +163,245 @@ src_dir = src
build_dir = build
bin_dir = bin
# Build modes (mutually exclusive)
# Build modes (mutually exclusive; CLI overrides)
debug = true
optimize = false
# Output verbosity
verbose = false
# Compiler settings
include_dirs = include,external/lib/include
libraries = pthread,boost_system
# Compiler settings (global defaults; directives can add/override)
include_dirs = include # Comma/space-separated
libraries = pthread # Comma/space-separated (global libs)
cflags = -Wall -Wextra -std=c++17
ldflags = -static
ldflags =
# Advanced
parallel_compilation = true
shaders_dir = bin/shaders # Enable shader compilation
dependencies_dir = dependencies
```
### Configuration Precedence
1. Command-line options (highest: e.g., `-d` overrides `debug=true`)
2. `config.ini`
3. Defaults (e.g., `debug=true`, compiler=`g++`)
1. Command line options (highest priority)
2. `config.ini` file
3. Default values (lowest priority)
Directives (in source files) handle per-file overrides (e.g., custom `cflags` for one unit).
### Directory Configuration
## Build Directives
You can customize directory paths:
Lana's killer feature: Embed build instructions **directly in C++ source files** using `// build-directive:` comments. No separate scripts—keeps projects self-contained.
```ini
src_dir = sources
include_dirs = headers,third_party/include
build_dir = obj
bin_dir = output
### How It Works
- **Parsing**: `lana build` scans sources for lines like `// build-directive: <type>(<value>)`.
- **Processing**: Builds a dependency graph; resolves build order, linking, and flags.
- **Placement**: At file top (before code). Multiple per file (one per line).
- **Fallback**: No directives? Auto-discover as simple tool using global config.
- **Output**: Verbose mode (`-v`) shows parsed units/graph.
### Syntax
```
// build-directive: <type>(<value>)
```
- `<type>`: Directive name (e.g., `unit-name`).
- `<value>`: Arguments (comma/space-separated for lists; `true/false` for bools).
Lana will create these directories automatically during the build process.
### Supported Directives
- **`unit-name(<name>)`**: Unique unit ID (e.g., `"lib/cli"`, `"tools/mytool"`). Required for custom builds. Defaults to file path if omitted.
- **`depends-units(<unit1>,<unit2>,...)`**: Dependencies (other units, e.g., `"lib/utils,lib/file"`). Builds them first.
- **`link(<lib1>,<lib2>,...)`**: Libraries to link (e.g., `"utils.so,pthread,boost_system"`). Internal (Lana-built) or external.
- **`out(<path>)`**: Output relative to `bin/` (e.g., `"tools/mytool"`, `"lib/mylib"`). Defaults to unit name.
- **`cflags(<flag1> <flag2> ...)`**: Per-file compiler flags (e.g., `"-std=c++20 -fPIC"`). Appends to global `cflags`.
- **`ldflags(<flag1> <flag2> ...)`**: Per-file linker flags (e.g., `"-static -pthread"`). Appends to global `ldflags`.
- **`shared(<true|false>)`**: Build as shared lib (`.so`/`.dll`, true) or executable (false, default).
### Examples
**Simple Executable (`src/main.cpp`)**:
```cpp
// build-directive: unit-name(tools/main)
// build-directive: depends-units()
// build-directive: link()
// build-directive: out(tools/main)
#include <iostream>
int main() {
std::cout << "Hello, World!" << std::endl;
return 0;
}
```
- Builds `bin/tools/main` executable. No deps.
**Shared Library (`src/lib/cli.cpp`)**:
```cpp
// build-directive: unit-name(lib/cli)
// build-directive: depends-units()
// build-directive: link()
// build-directive: out(lib/cli)
// build-directive: shared(true)
// build-directive: cflags(-fPIC)
#include <iostream>
#include "cli.h"
namespace lana {
void print_help() { std::cout << "CLI help" << std::endl; }
}
```
- Builds `bin/lib/cli.so`. PIC for shared lib.
**Tool Depending on Shared Lib (`src/tools/mytool.cpp`)**:
```cpp
// build-directive: unit-name(tools/mytool)
// build-directive: depends-units(lib/cli)
// build-directive: link(cli.so)
// build-directive: out(tools/mytool)
// build-directive: shared(false)
// build-directive: cflags(-std=c++17)
// build-directive: ldflags(-pthread)
#include <iostream>
#include "cli.h"
int main() {
lana::print_help();
return 0;
}
```
- Depends on/builds `lib/cli` first; links `cli.so`; outputs `bin/tools/mytool`.
### Tips
- **Order**: Directives before `#include` or code.
- **Empty Values**: Use `()` for none (e.g., `depends-units()`).
- **Global Interaction**: Directives add to `config.ini` settings (e.g., global `-Wall` + per-file `-std=c++20`).
- **Shaders**: No directives needed; auto-compiles if `shaders_dir` set.
- **Legacy**: Use `[shared_libs]`/`[tools]` in config for manual lists (overrides auto-parsing).
## Build Process
### Compilation
1. **Parse Directives**: Scans sources for `// build-directive:`; collects into units/graph.
2. **Source Discovery**: Finds `.cpp`/`.cc`/`.cxx` in `src_dir` (recursive).
3. **Dependency Analysis**: Extracts `#include`s; builds graph from directives/timestamps.
4. **Incremental Check**: Recompiles if source/header newer than `.o` or `.d` missing.
5. **Compilation**: `g++ -c` each source to `.o` (uses global + per-file flags).
6. **Linking**: Builds shared libs/tools per directives (e.g., `g++ -shared` for libs).
7. **Shaders** (if enabled): Compiles `.vsh`/`.fsh` to `.spv` with `glslc`.
1. **Source Discovery**: Lana recursively scans `src_dir` for `.cpp`, `.cc`, `.cxx` files
2. **Dependency Analysis**: Extracts `#include` directives from each source file
3. **Incremental Build Check**: Compares timestamps of source files and dependencies against object files
4. **Compilation**: Uses `g++` to compile each source file to an object file (`.o`)
5. **Dependency Generation**: Creates `.d` files for each object file
### Linking
1. **Object Collection**: Gathers all compiled object files
2. **Library Linking**: Includes specified libraries (`-l` flags)
3. **Final Linking**: Links all objects into the executable in `bin_dir`
### Compiler Flags
Lana generates compiler commands with:
**Debug Mode** (default):
```bash
g++ -c -g -O0 -Wall -Wextra -std=c++17 -Iinclude source.cpp -o build/source.o
**Example Output** (`lana build -v`):
```
**Optimized Mode**:
```bash
g++ -c -O3 -Wall -Wextra -std=c++17 -Iinclude source.cpp -o build/source.o
```
**Linking**:
```bash
g++ -g build/*.o -lpthread -o bin/myapp
Parsing build directives...
Found directive for unit: lib/cli in src/lib/cli.cpp
Found directive for unit: tools/mytool in src/tools/mytool.cpp
Building dependency graph...
Build order: lib/cli -> tools/mytool
Building unit: lib/cli
Compiling src/lib/cli.cpp -> build/lib/cli.o
Linking shared library: bin/lib/cli.so
Building unit: tools/mytool
Compiling src/tools/mytool.cpp -> build/tools/mytool.o
Linking executable: bin/tools/mytool
Build completed successfully!
```
## Dependency Management
Lana provides basic dependency tracking through:
### Include Extraction
Lana parses C++ source files to extract `#include` directives:
```cpp
#include "utils.hpp" // Local header
#include <iostream> // System header
#include "../external/lib.h" // Relative path
```
### Dependency Rules
For each source file, Lana tracks:
- **Source timestamp**: When the `.cpp` file was last modified
- **Header timestamps**: When included headers were last modified
- **Object timestamp**: When the corresponding `.o` file was created
### Rebuild Triggers
A source file is recompiled if:
1. The source file is newer than its object file
2. Any included header is newer than the object file
3. The object file doesn't exist
4. Dependencies change (detected via `.d` files)
### Generated Dependencies
Lana creates `.d` files for make compatibility:
```
build/main.o: src/main.cpp include/utils.hpp
build/utils/helper.o: src/utils/helper.cpp include/utils.hpp
```
- **Include Extraction**: Parses `#include` for rebuild triggers (local/system headers).
- **Directive-Based Deps**: `depends-units()` defines unit graph; `link()` handles libs.
- **Timestamp Checks**: Rebuild if source/header > `.o` timestamp.
- **Generated `.d` Files**: Make-compatible deps (e.g., `build/main.o: src/main.cpp include/utils.hpp`).
- **Graph Resolution**: Topological sort; warns on cycles.
## Command Line Options
### Build Options
| Option | Description | Example |
|--------|-------------|---------|
| `-d, --debug` | Enable debug symbols and no optimization | `lana build -d` |
| `-O, --optimize` | Enable optimization (disables debug) | `lana build -O` |
| `-v, --verbose` | Show detailed build commands | `lana build -v` |
| `-o <name>, --output <name>` | Set executable name | `lana build -o myapp` |
### Include/Library Options
| Option | Description | Example |
|--------|-------------|---------|
| `-I <dir>` | Add include directory | `lana build -I external/include` |
| `-l <lib>` | Link library | `lana build -l pthread` |
### Configuration Options
| Option | Description | Example |
|--------|-------------|---------|
| `--config <file>` | Use custom config file | `lana build --config custom.ini` |
### Examples
| `-d, --debug` | Debug mode (`-g -O0`) | `lana build -d` |
| `-O, --optimize` | Optimized mode (`-O3`) | `lana build -O` |
| `-v, --verbose` | Show commands/graph | `lana build -v` |
| `-p, --parallel` | Parallel jobs | `lana build -p` |
| `-o <name>` | Output name | `lana build -o app` |
| `-I <dir>` | Include dir | `lana build -I external/` |
| `-l <lib>` | Link lib | `lana build -l pthread` |
| `--config <file>` | Custom config | `lana build --config release.ini` |
| `--shared-lib <name> <source>` | Legacy shared lib | `lana build --shared-lib cli src/lib/cli.cpp` |
| `--tool <name> <source>` | Legacy tool | `lana build --tool mytool src/tools/mytool.cpp` |
**Examples:**
```bash
# Debug build with verbose output
lana build -d -v
# Optimized release build
lana build -O
# Build with external dependencies
lana build -I third_party/include -l boost_system -l sqlite3
# Custom output name
lana build -o game.exe
# Use custom config
lana build --config release.ini
lana build -d -v -p # Debug, verbose, parallel
lana run -O # Optimized run
lana build -I lib/include -l boost -l sqlite3
```
## Configuration File Format
The `config.ini` file uses a simple key-value format:
### Syntax
INI-style (`config.ini`):
```ini
# Comments start with #
# Comments with #
key = value
# Arrays use comma or space separation
array_key = value1, value2, value3
array_key = val1, val2 # Comma/space-separated
```
### Available Keys
#### Project Settings
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `project_name` | string | "project" | Executable name |
| `src_dir` | string | "src" | Source directory |
| `build_dir` | string | "build" | Object files directory |
| `bin_dir` | string | "bin" | Executable directory |
#### Build Options
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `debug` | bool | true | Enable debug mode |
| `optimize` | bool | false | Enable optimization |
| `verbose` | bool | false | Verbose output |
#### Compiler Settings
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `include_dirs` | string[] | [] | Include directories (comma/space separated) |
| `libraries` | string[] | [] | Linker libraries (comma/space separated) |
| `cflags` | string[] | [] | Additional compiler flags (space separated) |
| `ldflags` | string[] | [] | Additional linker flags (space separated) |
### Example Configurations
#### Debug Configuration (`debug.ini`)
**Full Example (`config.ini`)**:
```ini
[global]
project_name = myapp
src_dir = src
build_dir = build
bin_dir = bin
debug = true
optimize = false
verbose = true
include_dirs = include,external/lib/include
libraries = pthread
cflags = -Wall -Wextra -std=c++17 -fPIC
ldflags =
```
#### Release Configuration (`release.ini`)
```ini
project_name = myapp
src_dir = src
build_dir = build
bin_dir = bin
debug = false
optimize = true
verbose = false
parallel_compilation = true
include_dirs = include,external/include
libraries = pthread,boost_system
cflags = -Wall -Wextra -std=c++17
ldflags = -static
include_dirs = include
libraries = pthread
cflags = -O3 -DNDEBUG -std=c++17
ldflags = -s
[shared_libs] # Legacy/manual (directives preferred)
name = cli
sources = src/lib/cli.cpp
libraries =
[tools] # Legacy/manual
name = main
sources = src/main.cpp
libraries = cli
shaders_dir = bin/shaders # Enable shaders
```
## Troubleshooting
### Common Issues
#### "No source files found"
**Cause**: No `.cpp`, `.cc`, or `.cxx` files in the source directory.
- **"No source files found"**: Check `src_dir` in config; ensure `.cpp` files exist.
- **"Failed to parse directive"**: Verify syntax (e.g., `unit-name(lib/cli)`); use `-v` for details.
- **"Dependency not found"**: Add missing `depends-units()` or build order in directives.
- **Linking errors**: Check `link()` for libs; install dev packages (e.g., `sudo apt install libpthread-dev`).
- **Shader compilation fails**: Install Vulkan SDK (`glslc`); verify `shaders_dir` in config.
- **Permission denied**: `chmod +x bin/tools/mytool`.
**Solution**:
1. Check `src_dir` in `config.ini`
2. Verify source files exist in the specified directory
3. Ensure files have correct extensions
#### "Compilation failed"
**Cause**: Compiler errors in source code.
**Solution**:
1. Use `-v` flag to see full compiler output
2. Check for syntax errors, missing headers, or type issues
3. Verify include paths with `-I` flags
#### "Linking failed"
**Cause**: Missing libraries or undefined symbols.
**Solution**:
1. Install required development libraries (`sudo apt install libxxx-dev`)
2. Add libraries with `-l` flag or `libraries` in config
3. Check library paths with `-L` flag if needed
#### "Permission denied"
**Cause**: Missing execute permissions on generated binary.
**Solution**:
```bash
chmod +x bin/myapp
```
### Build Verbosity
Enable verbose mode to see detailed build information:
```bash
lana build -v
```
This shows:
- Full compiler and linker commands
- Include paths being used
- Dependency analysis results
- File timestamps
### Log Files
Lana doesn't create log files by default, but you can capture output:
```bash
lana build -v > build.log 2>&1
```
### Compiler Detection
Lana uses `g++` by default. To use a different compiler:
1. **Set environment variable**:
```bash
export CXX=clang++
```
2. **Modify build scripts** (advanced):
Edit `config.v` to change the compiler path.
### Debugging Tips
- **Verbose Build**: `lana build -v` shows directive parsing, graph, and commands.
- **Check Graph**: Look for "Build order:" in verbose output.
- **Dependency Files**: Inspect `build/*.d` for include tracking.
- **Logs**: `lana build -v > build.log 2>&1`.
- **Clean Rebuild**: `lana clean && lana build -v`.
## Development
### Architecture
See the source code structure in the repo. To extend:
- Add directives: Update `config.BuildDirective` and `builder.build_from_directives()`.
- New features: Modify `config.v` for parsing, `builder.v` for logic.
Lana consists of several modules:
```
lana/
├── config/ # Configuration parsing and defaults
├── builder/ # Core build logic
├── deps/ # Dependency extraction and tracking
├── runner/ # Executable execution
├── initializer/ # Project initialization
├── help/ # Help text and CLI interface
└── main.v # Entry point
```
### Building from Source
1. **Install V**:
```bash
git clone https://github.com/vlang/v
cd v
make
```
2. **Build Lana**:
```bash
v . -o lana
```
3. **Run tests** (if implemented):
```bash
v test .
```
### Adding Features
#### New Build Options
1. Add to `config.BuildConfig` struct
2. Update `parse_args()` in `config.v`
3. Modify compiler/linker command builders
4. Update help text
#### Custom Compilers
To support different compilers:
1. Add compiler detection in `config.v`
2. Create compiler-specific flag mappings
3. Update `build_compiler_command()` and `build_linker_command()`
#### Advanced Dependency Tracking
For more sophisticated dependency management:
1. Enhance `deps.extract_dependencies()` to handle:
- Preprocessor macros
- Template instantiations
- Conditional includes
2. Implement dependency graph analysis
3. Add parallel build support
### Contributing
1. **Fork** the repository
2. **Create feature branch**:
```bash
git checkout -b feature/amazing-feature
```
3. **Make changes** and add tests
4. **Commit**:
```bash
git commit -m 'Add amazing feature'
```
5. **Push** to branch:
```bash
git push origin feature/amazing-feature
```
6. **Create Pull Request**
### Code Style
- Follow V coding conventions
- Use descriptive variable names
- Keep functions small and focused
- Add comments for complex logic
- Write tests for new features
### License
Lana is licensed under the MIT License. See the LICENSE file for details.
For contributing, see [GitHub](https://github.com/yourusername/lana) (fork, branch, PR).
---
*Documentation generated for Lana version 1.0.0*
*Last updated: [Current Date]*
*Report issues: [GitHub Issues Link]*
*Contribute: [GitHub Repository Link]*
*Documentation for Lana v1.0.0*
*Last updated: 2025-09-17*
*Issues: [Issues](https://lostcave.ddnss.de/git/jocadbz/lana)*
*Contribute: [Repo](https://lostcave.ddnss.de/git/jocadbz/lana)*

View File

@ -4,54 +4,296 @@ import os
import config
import deps
// BuildTarget represents a build target (shared lib or tool)
pub enum BuildTarget {
shared_lib
tool
}
// BuildTargetInfo holds common information for all build targets
pub struct BuildTargetInfo {
name string
sources []string
object_dir string
output_dir string
debug bool
optimize bool
verbose bool
include_dirs []string
cflags []string
ldflags []string
libraries []string
}
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}')
os.mkdir_all('${build_config.bin_dir}/lib') or { return error('Failed to create lib directory') }
os.mkdir_all('${build_config.bin_dir}/tools') or { return error('Failed to create tools directory') }
if build_config.shaders_dir != '' && build_config.shaders_dir != 'bin/shaders' {
os.mkdir_all(build_config.shaders_dir) or { return error('Failed to create shaders directory') }
}
if source_files.len == 0 {
return error('No source files found in ${build_config.src_dir}')
}
// Auto-discover sources if not specified
auto_discover_sources(mut build_config)
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 {
// Build shared libraries first (from config)
mut shared_libs_built := []string{}
for mut lib_config in build_config.shared_libs {
if lib_config.sources.len == 0 {
if build_config.verbose {
println('Using cached ${obj_file}')
println('Skipping empty shared library: ${lib_config.name}')
}
object_files << obj_file
continue
}
println('Building shared library: ${lib_config.name}')
build_shared_library(mut lib_config, build_config) or {
return error('Failed to build shared library ${lib_config.name}: ${err}')
}
shared_libs_built << lib_config.name
if build_config.verbose {
println('Built shared library: ${lib_config.name}')
}
}
// 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') }
// Build targets from build directives
build_from_directives(mut build_config, mut shared_libs_built)!
// Build tools/executables from config
for mut tool_config in build_config.tools {
if tool_config.sources.len == 0 {
if build_config.verbose {
println('Skipping empty tool: ${tool_config.name}')
}
continue
}
println('Building tool: ${tool_config.name}')
build_tool(mut tool_config, build_config) or {
return error('Failed to build tool ${tool_config.name}: ${err}')
}
if build_config.verbose {
println('Built tool: ${tool_config.name}')
}
}
// Build shaders if configured and directory exists
if build_config.shaders_dir != '' {
compile_shaders(build_config) or {
if !build_config.verbose {
println('Warning: Failed to compile shaders: ${err}')
}
}
}
println('Build completed successfully!')
}
// Build targets based on build directives from source files
fn build_from_directives(mut build_config config.BuildConfig, mut shared_libs_built []string) ! {
// Build a dependency graph from directives
mut dep_graph := map[string]config.BuildDirective{}
mut build_order := []string{}
mut built_units := []string{}
// Initialize graph with all directives
for directive in build_config.build_directives {
dep_graph[directive.unit_name] = directive
}
// Topological sort to determine build order
for unit_name, directive in dep_graph {
if unit_name in built_units {
continue
}
build_unit_recursive(unit_name, dep_graph, mut build_order, mut built_units, mut build_config, shared_libs_built)!
}
// Build units in determined order
for unit_name in build_order {
directive := dep_graph[unit_name]
println('Building unit: ${unit_name}')
// Find source file for this unit
mut source_file := ''
for src_path in directive.unit_name.split('/') {
source_file = os.join_path(build_config.src_dir, src_path + '.cpp')
if os.is_file(source_file) {
break
}
source_file = os.join_path(build_config.src_dir, src_path + '.cc')
if os.is_file(source_file) {
break
}
source_file = os.join_path(build_config.src_dir, src_path + '.cxx')
if os.is_file(source_file) {
break
}
}
if source_file == '' {
if build_config.verbose {
println('Warning: Source file not found for unit ${unit_name}')
}
continue
}
// Create object directory
object_dir := os.join_path(build_config.build_dir, directive.unit_name)
os.mkdir_all(object_dir) or { return error('Failed to create object directory: ${object_dir}') }
obj_file := get_object_file(source_file, object_dir)
// Compile source file
if needs_recompile(source_file, obj_file) {
println('Compiling ${unit_name}: ${source_file}...')
target_config := config.TargetConfig(config.ToolConfig{
name: unit_name
sources: [source_file]
debug: build_config.debug
optimize: build_config.optimize
verbose: build_config.verbose
cflags: directive.cflags
ldflags: directive.ldflags
})
compile_file(source_file, obj_file, build_config, target_config) or {
return error('Failed to compile ${source_file} for ${unit_name}')
}
} else {
if build_config.verbose {
println('Using cached ${obj_file} for ${unit_name}')
}
}
// Link executable or shared library
if directive.is_shared {
// Link shared library
lib_output := os.join_path(build_config.bin_dir, 'lib', directive.unit_name)
println('Linking shared library: ${lib_output}')
link_shared_library([obj_file], directive.unit_name, lib_output, build_config, config.SharedLibConfig{
name: directive.unit_name
libraries: directive.link_libs
debug: build_config.debug
optimize: build_config.optimize
verbose: build_config.verbose
ldflags: directive.ldflags
}) or {
return error('Failed to link shared library ${unit_name}')
}
shared_libs_built << directive.unit_name
} else {
// Link executable
executable := os.join_path(build_config.bin_dir, directive.output_path)
println('Linking executable: ${executable}')
link_tool([obj_file], executable, build_config, config.ToolConfig{
name: directive.unit_name
libraries: directive.link_libs
debug: build_config.debug
optimize: build_config.optimize
verbose: build_config.verbose
ldflags: directive.ldflags
}) or {
return error('Failed to link executable ${unit_name}')
}
}
if build_config.verbose {
println('Successfully built unit: ${unit_name}')
}
}
}
// Recursively build unit and its dependencies
fn build_unit_recursive(unit_name string, dep_graph map[string]config.BuildDirective, mut build_order []string, mut built_units []string, mut build_config config.BuildConfig, shared_libs_built []string) ! {
if unit_name in built_units {
return
}
// Build dependencies first
directive := dep_graph[unit_name]
for dep_unit in directive.depends_units {
if dep_unit in dep_graph {
build_unit_recursive(dep_unit, dep_graph, mut build_order, mut built_units, mut build_config, shared_libs_built)!
} else if !dep_unit.ends_with('.so') && !dep_unit.contains('.') {
// Look for library in shared_libs_built
lib_name := 'lib/${dep_unit}'
if lib_name !in shared_libs_built {
if build_config.verbose {
println('Warning: Dependency ${dep_unit} not found for ${unit_name}')
}
}
}
}
build_order << unit_name
built_units << unit_name
}
fn auto_discover_sources(mut build_config config.BuildConfig) {
// Auto-discover shared library sources
for mut lib_config in build_config.shared_libs {
if lib_config.sources.len == 0 {
// 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_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}')
}
}
}
}
// Auto-discover tool sources
for mut tool_config in build_config.tools {
if tool_config.sources.len == 0 {
// 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{} }
if tool_sources.len > 0 {
tool_config.sources = tool_sources
} else {
// Fallback: look for main.cpp or tool_name.cpp in src/
fallback_sources := [
os.join_path('src', '${tool_config.name}.cpp'),
os.join_path('src', 'main.cpp')
]
for fallback in fallback_sources {
if os.is_file(fallback) {
tool_config.sources << fallback
break
}
}
}
if build_config.verbose && tool_config.sources.len > 0 {
println('Auto-discovered ${tool_config.sources.len} source files for tool ${tool_config.name}')
}
}
}
}
// If still no sources for default tool, use all files in src/
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{} }
if all_sources.len > 0 {
default_tool.sources = all_sources
if build_config.verbose {
println('Auto-discovered ${all_sources.len} source files for main project')
}
}
}
}
}
pub fn clean(build_config config.BuildConfig) {
println('Cleaning build files...')
@ -63,18 +305,352 @@ pub fn clean(build_config config.BuildConfig) {
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}')
// Remove bin directories
dirs_to_clean := ['lib', 'tools']
for dir in dirs_to_clean {
full_dir := os.join_path(build_config.bin_dir, dir)
if os.is_dir(full_dir) {
os.rmdir_all(full_dir) or {
println('Warning: Failed to remove ${full_dir}: ${err}')
}
println('Removed ${full_dir}')
}
println('Removed ${executable}')
}
// Remove shaders directory if it exists
shaders_dir := if build_config.shaders_dir.starts_with('bin/') {
os.join_path(build_config.bin_dir, build_config.shaders_dir[4..])
} else {
build_config.shaders_dir
}
if os.is_dir(shaders_dir) {
os.rmdir_all(shaders_dir) or {
println('Warning: Failed to remove ${shaders_dir}: ${err}')
}
println('Removed ${shaders_dir}')
}
// Remove main executable if it exists (backward compatibility)
main_exe := os.join_path(build_config.bin_dir, build_config.project_name)
if os.is_file(main_exe) {
os.rm(main_exe) or {
println('Warning: Failed to remove ${main_exe}: ${err}')
}
println('Removed ${main_exe}')
}
println('Clean completed!')
}
fn build_shared_library(mut lib_config config.SharedLibConfig, build_config config.BuildConfig) ! {
if lib_config.sources.len == 0 {
if build_config.verbose {
println('No sources specified for shared library ${lib_config.name}, skipping')
}
return
}
// Create output directory
os.mkdir_all(lib_config.output_dir) or { return error('Failed to create shared lib directory: ${lib_config.output_dir}') }
mut object_files := []string{}
mut object_dir := os.join_path(build_config.build_dir, lib_config.name)
os.mkdir_all(object_dir) or { return error('Failed to create object directory: ${object_dir}') }
// Compile each source file
for src_file in lib_config.sources {
if !os.is_file(src_file) {
if build_config.verbose {
println('Warning: Source file not found: ${src_file}')
}
continue
}
obj_file := get_object_file(src_file, object_dir)
// Create object directory if needed
obj_path := os.dir(obj_file)
os.mkdir_all(obj_path) or { return error('Failed to create object directory: ${obj_path}') }
// Check if we need to recompile
if needs_recompile(src_file, obj_file) {
println('Compiling ${lib_config.name}: ${src_file}...')
lib_target_config := config.TargetConfig(lib_config)
object_files << compile_file(src_file, obj_file, build_config, lib_target_config) or {
return error('Failed to compile ${src_file} for ${lib_config.name}')
}
} else {
if lib_config.verbose {
println('Using cached ${obj_file} for ${lib_config.name}')
}
object_files << obj_file
}
}
if object_files.len == 0 {
return error('No object files generated for shared library ${lib_config.name}')
}
// Link shared library
lib_output := os.join_path(lib_config.output_dir, lib_config.name)
println('Linking shared library: ${lib_output}')
link_shared_library(object_files, lib_config.name, lib_output, build_config, lib_config) or {
return error('Failed to link shared library ${lib_config.name}')
}
if build_config.verbose {
println('Successfully built shared library: ${lib_config.name}')
}
}
fn build_tool(mut tool_config config.ToolConfig, build_config config.BuildConfig) ! {
if tool_config.sources.len == 0 {
if build_config.verbose {
println('No sources specified for tool ${tool_config.name}, skipping')
}
return
}
// Create output directory
os.mkdir_all(tool_config.output_dir) or { return error('Failed to create tool directory: ${tool_config.output_dir}') }
mut object_files := []string{}
mut object_dir := os.join_path(build_config.build_dir, tool_config.name)
os.mkdir_all(object_dir) or { return error('Failed to create object directory: ${object_dir}') }
// Compile each source file
for src_file in tool_config.sources {
if !os.is_file(src_file) {
if build_config.verbose {
println('Warning: Source file not found: ${src_file}')
}
continue
}
obj_file := get_object_file(src_file, object_dir)
// Create object directory if needed
obj_path := os.dir(obj_file)
os.mkdir_all(obj_path) or { return error('Failed to create object directory: ${obj_path}') }
// Check if we need to recompile
if needs_recompile(src_file, obj_file) {
println('Compiling ${tool_config.name}: ${src_file}...')
tool_target_config := config.TargetConfig(tool_config)
object_files << compile_file(src_file, obj_file, build_config, tool_target_config) or {
return error('Failed to compile ${src_file} for ${tool_config.name}')
}
} else {
if tool_config.verbose {
println('Using cached ${obj_file} for ${tool_config.name}')
}
object_files << obj_file
}
}
if object_files.len == 0 {
return error('No object files generated for tool ${tool_config.name}')
}
// Link executable
executable := os.join_path(tool_config.output_dir, tool_config.name)
println('Linking tool: ${executable}')
link_tool(object_files, executable, build_config, tool_config) or {
return error('Failed to link tool ${tool_config.name}')
}
if build_config.verbose {
println('Successfully built tool: ${tool_config.name}')
}
}
// Helper function to get target verbose setting
fn get_target_verbose(target_config config.TargetConfig) bool {
mut verbose := false
match target_config {
config.SharedLibConfig {
verbose = target_config.verbose
}
config.ToolConfig {
verbose = target_config.verbose
}
}
return verbose
}
fn compile_file(source_file string, object_file string, build_config config.BuildConfig, target_config config.TargetConfig) !string {
cmd := config.build_shared_compiler_command(source_file, object_file, build_config, target_config)
target_verbose := get_target_verbose(target_config)
if target_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_shared_library(object_files []string, library_name string, output_path string, build_config config.BuildConfig, lib_config config.SharedLibConfig) ! {
cmd := config.build_shared_linker_command(object_files, library_name, output_path, build_config, lib_config)
if lib_config.verbose {
println('Shared lib link command: ${cmd}')
}
res := os.execute(cmd)
if res.exit_code != 0 {
return error('Shared library linking failed with exit code ${res.exit_code}:\n${res.output}')
}
}
fn link_tool(object_files []string, executable string, build_config config.BuildConfig, tool_config config.ToolConfig) ! {
cmd := config.build_tool_linker_command(object_files, executable, build_config, tool_config)
if tool_config.verbose {
println('Tool link command: ${cmd}')
}
res := os.execute(cmd)
if res.exit_code != 0 {
return error('Tool linking failed with exit code ${res.exit_code}:\n${res.output}')
}
}
fn compile_shaders(build_config config.BuildConfig) ! {
shaders_src_dir := 'src/shaders'
if !os.is_dir(shaders_src_dir) {
if build_config.verbose {
println('No shaders directory found, skipping shader compilation')
}
return
}
println('Compiling shaders...')
// Find glslc compiler
glslc_path := find_glslc() or {
println('Warning: glslc compiler not found, skipping shader compilation')
return
}
// Get shaders directory
shaders_out_dir := if build_config.shaders_dir.starts_with('bin/') {
os.join_path(build_config.bin_dir, build_config.shaders_dir[4..])
} else {
build_config.shaders_dir
}
os.mkdir_all(shaders_out_dir) or { return error('Failed to create shaders output directory') }
// List all shader files
shader_files := os.ls(shaders_src_dir) or { return error('Failed to list shaders directory') }
mut shader_count := 0
mut success_count := 0
// Compile vertex shaders (.vsh)
for shader in shader_files {
if !shader.ends_with('.vsh') {
continue
}
shader_count++
src_path := os.join_path(shaders_src_dir, shader)
output_name := shader.replace('.vsh', '.vsh.spv')
output_path := os.join_path(shaders_out_dir, output_name)
cmd := '${glslc_path} -o ${output_path} -fshader-stage=vertex ${src_path}'
println('Compiling vertex shader: ${shader}')
if build_config.verbose {
println('Shader compile command: ${cmd}')
}
res := os.execute(cmd)
if res.exit_code != 0 {
println('Error compiling ${shader}: ${res.output}')
} else {
success_count++
if build_config.verbose {
println('Compiled: ${shader} -> ${output_path}')
}
}
}
// Compile fragment shaders (.fsh)
for shader in shader_files {
if !shader.ends_with('.fsh') {
continue
}
shader_count++
src_path := os.join_path(shaders_src_dir, shader)
output_name := shader.replace('.fsh', '.fsh.spv')
output_path := os.join_path(shaders_out_dir, output_name)
cmd := '${glslc_path} -o ${output_path} -fshader-stage=fragment ${src_path}'
println('Compiling fragment shader: ${shader}')
if build_config.verbose {
println('Shader compile command: ${cmd}')
}
res := os.execute(cmd)
if res.exit_code != 0 {
println('Error compiling ${shader}: ${res.output}')
} else {
success_count++
if build_config.verbose {
println('Compiled: ${shader} -> ${output_path}')
}
}
}
if shader_count == 0 {
println('No shaders found to compile')
} else {
println('Shader compilation complete: ${success_count}/${shader_count} successful')
}
}
fn find_glslc() !string {
// Check common locations
paths := [
'/usr/bin/glslc',
'/usr/local/bin/glslc',
'/opt/homebrew/bin/glslc' // macOS
]
for path in paths {
if os.is_file(path) {
return path
}
}
// Try PATH using os.which
glslc_path := os.find_abs_path_of_executable('glslc') or { panic(err) }
if glslc_path != '' {
return glslc_path
}
return error('glslc not found. Install Vulkan SDK or shaderc')
}
fn get_object_file(source_file string, object_dir string) string {
// Replace src_dir with object_dir and change extension to .o
mut obj_file := source_file.replace('src', object_dir)
obj_file = obj_file.replace('.cpp', '.o').replace('.cc', '.o').replace('.cxx', '.o')
return obj_file
}
fn find_source_files(dir string) ![]string {
mut files := []string{}
@ -126,36 +702,4 @@ fn needs_recompile(source_file string, object_file string) bool {
}
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}')
}
}

View File

@ -2,20 +2,77 @@ module config
import os
// BuildDirective represents a single build directive found in source files
pub struct BuildDirective {
__global:
unit_name string // e.g., "tools/arraydump" or "lib/file"
depends_units []string // e.g., ["lib/file", "lib/cli"]
link_libs []string // e.g., ["file.so", "cli.so"]
output_path string // e.g., "tools/arraydump"
cflags []string // file-specific CFLAGS
ldflags []string // file-specific LDFLAGS
is_shared bool // whether this is a shared library
}
// TargetConfig is a sum type for build targets (shared lib or tool)
pub type TargetConfig = SharedLibConfig | ToolConfig
// SharedLibConfig holds the configuration for a shared library
pub struct SharedLibConfig {
__global:
name string // name of the shared library (e.g., "net")
output_dir string = 'bin/lib' // where to put .so/.dll files
sources []string // source files for this library
libraries []string // libraries this shared lib depends on
include_dirs []string // additional includes for this lib
cflags []string // additional CFLAGS for this lib
ldflags []string // additional LDFLAGS for this lib
debug bool
optimize bool
verbose bool
}
// ToolConfig holds the configuration for a tool/executable
pub struct ToolConfig {
__global:
name string // name of the tool/executable
output_dir string = 'bin/tools' // where to put executable
sources []string // source files for this tool
libraries []string // libraries this tool depends on
include_dirs []string // additional includes for this tool
cflags []string // additional CFLAGS for this tool
ldflags []string // additional LDFLAGS for this tool
debug bool
optimize bool
verbose bool
}
// 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
__global:
project_name string
src_dir string
build_dir string
bin_dir string
include_dirs []string
libraries []string // global libraries
cflags []string // global CFLAGS
ldflags []string // global LDFLAGS
debug bool
optimize bool
verbose bool
compiler string = 'g++' // C++ compiler binary
lib_search_paths []string // library search paths (-L)
// New fields for shared library support
shared_libs []SharedLibConfig
tools []ToolConfig
shaders_dir string = 'bin/shaders' // for shader compilation
dependencies_dir string = 'dependencies' // external dependencies
parallel_compilation bool = true // enable parallel builds
// Build directives from source files
build_directives []BuildDirective
}
// default config
@ -31,6 +88,108 @@ pub const default_config = BuildConfig{
debug: true
optimize: false
verbose: false
shared_libs: []
tools: []
}
// Parse build directives from source files
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 {
if build_config.verbose {
println('No source files found in ${build_config.src_dir}')
}
return
}
for src_file in src_files {
content := os.read_file(src_file) or { continue }
lines := content.split_into_lines()
mut unit_name := ''
mut depends_units := []string{}
mut link_libs := []string{}
mut output_path := ''
mut file_cflags := []string{}
mut file_ldflags := []string{}
mut is_shared := false
for line1 in lines {
line := line1.trim_space()
if !line.starts_with('// build-directive:') {
continue
}
parts := line[17..].trim_space().split('(')
if parts.len != 2 {
continue
}
directive_type := parts[0].trim_space()
directive_value := parts[1].trim('()').trim_space()
match directive_type {
'unit-name' {
unit_name = directive_value
}
'depends-units' {
depends_units = directive_value.split(',')
for mut d in depends_units {
d = d.trim_space()
}
}
'link' {
link_libs = directive_value.split(',')
for mut l in link_libs {
l = l.trim_space()
}
}
'out' {
output_path = directive_value
}
'cflags' {
file_cflags = directive_value.split(' ')
for mut f in file_cflags {
f = f.trim_space()
}
}
'ldflags' {
file_ldflags = directive_value.split(' ')
for mut f in file_ldflags {
f = f.trim_space()
}
}
'shared' {
is_shared = directive_value == 'true'
}
else {
if build_config.verbose {
println('Warning: Unknown build directive: ${directive_type} in ${src_file}')
}
}
}
}
if unit_name != '' {
directives << BuildDirective{
unit_name: unit_name
depends_units: depends_units
link_libs: link_libs
output_path: output_path
cflags: file_cflags
ldflags: file_ldflags
is_shared: is_shared
}
if build_config.verbose {
println('Found build directive for unit: ${unit_name} in ${src_file}')
}
}
}
build_config.build_directives = directives
}
pub fn parse_args() !BuildConfig {
@ -43,18 +202,31 @@ pub fn parse_args() !BuildConfig {
'-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 }
'-p', '--parallel' { build_config.parallel_compilation = true }
'-o', '--output' {
if i + 1 < os.args.len {
build_config.project_name = os.args[i + 1]
i++
}
}
'-c', '--compiler' {
if i + 1 < os.args.len {
build_config.compiler = 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.lib_search_paths << os.args[i + 1]
i++
}
}
'-l' {
if i + 1 < os.args.len {
build_config.libraries << os.args[i + 1]
@ -67,70 +239,480 @@ pub fn parse_args() !BuildConfig {
i++
}
}
'--shared-lib' {
// Parse shared library configuration
if i + 2 < os.args.len {
lib_name := os.args[i + 1]
lib_sources := os.args[i + 2]
mut lib_config := SharedLibConfig{
name: lib_name
sources: [lib_sources]
debug: build_config.debug
optimize: build_config.optimize
verbose: build_config.verbose
}
build_config.shared_libs << lib_config
i += 2
}
}
'--tool' {
// Parse tool configuration
if i + 2 < os.args.len {
tool_name := os.args[i + 1]
tool_sources := os.args[i + 2]
mut tool_config := ToolConfig{
name: tool_name
sources: [tool_sources]
debug: build_config.debug
optimize: build_config.optimize
verbose: build_config.verbose
}
build_config.tools << tool_config
i += 2
}
}
else {
if !arg.starts_with('-') {
build_config.project_name = arg
// Treat as project name or first source file
if build_config.project_name == 'project' {
build_config.project_name = arg
} else {
// Add as default tool
mut default_tool := ToolConfig{
name: build_config.project_name
sources: [arg]
debug: build_config.debug
optimize: build_config.optimize
verbose: build_config.verbose
}
build_config.tools << default_tool
}
}
}
}
i++
}
// Parse build directives from source files
build_config.parse_build_directives()!
// If no tools specified, create default tool from src_dir
if build_config.tools.len == 0 {
mut default_tool := ToolConfig{
name: build_config.project_name
sources: []
debug: build_config.debug
optimize: build_config.optimize
verbose: build_config.verbose
}
build_config.tools << default_tool
}
return build_config
}
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}') }
mut build_config := default_config
lines := content.split_into_lines()
mut current_section := ''
mut current_lib_index := 0
mut current_tool_index := 0
for line in lines {
if line.starts_with('#') || line.trim_space() == '' { continue }
if line.starts_with('[') && line.ends_with(']') {
current_section = line[1..line.len - 1]
// Reset indices when entering new sections
if current_section == '[shared_libs]' {
current_lib_index = 0
} else if current_section == '[tools]' {
current_tool_index = 0
}
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() }
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 }
'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() }
}
'shaders_dir' { build_config.shaders_dir = value }
'dependencies_dir' { build_config.dependencies_dir = value }
else {}
}
}
'libraries' {
libs := value.split(',')
for lib in libs { build_config.libraries << lib.trim_space() }
'[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}')
}
}
}
current_lib_index++
}
'cflags' {
flags := value.split(' ')
for flag in flags { build_config.cflags << flag.trim_space() }
'[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}')
}
}
}
current_tool_index++
}
'ldflags' {
flags := value.split(' ')
for flag in flags { build_config.ldflags << flag.trim_space() }
else {
if build_config.verbose {
println('Warning: Unknown config section: ${current_section}')
}
}
else {}
}
}
}
// 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 }
}
return build_config
}
// Utility function to build compiler command
// Utility function to get target-specific configuration values
pub fn get_target_config_values(target_config TargetConfig) (bool, bool, bool, bool, []string, []string) {
mut is_shared_lib := false
mut use_debug := false
mut use_optimize := false
mut use_verbose := false
mut target_includes := []string{}
mut target_cflags := []string{}
match target_config {
SharedLibConfig {
is_shared_lib = true
use_debug = target_config.debug
use_optimize = target_config.optimize
use_verbose = target_config.verbose
target_includes = target_config.include_dirs.clone()
target_cflags = target_config.cflags.clone()
}
ToolConfig {
is_shared_lib = false
use_debug = target_config.debug
use_optimize = target_config.optimize
use_verbose = target_config.verbose
target_includes = target_config.include_dirs.clone()
target_cflags = target_config.cflags.clone()
}
}
return is_shared_lib, use_debug, use_optimize, use_verbose, target_includes, target_cflags
}
// Utility function to build compiler command for shared libraries and tools
pub fn build_shared_compiler_command(source_file string, object_file string, build_config BuildConfig, target_config TargetConfig) string {
mut cmd := '${build_config.compiler} -c'
// Add include directories (project + target specific)
for include_dir in build_config.include_dirs {
cmd += ' -I${include_dir}'
}
// Add library search paths
for lib_path in build_config.lib_search_paths {
cmd += ' -L${lib_path}'
}
// Get target-specific values
_, _, _, _, target_includes, target_cflags := get_target_config_values(target_config)
// Add target-specific include dirs
for include_dir in target_includes {
if include_dir != '' {
cmd += ' -I${include_dir}'
}
}
// Add debug/optimization flags
_, use_debug, use_optimize, _, _, _ := get_target_config_values(target_config)
if use_debug {
cmd += ' -g -O0'
} else if use_optimize {
cmd += ' -O3'
} else {
cmd += ' -O2'
}
// Add PIC flag for shared libraries
is_shared_lib, _, _, _, _, _ := get_target_config_values(target_config)
if is_shared_lib {
cmd += ' -fPIC'
}
// Add standard flags
cmd += ' -Wall -Wextra -std=c++17'
// Add global CFLAGS
for flag in build_config.cflags {
cmd += ' ${flag}'
}
// Add target-specific CFLAGS
for flag in target_cflags {
if flag != '' {
cmd += ' ${flag}'
}
}
cmd += ' ${source_file} -o ${object_file}'
return cmd
}
// Utility function to build shared library linker command
pub fn build_shared_linker_command(object_files []string, library_name string, output_path string, build_config BuildConfig, lib_config SharedLibConfig) string {
mut cmd := '${build_config.compiler} -shared'
// Add library search paths
cmd += ' -L${build_config.bin_dir}/lib'
for lib_path in build_config.lib_search_paths {
cmd += ' -L${lib_path}'
}
// Add debug flags for linking
if lib_config.debug {
cmd += ' -g'
}
// Add object files
for obj_file in object_files {
cmd += ' ${obj_file}'
}
// Add project libraries
for library in build_config.libraries {
if library != '' {
cmd += ' -l${library}'
}
}
// Add this library's dependencies
for library in lib_config.libraries {
if library != '' {
cmd += ' -l:${library}.so'
}
}
// Add custom LDFLAGS
for flag in build_config.ldflags {
cmd += ' ${flag}'
}
for flag in lib_config.ldflags {
cmd += ' ${flag}'
}
// Set output name (with platform-specific extension)
mut lib_name := library_name
$if windows {
lib_name += '.dll'
} $else {
lib_name += '.so'
}
cmd += ' -o ${output_path}/${lib_name}'
return cmd
}
// Utility function to build linker command for tools/executables
pub fn build_tool_linker_command(object_files []string, executable string, build_config BuildConfig, tool_config ToolConfig) string {
mut cmd := '${build_config.compiler}'
// Add library search paths
cmd += ' -L${build_config.bin_dir}/lib'
for lib_path in build_config.lib_search_paths {
cmd += ' -L${lib_path}'
}
// Add debug flags for linking
if tool_config.debug {
cmd += ' -g'
}
// Add object files
for obj_file in object_files {
cmd += ' ${obj_file}'
}
// Add project libraries
for library in build_config.libraries {
if library != '' {
cmd += ' -l${library}'
}
}
// Add this tool's dependencies (shared libs)
for library in tool_config.libraries {
if library != '' {
cmd += ' -l:${library}.so'
}
}
// Add custom LDFLAGS
for flag in build_config.ldflags {
cmd += ' ${flag}'
}
for flag in tool_config.ldflags {
cmd += ' ${flag}'
}
cmd += ' -o ${executable}'
return cmd
}
// Utility function to build compiler command (existing - for backward compatibility)
pub fn build_compiler_command(source_file string, object_file string, build_config BuildConfig) string {
mut cmd := 'g++ -c'
mut cmd := '${build_config.compiler} -c'
// Add include directories
for include_dir in build_config.include_dirs {
cmd += ' -I${include_dir}'
}
// Add library search paths
for lib_path in build_config.lib_search_paths {
cmd += ' -L${lib_path}'
}
// Add debug/optimization flags
if build_config.debug {
cmd += ' -g -O0'
@ -152,30 +734,28 @@ pub fn build_compiler_command(source_file string, object_file string, build_conf
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++'
// Utility function to find source files
pub fn find_source_files(dir string) ![]string {
mut files := []string{}
// Add debug flags for linking
if build_config.debug {
cmd += ' -g'
if !os.is_dir(dir) {
return error('Source directory does not exist: ${dir}')
}
// Add object files
for obj_file in object_files {
cmd += ' ${obj_file}'
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
}
}
// 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
return files
}

View File

@ -5,29 +5,55 @@ pub fn show_help() {
println('Usage: lana [command] [options]')
println('')
println('Commands:')
println(' build Build the project')
println(' build Build the project (shared libs + tools)')
println(' clean Clean build files')
println(' run Build and run the project')
println(' run Build and run the main tool')
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(' -p, --parallel Enable parallel compilation')
println(' -o, --output Set output name')
println(' -I <dir> Add include directory')
println(' -l <lib> Add library')
println(' --config <file> Use config file')
println(' --shared-lib <name> <source> Add shared library')
println(' --tool <name> <source> Add tool/executable')
println('')
println('Configuration File (config.ini):')
println(' [global]')
println(' project_name = myproject')
println(' src_dir = src')
println(' debug = true')
println(' ')
println(' [shared_libs]')
println(' name = net')
println(' sources = src/lib/net/connection.cpp')
println(' libraries = cli')
println(' ')
println(' [tools]')
println(' name = dumpnbt')
println(' sources = src/tools/dumpnbt.cpp')
println(' libraries = nbt,cli')
println('')
println('Examples:')
println(' lana build -d -I include/mylib')
println(' lana build -d -v -p')
println(' lana run')
println(' lana build --shared-lib cli src/lib/cli.cpp --tool dumpnbt src/tools/dumpnbt.cpp')
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')
println(' src/ - Source files (.cpp, .cc, .cxx)')
println(' lib/ - Shared library sources')
println(' tools/ - Tool/executable sources')
println(' shaders/ - GLSL shader files (.vsh, .fsh)')
println(' include/ - Header files (.h, .hpp)')
println(' build/ - Object files and intermediates')
println(' bin/ - Output')
println(' lib/ - Shared libraries (.so/.dll)')
println(' tools/ - Executables')
println(' shaders/ - Compiled shaders (.spv)')
println(' config.ini - Build configuration')
}

View File

@ -6,7 +6,20 @@ pub fn init_project(project_name string) {
println('Initializing C++ project: ${project_name}')
// Create directory structure
dirs := ['src', 'include', 'build', 'bin']
dirs := [
'src',
'src/lib',
'src/lib/net',
'src/lib/game',
'src/tools',
'src/shaders',
'include',
'build',
'bin',
'bin/lib',
'bin/tools',
'bin/shaders'
]
for dir in dirs {
full_path := os.join_path(project_name, dir)
os.mkdir_all(full_path) or {
@ -14,27 +27,15 @@ pub fn init_project(project_name string) {
}
}
// 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
// Create basic main.cpp with build directives
main_content := r'
#include <iostream>
// build-directive: unit-name(tools/main)
// build-directive: depends-units()
// build-directive: link()
// build-directive: out(tools/main)
int main() {
std::cout << "Hello, ${project_name}!" << std::endl;
return 0;
@ -42,6 +43,50 @@ int main() {
'
os.write_file(os.join_path(project_name, 'src', 'main.cpp'), main_content) or { }
// Create example shared library with build directives
cli_content := r'
// build-directive: unit-name(lib/cli)
// build-directive: depends-units()
// build-directive: link()
// build-directive: out(lib/cli)
// build-directive: shared(true)
#include <iostream>
namespace lana {
void print_help() {
std::cout << "Lana CLI help" << std::endl;
}
}
'
os.write_file(os.join_path(project_name, 'src/lib', 'cli.cpp'), cli_content) or { }
// Create example tool with build directives
tool_content := r'
#include <iostream>
// build-directive: unit-name(tools/example_tool)
// build-directive: depends-units(lib/cli)
// build-directive: link(cli.so)
// build-directive: out(tools/example_tool)
int main() {
std::cout << "Tool example" << std::endl;
lana::print_help();
return 0;
}
'
os.write_file(os.join_path(project_name, 'src/tools', 'example_tool.cpp'), tool_content) or { }
// Create example shader
vertex_shader := r'
#version 450
layout(location = 0) in vec3 position;
void main() {
gl_Position = vec4(position, 1.0);
}
'
os.write_file(os.join_path(project_name, 'src/shaders', 'basic.vsh'), vertex_shader) or { }
// Create .gitignore
gitignore_content := r'
# Build files
@ -51,6 +96,9 @@ bin/
*.exe
*.dSYM
*.d
*.so
*.dll
*.dylib
# IDE files
.vscode/
@ -61,24 +109,48 @@ bin/
# OS files
.DS_Store
Thumbs.db
# Dependencies
dependencies/
'
os.write_file(os.join_path(project_name, '.gitignore'), gitignore_content) or { }
// Create config.ini
// Create config.ini with build directive support
config_content := r'
# ${project_name} lana build configuration
[global]
project_name = ${project_name}
src_dir = src
build_dir = build
bin_dir = bin
compiler = g++
debug = true
optimize = false
verbose = false
parallel_compilation = true
include_dirs = include
lib_search_paths =
cflags = -Wall -Wextra
ldflags =
# Build directives will be automatically parsed from source files
# using // build-directive: comments
[shared_libs]
# These are for legacy support or manual configuration
# Most shared libraries should use build directives in source files
[tools]
# These are for legacy support or manual configuration
# Most tools should use build directives in source files
# Uncomment for shader support
# shaders_dir = bin/shaders
'
os.write_file(os.join_path(project_name, 'config.ini'), config_content) or { }
// Create README.md
// Create README.md with build directive documentation
readme_content := r'
# ${project_name}
@ -91,43 +163,240 @@ A C++ project built with lana (Vlang C++ Build System)
lana build
```
### Run the project
### Run the main executable
```bash
lana run
```
### Run a specific tool
```bash
./bin/tools/example_tool
```
### Clean build files
```bash
lana clean
```
## Project Structure
- `src/` - Source files
- `include/` - Header files
- `src/` - Source files (.cpp, .cc, .cxx)
- `lib/` - Shared library sources
- `tools/` - Tool/executable sources
- `shaders/` - GLSL shader files (.vsh, .fsh)
- `include/` - Header files (.h, .hpp)
- `build/` - Object files and intermediate build files
- `bin/` - Executable output
- `lib/` - Shared libraries (.so/.dll)
- `tools/` - Tool executables
- `shaders/` - Compiled shaders (.spv)
- `config.ini` - Build configuration
## Build Directives
Lana reads build instructions directly from source files using special comments:
```
// build-directive: unit-name(tools/arraydump)
// build-directive: depends-units(lib/file,lib/cli)
// build-directive: link(file.so,cli.so)
// build-directive: out(tools/arraydump)
// build-directive: cflags(-Wall -Wextra)
// build-directive: ldflags()
// build-directive: shared(true)
```
### Directive Types
- **unit-name**: Name of the build unit (e.g., "tools/arraydump", "lib/file")
- **depends-units**: Dependencies for this unit (other units or libraries)
- **link**: Libraries to link against (e.g., "file.so", "cli.so")
- **out**: Output path for the binary (relative to bin/)
- **cflags**: Additional CFLAGS for this file
- **ldflags**: Additional LDFLAGS for this file
- **shared**: Whether this is a shared library (true/false)
### Example Source File
```cpp
// build-directive: unit-name(tools/dumpnbt)
// build-directive: depends-units(lib/nbt,lib/cli)
// build-directive: link(nbt.so,cli.so)
// build-directive: out(tools/dumpnbt)
// build-directive: cflags()
// build-directive: ldflags()
// build-directive: shared(false)
#include <iostream>
#include "nbt.h"
#include "cli.h"
int main() {
// Your code here
return 0;
}
```
## 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
Edit `config.ini` to customize global build settings:
### Global Settings
```ini
[global]
project_name = myproject
compiler = g++
debug = true
optimize = false
verbose = false
parallel_compilation = true
include_dirs = include,external/lib/include
lib_search_paths = /usr/local/lib,external/lib
cflags = -Wall -Wextra -std=c++17
ldflags = -pthread
```
### Shared Libraries (Legacy)
```ini
[shared_libs]
name = cli
sources = src/lib/cli.cpp
libraries =
include_dirs = include
cflags =
ldflags =
```
### Tools (Legacy)
```ini
[tools]
name = main
sources = src/main.cpp
libraries = cli
name = example_tool
sources = src/tools/example_tool.cpp
libraries = cli
```
### Shader Support
```ini
# Uncomment to enable shader compilation
shaders_dir = bin/shaders
```
## 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
-d, --debug Enable debug mode
-O, --optimize Enable optimization
-v, --verbose Verbose output
-p, --parallel Parallel compilation
-c, --compiler <name> Set C++ compiler (default: g++)
-o <name> Set project name
-I <dir> Add include directory
-L <dir> Add library search path
-l <lib> Add global library
--config <file> Use custom config file
--shared-lib <name> <source> Add shared library (legacy)
--tool <name> <source> Add tool (legacy)
```
## Build Process
1. **Parse build directives** from source files
2. **Build dependency graph** based on unit dependencies
3. **Compile source files** to object files
4. **Link libraries and executables** according to directives
5. **Compile shaders** if configured
The build system automatically handles:
- Dependency resolution and build ordering
- Incremental builds (only rebuild changed files)
- Shared library vs executable detection
- Custom flags per file
- Parallel compilation
## Examples
### Simple Tool
```cpp
// build-directive: unit-name(tools/calculator)
// build-directive: depends-units()
// build-directive: link()
// build-directive: out(tools/calculator)
#include <iostream>
int main() {
int a, b;
std::cin >> a >> b;
std::cout << "Sum: " << a + b << std::endl;
return 0;
}
```
### Shared Library with Dependencies
```cpp
// build-directive: unit-name(lib/math)
// build-directive: depends-units(lib/utils)
// build-directive: link(utils.so)
// build-directive: out(lib/math)
// build-directive: shared(true)
// build-directive: cflags(-fPIC)
#include "utils.h"
namespace math {
double add(double a, double b) {
return a + b;
}
}
```
### Complex Tool with Multiple Dependencies
```cpp
// build-directive: unit-name(tools/game)
// build-directive: depends-units(lib/graphics,lib/audio,lib/input)
// build-directive: link(graphics.so,audio.so,input.so,glfw,SDL2)
// build-directive: out(tools/game)
// build-directive: cflags(-DDEBUG)
// build-directive: ldflags(-pthread)
#include "graphics.h"
#include "audio.h"
#include "input.h"
int main() {
// Game loop
return 0;
}
```
## License
MIT License - see LICENSE file for details.
## Contributing
1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Submit a pull request
'
os.write_file(os.join_path(project_name, 'README.md'), readme_content) or { }
// Create example header
header_content := r'
#ifndef LANA_CLI_H
#define LANA_CLI_H
namespace lana {
void print_help();
}
#endif
'
os.write_file(os.join_path(project_name, 'include', 'cli.h'), header_content) or { }
println('Project initialized successfully!')
println('Created directory structure and template files')
println('')
@ -135,4 +404,13 @@ lana build [options]
println(' cd ${project_name}')
println(' lana build')
println(' lana run')
println(' ./bin/tools/example_tool')
println('')
println('Build Directives:')
println(' Add // build-directive: comments to your source files')
println(' See README.md for examples and documentation')
println('Configuration:')
println(' Edit config.ini for global build settings')
println(' Add your C++ source files to src/lib/ and src/tools/')
println(' Add GLSL shaders to src/shaders/ (.vsh, .fsh)')
}

46
lana.v
View File

@ -7,6 +7,12 @@ import runner
import initializer
import help
const (
// For runner compatibility
bin_dir = 'bin'
tools_dir = 'bin/tools'
)
fn main() {
mut config_data := config.parse_args() or { config.default_config }
@ -16,13 +22,47 @@ fn main() {
}
match os.args[1] {
'build' { builder.build(mut config_data) or { return } }
'build' {
builder.build(mut config_data) or {
eprintln('Build failed: ${err}')
exit(1)
}
}
'clean' { builder.clean(config_data) }
'run' {
builder.build(mut config_data) or { return }
runner.run_executable(config_data)
// For run, try to build first, then run the main tool
builder.build(mut config_data) or {
eprintln('Build failed: ${err}')
exit(1)
}
// Find and run the main executable (first tool or project_name)
main_executable := get_main_executable(config_data)
if main_executable != '' && os.is_file(main_executable) {
runner.run_executable(config_data)
} else {
println('No main executable found to run')
}
}
'init' { initializer.init_project(os.args[2] or { 'myproject' }) }
else { help.show_help() }
}
}
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)
}

View File

@ -4,20 +4,16 @@ import os
import config
pub fn run_executable(build_config config.BuildConfig) {
executable := os.join_path(build_config.bin_dir, build_config.project_name)
main_executable := get_main_executable(build_config)
if !os.is_file(executable) {
println('Executable not found: ${executable}')
if !os.is_file(main_executable) {
println('Main executable not found: ${main_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
}
println('Running ${main_executable}...')
res := os.execute('${main_executable}')
if res.exit_code == 0 {
println('Execution completed successfully!')
@ -30,4 +26,22 @@ 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)
}

7
v.mod Normal file
View File

@ -0,0 +1,7 @@
Module {
name: 'lana'
description: 'A build system for C++'
version: '0.0.0'
license: 'MIT'
dependencies: []
}