diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..01072ca --- /dev/null +++ b/.editorconfig @@ -0,0 +1,8 @@ +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.v] +indent_style = tab diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9a98968 --- /dev/null +++ b/.gitattributes @@ -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 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e3e1331 --- /dev/null +++ b/.gitignore @@ -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 diff --git a/DOCS.md b/DOCS.md index 3420732..942175b 100644 --- a/DOCS.md +++ b/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 ``` - -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 ` - Set output executable name -- `-I ` - Add include directory -- `-l ` - Link library -- `--config ` - 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: ()`. +- **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: () +``` +- ``: Directive name (e.g., `unit-name`). +- ``: Arguments (comma/space-separated for lists; `true/false` for bools). -Lana will create these directories automatically during the build process. +### Supported Directives +- **`unit-name()`**: Unique unit ID (e.g., `"lib/cli"`, `"tools/mytool"`). Required for custom builds. Defaults to file path if omitted. +- **`depends-units(,,...)`**: Dependencies (other units, e.g., `"lib/utils,lib/file"`). Builds them first. +- **`link(,,...)`**: Libraries to link (e.g., `"utils.so,pthread,boost_system"`). Internal (Lana-built) or external. +- **`out()`**: Output relative to `bin/` (e.g., `"tools/mytool"`, `"lib/mylib"`). Defaults to unit name. +- **`cflags( ...)`**: Per-file compiler flags (e.g., `"-std=c++20 -fPIC"`). Appends to global `cflags`. +- **`ldflags( ...)`**: Per-file linker flags (e.g., `"-static -pthread"`). Appends to global `ldflags`. +- **`shared()`**: 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 + +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 +#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 +#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 // 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 , --output ` | Set executable name | `lana build -o myapp` | - -### Include/Library Options - -| Option | Description | Example | -|--------|-------------|---------| -| `-I ` | Add include directory | `lana build -I external/include` | -| `-l ` | Link library | `lana build -l pthread` | - -### Configuration Options - -| Option | Description | Example | -|--------|-------------|---------| -| `--config ` | 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 ` | Output name | `lana build -o app` | +| `-I ` | Include dir | `lana build -I external/` | +| `-l ` | Link lib | `lana build -l pthread` | +| `--config ` | Custom config | `lana build --config release.ini` | +| `--shared-lib ` | Legacy shared lib | `lana build --shared-lib cli src/lib/cli.cpp` | +| `--tool ` | 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)* diff --git a/builder/builder.v b/builder/builder.v index c3b655e..1044073 100644 --- a/builder/builder.v +++ b/builder/builder.v @@ -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_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_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}') - } } \ No newline at end of file diff --git a/config/config.v b/config/config.v index 6f5754f..c624ee2 100644 --- a/config/config.v +++ b/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 } \ No newline at end of file diff --git a/help/help.v b/help/help.v index ae9a893..964681d 100644 --- a/help/help.v +++ b/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 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 Add include directory') println(' -l Add library') println(' --config Use config file') + println(' --shared-lib Add shared library') + println(' --tool 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') } \ No newline at end of file diff --git a/initializer/initializer.v b/initializer/initializer.v index 4db4535..c96cc97 100644 --- a/initializer/initializer.v +++ b/initializer/initializer.v @@ -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 +// 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 + +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 +// 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 +#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 Add include directory - -l Add library - -o Set output name - --config Use custom config file + -d, --debug Enable debug mode + -O, --optimize Enable optimization + -v, --verbose Verbose output + -p, --parallel Parallel compilation + -c, --compiler Set C++ compiler (default: g++) + -o Set project name + -I Add include directory + -L Add library search path + -l Add global library + --config Use custom config file + --shared-lib Add shared library (legacy) + --tool 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 + +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)') } \ No newline at end of file diff --git a/lana.v b/lana.v index 9ff1576..7b325ca 100644 --- a/lana.v +++ b/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) } \ No newline at end of file diff --git a/runner/runner.v b/runner/runner.v index f813d6a..6c443fb 100644 --- a/runner/runner.v +++ b/runner/runner.v @@ -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) } \ No newline at end of file diff --git a/v.mod b/v.mod new file mode 100644 index 0000000..cbe7aa9 --- /dev/null +++ b/v.mod @@ -0,0 +1,7 @@ +Module { + name: 'lana' + description: 'A build system for C++' + version: '0.0.0' + license: 'MIT' + dependencies: [] +}