Add build directives
parent
99dc627c8c
commit
3dcef4035b
|
@ -0,0 +1,8 @@
|
|||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.v]
|
||||
indent_style = tab
|
|
@ -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
|
|
@ -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
633
DOCS.md
|
@ -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)*
|
||||
|
|
|
@ -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}')
|
||||
}
|
||||
}
|
696
config/config.v
696
config/config.v
|
@ -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
|
||||
}
|
42
help/help.v
42
help/help.v
|
@ -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')
|
||||
}
|
|
@ -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
46
lana.v
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
Loading…
Reference in New Issue