Fix issues/update

This commit is contained in:
Chase Sunstrom
2025-12-21 15:12:57 -06:00
parent 065bbaf31d
commit 23ad251eac
97 changed files with 5269 additions and 3915 deletions
+34 -5
View File
@@ -1,10 +1,10 @@
# CMakeLists.txt for cforge v3.0.0
# CMakeLists.txt for cforge v3.0.1
# Generated by cforge - C++ project management tool
cmake_minimum_required(VERSION 3.15)
# Project configuration
project(cforge VERSION 3.0.0 LANGUAGES CXX )
project(cforge VERSION 3.0.1 LANGUAGES CXX )
# Set source directory
set(SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
@@ -46,6 +46,9 @@ else()
endif()
message(STATUS "Platform: ${CFORGE_PLATFORM}, Compiler: ${CFORGE_COMPILER}")
# CMake options
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Build configurations
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Debug")
@@ -120,12 +123,38 @@ target_link_libraries(${PROJECT_NAME} PUBLIC
tomlplusplus::tomlplusplus
)
# Portable compiler flags per configuration
# Debug configuration flags
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
# Portable compiler flags
if(MSVC AND NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
target_compile_options(${PROJECT_NAME} PRIVATE /Od /W4 /Zi /EHsc /GR)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
target_compile_options(${PROJECT_NAME} PRIVATE -Og -Wall -Wextra -g -fdiagnostics-color=always)
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
target_compile_options(${PROJECT_NAME} PRIVATE -Og -Wall -Wextra -g -fcolor-diagnostics)
endif()
endif()
# Release configuration flags
if(CMAKE_BUILD_TYPE STREQUAL "Release")
# Portable compiler flags
if(MSVC AND NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
target_compile_options(${PROJECT_NAME} PRIVATE /W4 /GL /EHsc /GR)
target_link_options(${PROJECT_NAME} PRIVATE /LTCG)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -flto -fdiagnostics-color=always)
target_link_options(${PROJECT_NAME} PRIVATE -flto)
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -flto -fcolor-diagnostics)
target_link_options(${PROJECT_NAME} PRIVATE -flto)
endif()
endif()
# Definitions for config 'Debug'
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
target_compile_definitions(${PROJECT_NAME} PUBLIC DEBUG=1)
target_compile_definitions(${PROJECT_NAME} PUBLIC FMT_HEADER_ONLY=ON)
target_compile_definitions(${PROJECT_NAME} PUBLIC FMT_UNICODE=0)
target_compile_definitions(${PROJECT_NAME} PUBLIC CMAKE_EXPORT_COMPILE_COMMANDS=ON)
endif()
# Link libraries
@@ -172,7 +201,7 @@ endif()
set(CPACK_PACKAGE_NAME "${PROJECT_NAME}")
set(CPACK_PACKAGE_VENDOR "Chase Sunstrom")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A C/C++ build tool with dependency management")
set(CPACK_PACKAGE_VERSION "3.0.0")
set(CPACK_PACKAGE_VERSION "3.0.1")
set(CPACK_PACKAGE_INSTALL_DIRECTORY "${PROJECT_NAME}")
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
+464 -1020
View File
File diff suppressed because it is too large Load Diff
+24 -24
View File
@@ -208,9 +208,9 @@
"0": {
"js": [
{
"file": "assets/js/66992f67.110c4298.js",
"hash": "6fd93c89ca2fa91e",
"publicPath": "/cforge/assets/js/66992f67.110c4298.js"
"file": "assets/js/66992f67.e27c6096.js",
"hash": "873adecb12806615",
"publicPath": "/cforge/assets/js/66992f67.e27c6096.js"
}
]
},
@@ -334,9 +334,9 @@
"354": {
"js": [
{
"file": "assets/js/runtime~main.76be665e.js",
"hash": "84860eeea74b6e98",
"publicPath": "/cforge/assets/js/runtime~main.76be665e.js"
"file": "assets/js/runtime~main.6fa7163a.js",
"hash": "9f7847a595add33a",
"publicPath": "/cforge/assets/js/runtime~main.6fa7163a.js"
}
]
},
@@ -352,9 +352,9 @@
"413": {
"js": [
{
"file": "assets/js/5e38ca63.85f4cb1c.js",
"hash": "577ac19e37e2db6d",
"publicPath": "/cforge/assets/js/5e38ca63.85f4cb1c.js"
"file": "assets/js/5e38ca63.df34827b.js",
"hash": "7c741cd2dc27bae3",
"publicPath": "/cforge/assets/js/5e38ca63.df34827b.js"
}
]
},
@@ -406,9 +406,9 @@
"568": {
"js": [
{
"file": "assets/js/449695dd.5657e882.js",
"hash": "6505c3eb51372379",
"publicPath": "/cforge/assets/js/449695dd.5657e882.js"
"file": "assets/js/449695dd.3c67e53d.js",
"hash": "0fd5c346ec5abed6",
"publicPath": "/cforge/assets/js/449695dd.3c67e53d.js"
}
]
},
@@ -496,9 +496,9 @@
"760": {
"js": [
{
"file": "assets/js/15db3679.48e38877.js",
"hash": "8370d682dd6625c6",
"publicPath": "/cforge/assets/js/15db3679.48e38877.js"
"file": "assets/js/15db3679.5e05e68a.js",
"hash": "a926f102752a1713",
"publicPath": "/cforge/assets/js/15db3679.5e05e68a.js"
}
]
},
@@ -514,9 +514,9 @@
"803": {
"js": [
{
"file": "assets/js/3b8c55ea.816fe1cc.js",
"hash": "dedd6f86afd2ce54",
"publicPath": "/cforge/assets/js/3b8c55ea.816fe1cc.js"
"file": "assets/js/3b8c55ea.2d34ef9c.js",
"hash": "fd7a99599d4939b3",
"publicPath": "/cforge/assets/js/3b8c55ea.2d34ef9c.js"
}
]
},
@@ -532,9 +532,9 @@
"814": {
"js": [
{
"file": "assets/js/72e14192.0ceab5a2.js",
"hash": "5c40164a27754d05",
"publicPath": "/cforge/assets/js/72e14192.0ceab5a2.js"
"file": "assets/js/72e14192.9a897053.js",
"hash": "456fea6a8bab0d62",
"publicPath": "/cforge/assets/js/72e14192.9a897053.js"
}
]
},
@@ -622,9 +622,9 @@
"976": {
"js": [
{
"file": "assets/js/0e384e19.2cfa9411.js",
"hash": "01cd1568b2530913",
"publicPath": "/cforge/assets/js/0e384e19.2cfa9411.js"
"file": "assets/js/0e384e19.0b56c902.js",
"hash": "c3fba18d51532011",
"publicPath": "/cforge/assets/js/0e384e19.0b56c902.js"
}
]
},
@@ -1 +1 @@
{"version":{"pluginId":"default","version":"current","label":"Next","banner":null,"badge":false,"noIndex":false,"className":"docs-version-current","isLast":true,"docsSidebars":{"sidebar":[{"type":"category","label":"CForge","collapsed":false,"items":[{"type":"link","label":"Introduction","href":"/cforge/docs/intro","docId":"intro","unlisted":false},{"type":"link","label":"Installation","href":"/cforge/docs/installation","docId":"installation","unlisted":false},{"type":"link","label":"Quick Start","href":"/cforge/docs/quick-start","docId":"quick-start","unlisted":false},{"type":"link","label":"Command Reference","href":"/cforge/docs/command-reference","docId":"command-reference","unlisted":false},{"type":"link","label":"Project Configuration","href":"/cforge/docs/project-configuration","docId":"project-configuration","unlisted":false},{"type":"link","label":"Working with Dependencies","href":"/cforge/docs/dependencies","docId":"dependencies","unlisted":false},{"type":"link","label":"Workspaces","href":"/cforge/docs/workspaces","docId":"workspaces","unlisted":false},{"type":"link","label":"Build Variants","href":"/cforge/docs/build-variants","docId":"build-variants","unlisted":false},{"type":"link","label":"Cross-Compilation","href":"/cforge/docs/cross-compilation","docId":"cross-compilation","unlisted":false},{"type":"link","label":"IDE Integration","href":"/cforge/docs/ide-integration","docId":"ide-integration","unlisted":false},{"type":"link","label":"Scripts & Hooks","href":"/cforge/docs/scripts-hooks","docId":"scripts-hooks","unlisted":false},{"type":"link","label":"Testing","href":"/cforge/docs/testing","docId":"testing","unlisted":false},{"type":"link","label":"Advanced Topics","href":"/cforge/docs/advanced-topics","docId":"advanced-topics","unlisted":false},{"type":"link","label":"Examples","href":"/cforge/docs/examples","docId":"examples","unlisted":false},{"type":"link","label":"Troubleshooting","href":"/cforge/docs/troubleshooting","docId":"troubleshooting","unlisted":false},{"type":"link","label":"Contributing","href":"/cforge/docs/contributing","docId":"contributing","unlisted":false},{"type":"link","label":"License","href":"/cforge/docs/license","docId":"license","unlisted":false}],"collapsible":true}]},"docs":{"advanced-topics":{"id":"advanced-topics","title":"Advanced Topics","description":"🧩 Advanced Topics","sidebar":"sidebar"},"build-variants":{"id":"build-variants","title":"Build Variants","description":"🚩 Build Variants","sidebar":"sidebar"},"command-reference":{"id":"command-reference","title":"Command Reference","description":"CForge provides a comprehensive set of commands for building, testing, and managing C/C++ projects.","sidebar":"sidebar"},"contributing":{"id":"contributing","title":"Contributing","description":"🤝 Contributing","sidebar":"sidebar"},"cross-compilation":{"id":"cross-compilation","title":"Cross-Compilation","description":"Cross-Compilation","sidebar":"sidebar"},"dependencies":{"id":"dependencies","title":"Working with Dependencies","description":"Working with Dependencies","sidebar":"sidebar"},"examples":{"id":"examples","title":"Examples","description":"📚 Examples","sidebar":"sidebar"},"ide-integration":{"id":"ide-integration","title":"IDE Integration","description":"🖥️ IDE Integration","sidebar":"sidebar"},"installation":{"id":"installation","title":"Installation","description":"Install CForge on your system.","sidebar":"sidebar"},"intro":{"id":"intro","title":"Introduction","description":"Version","sidebar":"sidebar"},"license":{"id":"license","title":"License","description":"📄 License","sidebar":"sidebar"},"project-configuration":{"id":"project-configuration","title":"Project Configuration","description":"Project Configuration","sidebar":"sidebar"},"quick-start":{"id":"quick-start","title":"Quick Start","description":"Get up and running with CForge in minutes.","sidebar":"sidebar"},"scripts-hooks":{"id":"scripts-hooks","title":"Scripts & Hooks","description":"📝 Scripts & Hooks","sidebar":"sidebar"},"testing":{"id":"testing","title":"Testing","description":"🧪 Testing","sidebar":"sidebar"},"troubleshooting":{"id":"troubleshooting","title":"Troubleshooting","description":"Common issues and their solutions.","sidebar":"sidebar"},"workspaces":{"id":"workspaces","title":"Workspaces","description":"Workspaces","sidebar":"sidebar"}}}}
{"version":{"pluginId":"default","version":"current","label":"Next","banner":null,"badge":false,"noIndex":false,"className":"docs-version-current","isLast":true,"docsSidebars":{"sidebar":[{"type":"category","label":"CForge","collapsed":false,"items":[{"type":"link","label":"Introduction","href":"/cforge/docs/intro","docId":"intro","unlisted":false},{"type":"link","label":"Installation","href":"/cforge/docs/installation","docId":"installation","unlisted":false},{"type":"link","label":"Quick Start","href":"/cforge/docs/quick-start","docId":"quick-start","unlisted":false},{"type":"link","label":"Command Reference","href":"/cforge/docs/command-reference","docId":"command-reference","unlisted":false},{"type":"link","label":"Project Configuration","href":"/cforge/docs/project-configuration","docId":"project-configuration","unlisted":false},{"type":"link","label":"Working with Dependencies","href":"/cforge/docs/dependencies","docId":"dependencies","unlisted":false},{"type":"link","label":"Workspaces","href":"/cforge/docs/workspaces","docId":"workspaces","unlisted":false},{"type":"link","label":"Build Variants","href":"/cforge/docs/build-variants","docId":"build-variants","unlisted":false},{"type":"link","label":"Cross-Compilation","href":"/cforge/docs/cross-compilation","docId":"cross-compilation","unlisted":false},{"type":"link","label":"IDE Integration","href":"/cforge/docs/ide-integration","docId":"ide-integration","unlisted":false},{"type":"link","label":"Scripts & Hooks","href":"/cforge/docs/scripts-hooks","docId":"scripts-hooks","unlisted":false},{"type":"link","label":"Testing","href":"/cforge/docs/testing","docId":"testing","unlisted":false},{"type":"link","label":"Advanced Topics","href":"/cforge/docs/advanced-topics","docId":"advanced-topics","unlisted":false},{"type":"link","label":"Examples","href":"/cforge/docs/examples","docId":"examples","unlisted":false},{"type":"link","label":"Troubleshooting","href":"/cforge/docs/troubleshooting","docId":"troubleshooting","unlisted":false},{"type":"link","label":"Contributing","href":"/cforge/docs/contributing","docId":"contributing","unlisted":false},{"type":"link","label":"License","href":"/cforge/docs/license","docId":"license","unlisted":false}],"collapsible":true}]},"docs":{"advanced-topics":{"id":"advanced-topics","title":"Advanced Topics","description":"🧩 Advanced Topics","sidebar":"sidebar"},"build-variants":{"id":"build-variants","title":"Build Variants","description":"Build Variants","sidebar":"sidebar"},"command-reference":{"id":"command-reference","title":"Command Reference","description":"CForge provides a comprehensive set of commands for building, testing, and managing C/C++ projects.","sidebar":"sidebar"},"contributing":{"id":"contributing","title":"Contributing","description":"🤝 Contributing","sidebar":"sidebar"},"cross-compilation":{"id":"cross-compilation","title":"Cross-Compilation","description":"Cross-Compilation","sidebar":"sidebar"},"dependencies":{"id":"dependencies","title":"Working with Dependencies","description":"Working with Dependencies","sidebar":"sidebar"},"examples":{"id":"examples","title":"Examples","description":"📚 Examples","sidebar":"sidebar"},"ide-integration":{"id":"ide-integration","title":"IDE Integration","description":"🖥️ IDE Integration","sidebar":"sidebar"},"installation":{"id":"installation","title":"Installation","description":"Install CForge on your system.","sidebar":"sidebar"},"intro":{"id":"intro","title":"Introduction","description":"Version","sidebar":"sidebar"},"license":{"id":"license","title":"License","description":"📄 License","sidebar":"sidebar"},"project-configuration":{"id":"project-configuration","title":"Project Configuration","description":"Project Configuration","sidebar":"sidebar"},"quick-start":{"id":"quick-start","title":"Quick Start","description":"Get up and running with CForge in minutes.","sidebar":"sidebar"},"scripts-hooks":{"id":"scripts-hooks","title":"Scripts & Hooks","description":"📝 Scripts & Hooks","sidebar":"sidebar"},"testing":{"id":"testing","title":"Testing","description":"🧪 Testing","sidebar":"sidebar"},"troubleshooting":{"id":"troubleshooting","title":"Troubleshooting","description":"Common issues and their solutions.","sidebar":"sidebar"},"workspaces":{"id":"workspaces","title":"Workspaces","description":"Workspaces","sidebar":"sidebar"}}}}
@@ -1,7 +1,7 @@
{
"id": "build-variants",
"title": "Build Variants",
"description": "🚩 Build Variants",
"description": "Build Variants",
"source": "@site/docs/build-variants.md",
"sourceDirName": ".",
"slug": "/build-variants",
+41 -4
View File
@@ -3,7 +3,7 @@ id: build-variants
title: Build Variants
---
## 🚩 Build Variants
## Build Variants
Build variants allow for different build configurations beyond just Debug/Release:
@@ -16,15 +16,52 @@ description = "Standard build"
[variants.variants.performance]
description = "Optimized build"
optimize = "aggressive"
lto = true
defines = ["HIGH_PERF=1"]
flags = ["OPTIMIZE_MAX", "LTO"]
[variants.variants.memory_safety]
description = "Build with memory safety checks"
sanitizers = ["address", "undefined"]
hardening = "full"
defines = ["ENABLE_MEMORY_SAFETY=1"]
flags = ["MEMSAFE"]
[variants.variants.minimal]
description = "Minimal size build"
optimize = "size"
exceptions = false
rtti = false
```
Building with variants:
`` cforge build --variant performance ``
```bash
cforge build --variant performance
cforge build --variant memory_safety
```
### Combining with Build Configurations
Variants work alongside standard build configurations:
```toml
[build.config.debug]
optimize = "debug"
debug_info = true
warnings = "all"
[build.config.release]
optimize = "speed"
lto = true
[variants.variants.asan]
sanitizers = ["address"]
```
```bash
# Debug with address sanitizer
cforge build -c Debug --variant asan
# Release with address sanitizer
cforge build -c Release --variant asan
```
+51 -5
View File
@@ -39,9 +39,11 @@ CForge provides a comprehensive set of commands for building, testing, and manag
|--------------|------------------------------------------|------------------------------------|
| `add` | Add a dependency to the project | `cforge add fmt` |
| `remove` | Remove a dependency | `cforge remove spdlog` |
| `update` | Update dependencies | `cforge update` |
| `search` | Search package registry | `cforge search json` |
| `info` | Show package details | `cforge info spdlog` |
| `update` | Update cforge or packages | `cforge update --self` |
| `lock` | Lock dependency versions | `cforge lock` |
| `list` | List variants, configs, or targets | `cforge list variants` |
| `list` | List dependencies or projects | `cforge list` |
| `ide` | Generate IDE project files | `cforge ide vscode` |
| `vcpkg` | Manage vcpkg integration | `cforge vcpkg install` |
@@ -332,21 +334,65 @@ cforge deps --update
cforge deps --check
```
### search
Search the cforge package registry.
```bash
# Search for packages
cforge search json
# Search for logging libraries
cforge search log
```
### info
Get detailed information about a package.
```bash
# Show package info
cforge info spdlog
# Show available versions
cforge info fmt --versions
```
### add / remove
Manage dependencies in cforge.toml.
```bash
# Add a vcpkg dependency
# Add from registry (default)
cforge add fmt
# Add with version
cforge add "spdlog@1.12.0"
# Add with specific version
cforge add fmt@11.1.4
# Add with features
cforge add spdlog --features async
# Add from Git
cforge add mylib --git https://github.com/user/mylib --tag v1.0
# Add from vcpkg
cforge add boost --vcpkg
# Remove a dependency
cforge remove spdlog
```
**Options:**
| Option | Description |
|--------|-------------|
| `@version` | Specify version (e.g., `fmt@11.1.4`) |
| `--git <url>` | Add as Git dependency |
| `--tag <tag>` | Git tag (with --git) |
| `--branch <name>` | Git branch (with --git) |
| `--vcpkg` | Add as vcpkg package |
| `--features <list>` | Comma-separated features |
| `--header-only` | Mark as header-only library |
### package
Create distributable packages.
+1 -1
View File
@@ -70,7 +70,7 @@ cforge version
Expected output:
```
cforge 1.5.0
cforge 3.0.1
```
## Shell Completions
+17 -5
View File
@@ -6,8 +6,8 @@ slug: /intro
# CForge
![Version](https://img.shields.io/badge/beta-1.5.0-blue.svg)
![License](https://img.shields.io/badge/license-MIT-green.svg)
![Version](https://img.shields.io/badge/version-3.0.1-blue.svg)
![License](https://img.shields.io/badge/license-PolyForm%20NC-green.svg)
![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-lightgrey.svg)
**A modern TOML-based build system for C/C++ with CMake & vcpkg integration.**
@@ -22,10 +22,12 @@ This project is currently in **BETA**. Features may be incomplete, contain bugs,
### Build System
- **Simple TOML Configuration** - Replace complex CMakeLists.txt with readable TOML
- **Portable Compiler Flags** - Write once, works on MSVC, GCC, and Clang
- **Cargo-Style Output** - Beautiful colored output with build timing
- **Multi-Platform** - Windows, macOS, and Linux support
- **Cross-Compilation** - Android, iOS, Raspberry Pi, WebAssembly toolchains
- **Workspaces** - Manage monorepos with automatic dependency resolution
- **Lock Files** - Reproducible builds with exact dependency versions
### Dependency Management
- **vcpkg Integration** - First-class support for vcpkg packages
@@ -62,11 +64,21 @@ This project is currently in **BETA**. Features may be incomplete, contain bugs,
[project]
name = "my_app"
version = "1.0.0"
type = "executable"
binary_type = "executable"
cpp_standard = "17"
[build.config.debug]
optimize = "debug"
debug_info = true
warnings = "all"
[build.config.release]
optimize = "speed"
lto = true
[dependencies]
fmt = { version = "10.1.0", source = "vcpkg" }
spdlog = { version = "1.12.0", source = "vcpkg" }
fmt = "11.1.4"
spdlog = "1.12.0"
```
**Build & Run:**
+50 -14
View File
@@ -188,30 +188,66 @@ flags = ["-Wall", "-Wextra", "-fPIC"]
### Build Configurations
Define build configurations with specific compiler flags and defines:
Define build configurations with portable compiler options:
```toml
[build.config.debug]
defines = ["DEBUG=1", "_DEBUG"]
flags = ["DEBUG_INFO", "NO_OPT"]
optimize = "debug"
debug_info = true
warnings = "all"
sanitizers = ["address"]
defines = ["DEBUG=1"]
[build.config.release]
optimize = "speed"
warnings = "all"
lto = true
defines = ["NDEBUG=1"]
flags = ["OPTIMIZE", "LTO"]
[build.config.relwithdebinfo]
optimize = "speed"
debug_info = true
defines = ["NDEBUG=1"]
flags = ["OPTIMIZE", "DEBUG_INFO"]
```
### Available Flags
### Portable Compiler Flags
Abstract flags that translate to compiler-specific options:
CForge provides portable build options that automatically translate to the correct flags for each compiler:
| Flag | Description | MSVC | GCC/Clang |
|------|-------------|------|-----------|
| `DEBUG_INFO` | Debug symbols | `/Zi` | `-g` |
| `NO_OPT` | Disable optimization | `/Od` | `-O0` |
| `OPTIMIZE` | Full optimization | `/O2` | `-O3` |
| `LTO` | Link-time optimization | `/GL` | `-flto` |
| `UNICODE` | Unicode support | `/DUNICODE` | `-DUNICODE` |
| Option | Values | Description |
|--------|--------|-------------|
| `optimize` | `"none"`, `"debug"`, `"size"`, `"speed"`, `"aggressive"` | Optimization level |
| `warnings` | `"none"`, `"default"`, `"all"`, `"strict"`, `"pedantic"` | Warning level |
| `debug_info` | `true` / `false` | Include debug symbols |
| `sanitizers` | `["address", "undefined", "thread", "leak"]` | Runtime sanitizers |
| `lto` | `true` / `false` | Link-time optimization |
| `exceptions` | `true` / `false` | C++ exceptions |
| `rtti` | `true` / `false` | Runtime type info |
| `hardening` | `"none"`, `"basic"`, `"full"` | Security hardening |
| `stdlib` | `"default"`, `"libc++"`, `"libstdc++"` | Standard library |
| `visibility` | `"default"`, `"hidden"` | Symbol visibility |
### Flag Translation
| Portable | MSVC | GCC/Clang |
|----------|------|-----------|
| `optimize = "none"` | `/Od` | `-O0` |
| `optimize = "debug"` | `/Od` | `-Og` |
| `optimize = "size"` | `/O1 /Os` | `-Os` |
| `optimize = "speed"` | `/O2` | `-O2` |
| `optimize = "aggressive"` | `/Ox` | `-O3` |
| `warnings = "all"` | `/W4` | `-Wall -Wextra` |
| `warnings = "strict"` | `/W4 /WX` | `-Wall -Wextra -Werror` |
| `warnings = "pedantic"` | `/W4 /WX /permissive-` | `-Wall -Wextra -Wpedantic -Werror` |
| `lto = true` | `/GL` + `/LTCG` | `-flto` |
| `debug_info = true` | `/Zi` | `-g` |
| `sanitizers = ["address"]` | `/fsanitize=address` | `-fsanitize=address` |
| `hardening = "basic"` | `/GS /sdl` | `-fstack-protector-strong -D_FORTIFY_SOURCE=2` |
| `hardening = "full"` | `/GS /sdl /GUARD:CF` | `-fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIE` |
These portable options can be used in:
- `[build.config.<name>]` - Per-configuration settings
- `[platform.<name>]` - Per-platform settings
- `[compiler.<name>]` - Per-compiler settings
Raw compiler flags can still be used alongside portable options with the `flags` array.
+23 -8
View File
@@ -58,12 +58,21 @@ my_project/
[project]
name = "my_project"
version = "1.0.0"
type = "executable"
binary_type = "executable"
cpp_standard = "17"
[build]
source_dir = "src"
include_dir = "include"
source_dirs = ["src"]
include_dirs = ["include"]
[build.config.debug]
optimize = "debug"
debug_info = true
warnings = "all"
[build.config.release]
optimize = "speed"
lto = true
```
## Build & Run
@@ -100,15 +109,21 @@ Edit `cforge.toml` to add dependencies:
```toml
[dependencies]
fmt = { version = "10.1.0", source = "vcpkg" }
spdlog = { version = "1.12.0", source = "vcpkg" }
fmt = "11.1.4"
spdlog = "1.12.0"
```
Or use the CLI:
```bash
cforge add fmt
cforge add spdlog
# Add from registry
cforge add fmt@11.1.4
# Search for packages
cforge search json
# Add from vcpkg
cforge add boost --vcpkg
```
Then rebuild:
@@ -117,7 +132,7 @@ Then rebuild:
cforge build
```
CForge will automatically install dependencies via vcpkg.
CForge will automatically download and build dependencies.
## Running Tests
+11 -6
View File
@@ -1,27 +1,32 @@
[project]
name = "cforge"
version = "3.0.0"
version = "3.0.1"
description = "A C/C++ build tool with dependency management"
cpp_standard = "17"
binary_type = "executable"
authors = ["Chase Sunstrom <casunstrom@gmail.com>"]
homepage = "https://github.com/ChaseSunstrom/cforge"
repository = "https://github.com/ChaseSunstrom/cforge.git"
license = "MIT"
license = "Polyform Noncommercial 1.0.0"
[build]
build_type = "Debug"
directory = "build"
source_dirs = ["src"]
include_dirs = ["include"]
export_compile_commands = true
[build.config.debug]
defines = ["DEBUG=1", "FMT_HEADER_ONLY=ON", "FMT_UNICODE=0", "CMAKE_EXPORT_COMPILE_COMMANDS=ON"]
flags = ["DEBUG_INFO", "NO_OPT"]
optimize = "debug"
debug_info = true
warnings = "all"
defines = ["DEBUG=1", "FMT_UNICODE=0"]
[build.config.release]
defines = ["NDEBUG=1", "FMT_HEADER_ONLY=ON", "FMT_UNICODE=0", "CMAKE_EXPORT_COMPILE_COMMANDS=ON"]
flags = ["OPTIMIZE"]
optimize = "agressive"
warnings = "all"
lto = true
defines = ["NDEBUG=1", "FMT_UNICODE=0"]
[package]
enabled = true
+1 -1
View File
@@ -5,7 +5,7 @@
<meta name="generator" content="Docusaurus v3.7.0">
<title data-rh="true">Page Not Found | CForge</title><meta data-rh="true" name="viewport" content="width=device-width,initial-scale=1"><meta data-rh="true" name="twitter:card" content="summary_large_image"><meta data-rh="true" property="og:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" name="twitter:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" property="og:url" content="https://chasesunstrom.github.io/cforge/404.html"><meta data-rh="true" property="og:locale" content="en"><meta data-rh="true" name="docusaurus_locale" content="en"><meta data-rh="true" name="docusaurus_tag" content="default"><meta data-rh="true" name="docsearch:language" content="en"><meta data-rh="true" name="docsearch:docusaurus_tag" content="default"><meta data-rh="true" property="og:title" content="Page Not Found | CForge"><link data-rh="true" rel="icon" href="/cforge/img/logo.svg"><link data-rh="true" rel="canonical" href="https://chasesunstrom.github.io/cforge/404.html"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/404.html" hreflang="en"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/404.html" hreflang="x-default"><link rel="alternate" type="application/rss+xml" href="/cforge/blog/rss.xml" title="CForge RSS Feed">
<link rel="alternate" type="application/atom+xml" href="/cforge/blog/atom.xml" title="CForge Atom Feed"><link rel="stylesheet" href="/cforge/assets/css/styles.5a56e4b0.css">
<script src="/cforge/assets/js/runtime~main.76be665e.js" defer="defer"></script>
<script src="/cforge/assets/js/runtime~main.6fa7163a.js" defer="defer"></script>
<script src="/cforge/assets/js/main.50450276.js" defer="defer"></script>
</head>
<body class="navigation-with-keyboard">
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[760],{6354:(n,i,e)=>{e.r(i),e.d(i,{assets:()=>o,contentTitle:()=>d,default:()=>u,frontMatter:()=>t,metadata:()=>r,toc:()=>l});const r=JSON.parse('{"id":"build-variants","title":"Build Variants","description":"Build Variants","source":"@site/docs/build-variants.md","sourceDirName":".","slug":"/build-variants","permalink":"/cforge/docs/build-variants","draft":false,"unlisted":false,"editUrl":"https://github.com/chasesunstrom/cforge/edit/main/docs/docs/build-variants.md","tags":[],"version":"current","frontMatter":{"id":"build-variants","title":"Build Variants"},"sidebar":"sidebar","previous":{"title":"Workspaces","permalink":"/cforge/docs/workspaces"},"next":{"title":"Cross-Compilation","permalink":"/cforge/docs/cross-compilation"}}');var a=e(4848),s=e(8453);const t={id:"build-variants",title:"Build Variants"},d=void 0,o={},l=[{value:"Build Variants",id:"build-variants",level:2},{value:"Combining with Build Configurations",id:"combining-with-build-configurations",level:3}];function c(n){const i={code:"code",h2:"h2",h3:"h3",p:"p",pre:"pre",...(0,s.R)(),...n.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(i.h2,{id:"build-variants",children:"Build Variants"}),"\n",(0,a.jsx)(i.p,{children:"Build variants allow for different build configurations beyond just Debug/Release:"}),"\n",(0,a.jsx)(i.pre,{children:(0,a.jsx)(i.code,{className:"language-toml",children:'[variants]\r\ndefault = "standard"\r\n\r\n[variants.variants.standard]\r\ndescription = "Standard build"\r\n\r\n[variants.variants.performance]\r\ndescription = "Optimized build"\r\noptimize = "aggressive"\r\nlto = true\r\ndefines = ["HIGH_PERF=1"]\r\n\r\n[variants.variants.memory_safety]\r\ndescription = "Build with memory safety checks"\r\nsanitizers = ["address", "undefined"]\r\nhardening = "full"\r\ndefines = ["ENABLE_MEMORY_SAFETY=1"]\r\n\r\n[variants.variants.minimal]\r\ndescription = "Minimal size build"\r\noptimize = "size"\r\nexceptions = false\r\nrtti = false\n'})}),"\n",(0,a.jsx)(i.p,{children:"Building with variants:"}),"\n",(0,a.jsx)(i.pre,{children:(0,a.jsx)(i.code,{className:"language-bash",children:"cforge build --variant performance\r\ncforge build --variant memory_safety\n"})}),"\n",(0,a.jsx)(i.h3,{id:"combining-with-build-configurations",children:"Combining with Build Configurations"}),"\n",(0,a.jsx)(i.p,{children:"Variants work alongside standard build configurations:"}),"\n",(0,a.jsx)(i.pre,{children:(0,a.jsx)(i.code,{className:"language-toml",children:'[build.config.debug]\r\noptimize = "debug"\r\ndebug_info = true\r\nwarnings = "all"\r\n\r\n[build.config.release]\r\noptimize = "speed"\r\nlto = true\r\n\r\n[variants.variants.asan]\r\nsanitizers = ["address"]\n'})}),"\n",(0,a.jsx)(i.pre,{children:(0,a.jsx)(i.code,{className:"language-bash",children:"# Debug with address sanitizer\r\ncforge build -c Debug --variant asan\r\n\r\n# Release with address sanitizer\r\ncforge build -c Release --variant asan\n"})})]})}function u(n={}){const{wrapper:i}={...(0,s.R)(),...n.components};return i?(0,a.jsx)(i,{...n,children:(0,a.jsx)(c,{...n})}):c(n)}},8453:(n,i,e)=>{e.d(i,{R:()=>t,x:()=>d});var r=e(6540);const a={},s=r.createContext(a);function t(n){const i=r.useContext(s);return r.useMemo((function(){return"function"==typeof n?n(i):{...i,...n}}),[i,n])}function d(n){let i;return i=n.disableParentContext?"function"==typeof n.components?n.components(a):n.components||a:t(n.components),r.createElement(s.Provider,{value:i},n.children)}}}]);
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[413],{9328:e=>{e.exports=JSON.parse('{"version":{"pluginId":"default","version":"current","label":"Next","banner":null,"badge":false,"noIndex":false,"className":"docs-version-current","isLast":true,"docsSidebars":{"sidebar":[{"type":"category","label":"CForge","collapsed":false,"items":[{"type":"link","label":"Introduction","href":"/cforge/docs/intro","docId":"intro","unlisted":false},{"type":"link","label":"Installation","href":"/cforge/docs/installation","docId":"installation","unlisted":false},{"type":"link","label":"Quick Start","href":"/cforge/docs/quick-start","docId":"quick-start","unlisted":false},{"type":"link","label":"Command Reference","href":"/cforge/docs/command-reference","docId":"command-reference","unlisted":false},{"type":"link","label":"Project Configuration","href":"/cforge/docs/project-configuration","docId":"project-configuration","unlisted":false},{"type":"link","label":"Working with Dependencies","href":"/cforge/docs/dependencies","docId":"dependencies","unlisted":false},{"type":"link","label":"Workspaces","href":"/cforge/docs/workspaces","docId":"workspaces","unlisted":false},{"type":"link","label":"Build Variants","href":"/cforge/docs/build-variants","docId":"build-variants","unlisted":false},{"type":"link","label":"Cross-Compilation","href":"/cforge/docs/cross-compilation","docId":"cross-compilation","unlisted":false},{"type":"link","label":"IDE Integration","href":"/cforge/docs/ide-integration","docId":"ide-integration","unlisted":false},{"type":"link","label":"Scripts & Hooks","href":"/cforge/docs/scripts-hooks","docId":"scripts-hooks","unlisted":false},{"type":"link","label":"Testing","href":"/cforge/docs/testing","docId":"testing","unlisted":false},{"type":"link","label":"Advanced Topics","href":"/cforge/docs/advanced-topics","docId":"advanced-topics","unlisted":false},{"type":"link","label":"Examples","href":"/cforge/docs/examples","docId":"examples","unlisted":false},{"type":"link","label":"Troubleshooting","href":"/cforge/docs/troubleshooting","docId":"troubleshooting","unlisted":false},{"type":"link","label":"Contributing","href":"/cforge/docs/contributing","docId":"contributing","unlisted":false},{"type":"link","label":"License","href":"/cforge/docs/license","docId":"license","unlisted":false}],"collapsible":true}]},"docs":{"advanced-topics":{"id":"advanced-topics","title":"Advanced Topics","description":"\ud83e\udde9 Advanced Topics","sidebar":"sidebar"},"build-variants":{"id":"build-variants","title":"Build Variants","description":"Build Variants","sidebar":"sidebar"},"command-reference":{"id":"command-reference","title":"Command Reference","description":"CForge provides a comprehensive set of commands for building, testing, and managing C/C++ projects.","sidebar":"sidebar"},"contributing":{"id":"contributing","title":"Contributing","description":"\ud83e\udd1d Contributing","sidebar":"sidebar"},"cross-compilation":{"id":"cross-compilation","title":"Cross-Compilation","description":"Cross-Compilation","sidebar":"sidebar"},"dependencies":{"id":"dependencies","title":"Working with Dependencies","description":"Working with Dependencies","sidebar":"sidebar"},"examples":{"id":"examples","title":"Examples","description":"\ud83d\udcda Examples","sidebar":"sidebar"},"ide-integration":{"id":"ide-integration","title":"IDE Integration","description":"\ud83d\udda5\ufe0f IDE Integration","sidebar":"sidebar"},"installation":{"id":"installation","title":"Installation","description":"Install CForge on your system.","sidebar":"sidebar"},"intro":{"id":"intro","title":"Introduction","description":"Version","sidebar":"sidebar"},"license":{"id":"license","title":"License","description":"\ud83d\udcc4 License","sidebar":"sidebar"},"project-configuration":{"id":"project-configuration","title":"Project Configuration","description":"Project Configuration","sidebar":"sidebar"},"quick-start":{"id":"quick-start","title":"Quick Start","description":"Get up and running with CForge in minutes.","sidebar":"sidebar"},"scripts-hooks":{"id":"scripts-hooks","title":"Scripts & Hooks","description":"\ud83d\udcdd Scripts & Hooks","sidebar":"sidebar"},"testing":{"id":"testing","title":"Testing","description":"\ud83e\uddea Testing","sidebar":"sidebar"},"troubleshooting":{"id":"troubleshooting","title":"Troubleshooting","description":"Common issues and their solutions.","sidebar":"sidebar"},"workspaces":{"id":"workspaces","title":"Workspaces","description":"Workspaces","sidebar":"sidebar"}}}}')}}]);
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -5,7 +5,7 @@
<meta name="generator" content="Docusaurus v3.7.0">
<title data-rh="true">Announcing CForge Beta-v1.4.0 | CForge</title><meta data-rh="true" name="viewport" content="width=device-width,initial-scale=1"><meta data-rh="true" name="twitter:card" content="summary_large_image"><meta data-rh="true" property="og:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" name="twitter:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" property="og:url" content="https://chasesunstrom.github.io/cforge/blog/announcing-cforge-beta-v1-4-0"><meta data-rh="true" property="og:locale" content="en"><meta data-rh="true" name="docusaurus_locale" content="en"><meta data-rh="true" name="docusaurus_tag" content="default"><meta data-rh="true" name="docsearch:language" content="en"><meta data-rh="true" name="docsearch:docusaurus_tag" content="default"><meta data-rh="true" property="og:title" content="Announcing CForge Beta-v1.4.0 | CForge"><meta data-rh="true" name="description" content="Today we&#x27;re excited to announce the beta release of CForge, a TOML-based build system for C/C++ projects!"><meta data-rh="true" property="og:description" content="Today we&#x27;re excited to announce the beta release of CForge, a TOML-based build system for C/C++ projects!"><meta data-rh="true" property="og:type" content="article"><meta data-rh="true" property="article:published_time" content="2025-03-25T00:00:00.000Z"><meta data-rh="true" property="article:author" content="https://github.com/ChaseSunstrom"><meta data-rh="true" property="article:tag" content="cforge,release,beta"><link data-rh="true" rel="icon" href="/cforge/img/logo.svg"><link data-rh="true" rel="canonical" href="https://chasesunstrom.github.io/cforge/blog/announcing-cforge-beta-v1-4-0"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/blog/announcing-cforge-beta-v1-4-0" hreflang="en"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/blog/announcing-cforge-beta-v1-4-0" hreflang="x-default"><script data-rh="true" type="application/ld+json">{"@context":"https://schema.org","@type":"BlogPosting","@id":"https://chasesunstrom.github.io/cforge/blog/announcing-cforge-beta-v1-4-0","mainEntityOfPage":"https://chasesunstrom.github.io/cforge/blog/announcing-cforge-beta-v1-4-0","url":"https://chasesunstrom.github.io/cforge/blog/announcing-cforge-beta-v1-4-0","headline":"Announcing CForge Beta-v1.4.0","name":"Announcing CForge Beta-v1.4.0","description":"Today we're excited to announce the beta release of CForge, a TOML-based build system for C/C++ projects!","datePublished":"2025-03-25T00:00:00.000Z","author":{"@type":"Person","name":"Chase Sunstrom","description":"CForge Creator","url":"https://github.com/ChaseSunstrom","image":"https://github.com/ChaseSunstrom.png"},"keywords":[],"isPartOf":{"@type":"Blog","@id":"https://chasesunstrom.github.io/cforge/blog","name":"Blog"}}</script><link rel="alternate" type="application/rss+xml" href="/cforge/blog/rss.xml" title="CForge RSS Feed">
<link rel="alternate" type="application/atom+xml" href="/cforge/blog/atom.xml" title="CForge Atom Feed"><link rel="stylesheet" href="/cforge/assets/css/styles.5a56e4b0.css">
<script src="/cforge/assets/js/runtime~main.76be665e.js" defer="defer"></script>
<script src="/cforge/assets/js/runtime~main.6fa7163a.js" defer="defer"></script>
<script src="/cforge/assets/js/main.50450276.js" defer="defer"></script>
</head>
<body class="navigation-with-keyboard">
+1 -1
View File
@@ -5,7 +5,7 @@
<meta name="generator" content="Docusaurus v3.7.0">
<title data-rh="true">Archive | CForge</title><meta data-rh="true" name="viewport" content="width=device-width,initial-scale=1"><meta data-rh="true" name="twitter:card" content="summary_large_image"><meta data-rh="true" property="og:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" name="twitter:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" property="og:url" content="https://chasesunstrom.github.io/cforge/blog/archive"><meta data-rh="true" property="og:locale" content="en"><meta data-rh="true" name="docusaurus_locale" content="en"><meta data-rh="true" name="docusaurus_tag" content="default"><meta data-rh="true" name="docsearch:language" content="en"><meta data-rh="true" name="docsearch:docusaurus_tag" content="default"><meta data-rh="true" property="og:title" content="Archive | CForge"><meta data-rh="true" name="description" content="Archive"><meta data-rh="true" property="og:description" content="Archive"><link data-rh="true" rel="icon" href="/cforge/img/logo.svg"><link data-rh="true" rel="canonical" href="https://chasesunstrom.github.io/cforge/blog/archive"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/blog/archive" hreflang="en"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/blog/archive" hreflang="x-default"><link rel="alternate" type="application/rss+xml" href="/cforge/blog/rss.xml" title="CForge RSS Feed">
<link rel="alternate" type="application/atom+xml" href="/cforge/blog/atom.xml" title="CForge Atom Feed"><link rel="stylesheet" href="/cforge/assets/css/styles.5a56e4b0.css">
<script src="/cforge/assets/js/runtime~main.76be665e.js" defer="defer"></script>
<script src="/cforge/assets/js/runtime~main.6fa7163a.js" defer="defer"></script>
<script src="/cforge/assets/js/main.50450276.js" defer="defer"></script>
</head>
<body class="navigation-with-keyboard">
+1 -1
View File
@@ -5,7 +5,7 @@
<meta name="generator" content="Docusaurus v3.7.0">
<title data-rh="true">Blog | CForge</title><meta data-rh="true" name="viewport" content="width=device-width,initial-scale=1"><meta data-rh="true" name="twitter:card" content="summary_large_image"><meta data-rh="true" property="og:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" name="twitter:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" property="og:url" content="https://chasesunstrom.github.io/cforge/blog"><meta data-rh="true" property="og:locale" content="en"><meta data-rh="true" name="docusaurus_locale" content="en"><meta data-rh="true" name="docsearch:language" content="en"><meta data-rh="true" property="og:title" content="Blog | CForge"><meta data-rh="true" name="description" content="Blog"><meta data-rh="true" property="og:description" content="Blog"><meta data-rh="true" name="docusaurus_tag" content="blog_posts_list"><meta data-rh="true" name="docsearch:docusaurus_tag" content="blog_posts_list"><link data-rh="true" rel="icon" href="/cforge/img/logo.svg"><link data-rh="true" rel="canonical" href="https://chasesunstrom.github.io/cforge/blog"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/blog" hreflang="en"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/blog" hreflang="x-default"><script data-rh="true" type="application/ld+json">{"@context":"https://schema.org","@type":"Blog","@id":"https://chasesunstrom.github.io/cforge/blog","mainEntityOfPage":"https://chasesunstrom.github.io/cforge/blog","headline":"Blog","description":"Blog","blogPost":[{"@type":"BlogPosting","@id":"https://chasesunstrom.github.io/cforge/blog/announcing-cforge-beta-v1-4-0","mainEntityOfPage":"https://chasesunstrom.github.io/cforge/blog/announcing-cforge-beta-v1-4-0","url":"https://chasesunstrom.github.io/cforge/blog/announcing-cforge-beta-v1-4-0","headline":"Announcing CForge Beta-v1.4.0","name":"Announcing CForge Beta-v1.4.0","description":"Today we're excited to announce the beta release of CForge, a TOML-based build system for C/C++ projects!","datePublished":"2025-03-25T00:00:00.000Z","author":{"@type":"Person","name":"Chase Sunstrom","description":"CForge Creator","url":"https://github.com/ChaseSunstrom","image":"https://github.com/ChaseSunstrom.png"},"keywords":[]}]}</script><link rel="alternate" type="application/rss+xml" href="/cforge/blog/rss.xml" title="CForge RSS Feed">
<link rel="alternate" type="application/atom+xml" href="/cforge/blog/atom.xml" title="CForge Atom Feed"><link rel="stylesheet" href="/cforge/assets/css/styles.5a56e4b0.css">
<script src="/cforge/assets/js/runtime~main.76be665e.js" defer="defer"></script>
<script src="/cforge/assets/js/runtime~main.6fa7163a.js" defer="defer"></script>
<script src="/cforge/assets/js/main.50450276.js" defer="defer"></script>
</head>
<body class="navigation-with-keyboard">
+1 -1
View File
@@ -5,7 +5,7 @@
<meta name="generator" content="Docusaurus v3.7.0">
<title data-rh="true">One post tagged with &quot;beta&quot; | CForge</title><meta data-rh="true" name="viewport" content="width=device-width,initial-scale=1"><meta data-rh="true" name="twitter:card" content="summary_large_image"><meta data-rh="true" property="og:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" name="twitter:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" property="og:url" content="https://chasesunstrom.github.io/cforge/blog/tags/beta"><meta data-rh="true" property="og:locale" content="en"><meta data-rh="true" name="docusaurus_locale" content="en"><meta data-rh="true" name="docsearch:language" content="en"><meta data-rh="true" property="og:title" content="One post tagged with &quot;beta&quot; | CForge"><meta data-rh="true" name="docusaurus_tag" content="blog_tags_posts"><meta data-rh="true" name="docsearch:docusaurus_tag" content="blog_tags_posts"><link data-rh="true" rel="icon" href="/cforge/img/logo.svg"><link data-rh="true" rel="canonical" href="https://chasesunstrom.github.io/cforge/blog/tags/beta"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/blog/tags/beta" hreflang="en"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/blog/tags/beta" hreflang="x-default"><link rel="alternate" type="application/rss+xml" href="/cforge/blog/rss.xml" title="CForge RSS Feed">
<link rel="alternate" type="application/atom+xml" href="/cforge/blog/atom.xml" title="CForge Atom Feed"><link rel="stylesheet" href="/cforge/assets/css/styles.5a56e4b0.css">
<script src="/cforge/assets/js/runtime~main.76be665e.js" defer="defer"></script>
<script src="/cforge/assets/js/runtime~main.6fa7163a.js" defer="defer"></script>
<script src="/cforge/assets/js/main.50450276.js" defer="defer"></script>
</head>
<body class="navigation-with-keyboard">
+1 -1
View File
@@ -5,7 +5,7 @@
<meta name="generator" content="Docusaurus v3.7.0">
<title data-rh="true">Tags | CForge</title><meta data-rh="true" name="viewport" content="width=device-width,initial-scale=1"><meta data-rh="true" name="twitter:card" content="summary_large_image"><meta data-rh="true" property="og:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" name="twitter:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" property="og:url" content="https://chasesunstrom.github.io/cforge/blog/tags"><meta data-rh="true" property="og:locale" content="en"><meta data-rh="true" name="docusaurus_locale" content="en"><meta data-rh="true" name="docsearch:language" content="en"><meta data-rh="true" property="og:title" content="Tags | CForge"><meta data-rh="true" name="docusaurus_tag" content="blog_tags_list"><meta data-rh="true" name="docsearch:docusaurus_tag" content="blog_tags_list"><link data-rh="true" rel="icon" href="/cforge/img/logo.svg"><link data-rh="true" rel="canonical" href="https://chasesunstrom.github.io/cforge/blog/tags"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/blog/tags" hreflang="en"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/blog/tags" hreflang="x-default"><link rel="alternate" type="application/rss+xml" href="/cforge/blog/rss.xml" title="CForge RSS Feed">
<link rel="alternate" type="application/atom+xml" href="/cforge/blog/atom.xml" title="CForge Atom Feed"><link rel="stylesheet" href="/cforge/assets/css/styles.5a56e4b0.css">
<script src="/cforge/assets/js/runtime~main.76be665e.js" defer="defer"></script>
<script src="/cforge/assets/js/runtime~main.6fa7163a.js" defer="defer"></script>
<script src="/cforge/assets/js/main.50450276.js" defer="defer"></script>
</head>
<body class="navigation-with-keyboard">
+1 -1
View File
@@ -5,7 +5,7 @@
<meta name="generator" content="Docusaurus v3.7.0">
<title data-rh="true">One post tagged with &quot;release&quot; | CForge</title><meta data-rh="true" name="viewport" content="width=device-width,initial-scale=1"><meta data-rh="true" name="twitter:card" content="summary_large_image"><meta data-rh="true" property="og:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" name="twitter:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" property="og:url" content="https://chasesunstrom.github.io/cforge/blog/tags/release"><meta data-rh="true" property="og:locale" content="en"><meta data-rh="true" name="docusaurus_locale" content="en"><meta data-rh="true" name="docsearch:language" content="en"><meta data-rh="true" property="og:title" content="One post tagged with &quot;release&quot; | CForge"><meta data-rh="true" name="docusaurus_tag" content="blog_tags_posts"><meta data-rh="true" name="docsearch:docusaurus_tag" content="blog_tags_posts"><link data-rh="true" rel="icon" href="/cforge/img/logo.svg"><link data-rh="true" rel="canonical" href="https://chasesunstrom.github.io/cforge/blog/tags/release"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/blog/tags/release" hreflang="en"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/blog/tags/release" hreflang="x-default"><link rel="alternate" type="application/rss+xml" href="/cforge/blog/rss.xml" title="CForge RSS Feed">
<link rel="alternate" type="application/atom+xml" href="/cforge/blog/atom.xml" title="CForge Atom Feed"><link rel="stylesheet" href="/cforge/assets/css/styles.5a56e4b0.css">
<script src="/cforge/assets/js/runtime~main.76be665e.js" defer="defer"></script>
<script src="/cforge/assets/js/runtime~main.6fa7163a.js" defer="defer"></script>
<script src="/cforge/assets/js/main.50450276.js" defer="defer"></script>
</head>
<body class="navigation-with-keyboard">
+1 -1
View File
@@ -5,7 +5,7 @@
<meta name="generator" content="Docusaurus v3.7.0">
<title data-rh="true">Advanced Topics | CForge</title><meta data-rh="true" name="viewport" content="width=device-width,initial-scale=1"><meta data-rh="true" name="twitter:card" content="summary_large_image"><meta data-rh="true" property="og:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" name="twitter:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" property="og:url" content="https://chasesunstrom.github.io/cforge/docs/advanced-topics"><meta data-rh="true" property="og:locale" content="en"><meta data-rh="true" name="docusaurus_locale" content="en"><meta data-rh="true" name="docsearch:language" content="en"><meta data-rh="true" name="docusaurus_version" content="current"><meta data-rh="true" name="docusaurus_tag" content="docs-default-current"><meta data-rh="true" name="docsearch:version" content="current"><meta data-rh="true" name="docsearch:docusaurus_tag" content="docs-default-current"><meta data-rh="true" property="og:title" content="Advanced Topics | CForge"><meta data-rh="true" name="description" content="🧩 Advanced Topics"><meta data-rh="true" property="og:description" content="🧩 Advanced Topics"><link data-rh="true" rel="icon" href="/cforge/img/logo.svg"><link data-rh="true" rel="canonical" href="https://chasesunstrom.github.io/cforge/docs/advanced-topics"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/docs/advanced-topics" hreflang="en"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/docs/advanced-topics" hreflang="x-default"><link rel="alternate" type="application/rss+xml" href="/cforge/blog/rss.xml" title="CForge RSS Feed">
<link rel="alternate" type="application/atom+xml" href="/cforge/blog/atom.xml" title="CForge Atom Feed"><link rel="stylesheet" href="/cforge/assets/css/styles.5a56e4b0.css">
<script src="/cforge/assets/js/runtime~main.76be665e.js" defer="defer"></script>
<script src="/cforge/assets/js/runtime~main.6fa7163a.js" defer="defer"></script>
<script src="/cforge/assets/js/main.50450276.js" defer="defer"></script>
</head>
<body class="navigation-with-keyboard">
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -5,7 +5,7 @@
<meta name="generator" content="Docusaurus v3.7.0">
<title data-rh="true">Contributing | CForge</title><meta data-rh="true" name="viewport" content="width=device-width,initial-scale=1"><meta data-rh="true" name="twitter:card" content="summary_large_image"><meta data-rh="true" property="og:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" name="twitter:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" property="og:url" content="https://chasesunstrom.github.io/cforge/docs/contributing"><meta data-rh="true" property="og:locale" content="en"><meta data-rh="true" name="docusaurus_locale" content="en"><meta data-rh="true" name="docsearch:language" content="en"><meta data-rh="true" name="docusaurus_version" content="current"><meta data-rh="true" name="docusaurus_tag" content="docs-default-current"><meta data-rh="true" name="docsearch:version" content="current"><meta data-rh="true" name="docsearch:docusaurus_tag" content="docs-default-current"><meta data-rh="true" property="og:title" content="Contributing | CForge"><meta data-rh="true" name="description" content="🤝 Contributing"><meta data-rh="true" property="og:description" content="🤝 Contributing"><link data-rh="true" rel="icon" href="/cforge/img/logo.svg"><link data-rh="true" rel="canonical" href="https://chasesunstrom.github.io/cforge/docs/contributing"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/docs/contributing" hreflang="en"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/docs/contributing" hreflang="x-default"><link rel="alternate" type="application/rss+xml" href="/cforge/blog/rss.xml" title="CForge RSS Feed">
<link rel="alternate" type="application/atom+xml" href="/cforge/blog/atom.xml" title="CForge Atom Feed"><link rel="stylesheet" href="/cforge/assets/css/styles.5a56e4b0.css">
<script src="/cforge/assets/js/runtime~main.76be665e.js" defer="defer"></script>
<script src="/cforge/assets/js/runtime~main.6fa7163a.js" defer="defer"></script>
<script src="/cforge/assets/js/main.50450276.js" defer="defer"></script>
</head>
<body class="navigation-with-keyboard">
+1 -1
View File
@@ -5,7 +5,7 @@
<meta name="generator" content="Docusaurus v3.7.0">
<title data-rh="true">Cross-Compilation | CForge</title><meta data-rh="true" name="viewport" content="width=device-width,initial-scale=1"><meta data-rh="true" name="twitter:card" content="summary_large_image"><meta data-rh="true" property="og:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" name="twitter:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" property="og:url" content="https://chasesunstrom.github.io/cforge/docs/cross-compilation"><meta data-rh="true" property="og:locale" content="en"><meta data-rh="true" name="docusaurus_locale" content="en"><meta data-rh="true" name="docsearch:language" content="en"><meta data-rh="true" name="docusaurus_version" content="current"><meta data-rh="true" name="docusaurus_tag" content="docs-default-current"><meta data-rh="true" name="docsearch:version" content="current"><meta data-rh="true" name="docsearch:docusaurus_tag" content="docs-default-current"><meta data-rh="true" property="og:title" content="Cross-Compilation | CForge"><meta data-rh="true" name="description" content="Cross-Compilation"><meta data-rh="true" property="og:description" content="Cross-Compilation"><link data-rh="true" rel="icon" href="/cforge/img/logo.svg"><link data-rh="true" rel="canonical" href="https://chasesunstrom.github.io/cforge/docs/cross-compilation"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/docs/cross-compilation" hreflang="en"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/docs/cross-compilation" hreflang="x-default"><link rel="alternate" type="application/rss+xml" href="/cforge/blog/rss.xml" title="CForge RSS Feed">
<link rel="alternate" type="application/atom+xml" href="/cforge/blog/atom.xml" title="CForge Atom Feed"><link rel="stylesheet" href="/cforge/assets/css/styles.5a56e4b0.css">
<script src="/cforge/assets/js/runtime~main.76be665e.js" defer="defer"></script>
<script src="/cforge/assets/js/runtime~main.6fa7163a.js" defer="defer"></script>
<script src="/cforge/assets/js/main.50450276.js" defer="defer"></script>
</head>
<body class="navigation-with-keyboard">
+1 -1
View File
@@ -5,7 +5,7 @@
<meta name="generator" content="Docusaurus v3.7.0">
<title data-rh="true">Working with Dependencies | CForge</title><meta data-rh="true" name="viewport" content="width=device-width,initial-scale=1"><meta data-rh="true" name="twitter:card" content="summary_large_image"><meta data-rh="true" property="og:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" name="twitter:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" property="og:url" content="https://chasesunstrom.github.io/cforge/docs/dependencies"><meta data-rh="true" property="og:locale" content="en"><meta data-rh="true" name="docusaurus_locale" content="en"><meta data-rh="true" name="docsearch:language" content="en"><meta data-rh="true" name="docusaurus_version" content="current"><meta data-rh="true" name="docusaurus_tag" content="docs-default-current"><meta data-rh="true" name="docsearch:version" content="current"><meta data-rh="true" name="docsearch:docusaurus_tag" content="docs-default-current"><meta data-rh="true" property="og:title" content="Working with Dependencies | CForge"><meta data-rh="true" name="description" content="Working with Dependencies"><meta data-rh="true" property="og:description" content="Working with Dependencies"><link data-rh="true" rel="icon" href="/cforge/img/logo.svg"><link data-rh="true" rel="canonical" href="https://chasesunstrom.github.io/cforge/docs/dependencies"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/docs/dependencies" hreflang="en"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/docs/dependencies" hreflang="x-default"><link rel="alternate" type="application/rss+xml" href="/cforge/blog/rss.xml" title="CForge RSS Feed">
<link rel="alternate" type="application/atom+xml" href="/cforge/blog/atom.xml" title="CForge Atom Feed"><link rel="stylesheet" href="/cforge/assets/css/styles.5a56e4b0.css">
<script src="/cforge/assets/js/runtime~main.76be665e.js" defer="defer"></script>
<script src="/cforge/assets/js/runtime~main.6fa7163a.js" defer="defer"></script>
<script src="/cforge/assets/js/main.50450276.js" defer="defer"></script>
</head>
<body class="navigation-with-keyboard">
+1 -1
View File
@@ -5,7 +5,7 @@
<meta name="generator" content="Docusaurus v3.7.0">
<title data-rh="true">Examples | CForge</title><meta data-rh="true" name="viewport" content="width=device-width,initial-scale=1"><meta data-rh="true" name="twitter:card" content="summary_large_image"><meta data-rh="true" property="og:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" name="twitter:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" property="og:url" content="https://chasesunstrom.github.io/cforge/docs/examples"><meta data-rh="true" property="og:locale" content="en"><meta data-rh="true" name="docusaurus_locale" content="en"><meta data-rh="true" name="docsearch:language" content="en"><meta data-rh="true" name="docusaurus_version" content="current"><meta data-rh="true" name="docusaurus_tag" content="docs-default-current"><meta data-rh="true" name="docsearch:version" content="current"><meta data-rh="true" name="docsearch:docusaurus_tag" content="docs-default-current"><meta data-rh="true" property="og:title" content="Examples | CForge"><meta data-rh="true" name="description" content="📚 Examples"><meta data-rh="true" property="og:description" content="📚 Examples"><link data-rh="true" rel="icon" href="/cforge/img/logo.svg"><link data-rh="true" rel="canonical" href="https://chasesunstrom.github.io/cforge/docs/examples"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/docs/examples" hreflang="en"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/docs/examples" hreflang="x-default"><link rel="alternate" type="application/rss+xml" href="/cforge/blog/rss.xml" title="CForge RSS Feed">
<link rel="alternate" type="application/atom+xml" href="/cforge/blog/atom.xml" title="CForge Atom Feed"><link rel="stylesheet" href="/cforge/assets/css/styles.5a56e4b0.css">
<script src="/cforge/assets/js/runtime~main.76be665e.js" defer="defer"></script>
<script src="/cforge/assets/js/runtime~main.6fa7163a.js" defer="defer"></script>
<script src="/cforge/assets/js/main.50450276.js" defer="defer"></script>
</head>
<body class="navigation-with-keyboard">
+1 -1
View File
@@ -5,7 +5,7 @@
<meta name="generator" content="Docusaurus v3.7.0">
<title data-rh="true">IDE Integration | CForge</title><meta data-rh="true" name="viewport" content="width=device-width,initial-scale=1"><meta data-rh="true" name="twitter:card" content="summary_large_image"><meta data-rh="true" property="og:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" name="twitter:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" property="og:url" content="https://chasesunstrom.github.io/cforge/docs/ide-integration"><meta data-rh="true" property="og:locale" content="en"><meta data-rh="true" name="docusaurus_locale" content="en"><meta data-rh="true" name="docsearch:language" content="en"><meta data-rh="true" name="docusaurus_version" content="current"><meta data-rh="true" name="docusaurus_tag" content="docs-default-current"><meta data-rh="true" name="docsearch:version" content="current"><meta data-rh="true" name="docsearch:docusaurus_tag" content="docs-default-current"><meta data-rh="true" property="og:title" content="IDE Integration | CForge"><meta data-rh="true" name="description" content="🖥️ IDE Integration"><meta data-rh="true" property="og:description" content="🖥️ IDE Integration"><link data-rh="true" rel="icon" href="/cforge/img/logo.svg"><link data-rh="true" rel="canonical" href="https://chasesunstrom.github.io/cforge/docs/ide-integration"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/docs/ide-integration" hreflang="en"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/docs/ide-integration" hreflang="x-default"><link rel="alternate" type="application/rss+xml" href="/cforge/blog/rss.xml" title="CForge RSS Feed">
<link rel="alternate" type="application/atom+xml" href="/cforge/blog/atom.xml" title="CForge Atom Feed"><link rel="stylesheet" href="/cforge/assets/css/styles.5a56e4b0.css">
<script src="/cforge/assets/js/runtime~main.76be665e.js" defer="defer"></script>
<script src="/cforge/assets/js/runtime~main.6fa7163a.js" defer="defer"></script>
<script src="/cforge/assets/js/main.50450276.js" defer="defer"></script>
</head>
<body class="navigation-with-keyboard">
+2 -2
View File
@@ -5,7 +5,7 @@
<meta name="generator" content="Docusaurus v3.7.0">
<title data-rh="true">Installation | CForge</title><meta data-rh="true" name="viewport" content="width=device-width,initial-scale=1"><meta data-rh="true" name="twitter:card" content="summary_large_image"><meta data-rh="true" property="og:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" name="twitter:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" property="og:url" content="https://chasesunstrom.github.io/cforge/docs/installation"><meta data-rh="true" property="og:locale" content="en"><meta data-rh="true" name="docusaurus_locale" content="en"><meta data-rh="true" name="docsearch:language" content="en"><meta data-rh="true" name="docusaurus_version" content="current"><meta data-rh="true" name="docusaurus_tag" content="docs-default-current"><meta data-rh="true" name="docsearch:version" content="current"><meta data-rh="true" name="docsearch:docusaurus_tag" content="docs-default-current"><meta data-rh="true" property="og:title" content="Installation | CForge"><meta data-rh="true" name="description" content="Install CForge on your system."><meta data-rh="true" property="og:description" content="Install CForge on your system."><link data-rh="true" rel="icon" href="/cforge/img/logo.svg"><link data-rh="true" rel="canonical" href="https://chasesunstrom.github.io/cforge/docs/installation"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/docs/installation" hreflang="en"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/docs/installation" hreflang="x-default"><link rel="alternate" type="application/rss+xml" href="/cforge/blog/rss.xml" title="CForge RSS Feed">
<link rel="alternate" type="application/atom+xml" href="/cforge/blog/atom.xml" title="CForge Atom Feed"><link rel="stylesheet" href="/cforge/assets/css/styles.5a56e4b0.css">
<script src="/cforge/assets/js/runtime~main.76be665e.js" defer="defer"></script>
<script src="/cforge/assets/js/runtime~main.6fa7163a.js" defer="defer"></script>
<script src="/cforge/assets/js/main.50450276.js" defer="defer"></script>
</head>
<body class="navigation-with-keyboard">
@@ -48,7 +48,7 @@
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="verify-installation">Verify Installation<a href="#verify-installation" class="hash-link" aria-label="Direct link to Verify Installation" title="Direct link to Verify Installation"></a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">cforge version</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Expected output:</p>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">cforge 1.5.0</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">cforge 3.0.1</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="shell-completions">Shell Completions<a href="#shell-completions" class="hash-link" aria-label="Direct link to Shell Completions" title="Direct link to Shell Completions"></a></h2>
<p>Enable tab completion for your shell:</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="bash">Bash<a href="#bash" class="hash-link" aria-label="Direct link to Bash" title="Direct link to Bash"></a></h3>
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -5,7 +5,7 @@
<meta name="generator" content="Docusaurus v3.7.0">
<title data-rh="true">License | CForge</title><meta data-rh="true" name="viewport" content="width=device-width,initial-scale=1"><meta data-rh="true" name="twitter:card" content="summary_large_image"><meta data-rh="true" property="og:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" name="twitter:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" property="og:url" content="https://chasesunstrom.github.io/cforge/docs/license"><meta data-rh="true" property="og:locale" content="en"><meta data-rh="true" name="docusaurus_locale" content="en"><meta data-rh="true" name="docsearch:language" content="en"><meta data-rh="true" name="docusaurus_version" content="current"><meta data-rh="true" name="docusaurus_tag" content="docs-default-current"><meta data-rh="true" name="docsearch:version" content="current"><meta data-rh="true" name="docsearch:docusaurus_tag" content="docs-default-current"><meta data-rh="true" property="og:title" content="License | CForge"><meta data-rh="true" name="description" content="📄 License"><meta data-rh="true" property="og:description" content="📄 License"><link data-rh="true" rel="icon" href="/cforge/img/logo.svg"><link data-rh="true" rel="canonical" href="https://chasesunstrom.github.io/cforge/docs/license"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/docs/license" hreflang="en"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/docs/license" hreflang="x-default"><link rel="alternate" type="application/rss+xml" href="/cforge/blog/rss.xml" title="CForge RSS Feed">
<link rel="alternate" type="application/atom+xml" href="/cforge/blog/atom.xml" title="CForge Atom Feed"><link rel="stylesheet" href="/cforge/assets/css/styles.5a56e4b0.css">
<script src="/cforge/assets/js/runtime~main.76be665e.js" defer="defer"></script>
<script src="/cforge/assets/js/runtime~main.6fa7163a.js" defer="defer"></script>
<script src="/cforge/assets/js/main.50450276.js" defer="defer"></script>
</head>
<body class="navigation-with-keyboard">
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -5,7 +5,7 @@
<meta name="generator" content="Docusaurus v3.7.0">
<title data-rh="true">Scripts &amp; Hooks | CForge</title><meta data-rh="true" name="viewport" content="width=device-width,initial-scale=1"><meta data-rh="true" name="twitter:card" content="summary_large_image"><meta data-rh="true" property="og:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" name="twitter:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" property="og:url" content="https://chasesunstrom.github.io/cforge/docs/scripts-hooks"><meta data-rh="true" property="og:locale" content="en"><meta data-rh="true" name="docusaurus_locale" content="en"><meta data-rh="true" name="docsearch:language" content="en"><meta data-rh="true" name="docusaurus_version" content="current"><meta data-rh="true" name="docusaurus_tag" content="docs-default-current"><meta data-rh="true" name="docsearch:version" content="current"><meta data-rh="true" name="docsearch:docusaurus_tag" content="docs-default-current"><meta data-rh="true" property="og:title" content="Scripts &amp; Hooks | CForge"><meta data-rh="true" name="description" content="📝 Scripts &amp; Hooks"><meta data-rh="true" property="og:description" content="📝 Scripts &amp; Hooks"><link data-rh="true" rel="icon" href="/cforge/img/logo.svg"><link data-rh="true" rel="canonical" href="https://chasesunstrom.github.io/cforge/docs/scripts-hooks"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/docs/scripts-hooks" hreflang="en"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/docs/scripts-hooks" hreflang="x-default"><link rel="alternate" type="application/rss+xml" href="/cforge/blog/rss.xml" title="CForge RSS Feed">
<link rel="alternate" type="application/atom+xml" href="/cforge/blog/atom.xml" title="CForge Atom Feed"><link rel="stylesheet" href="/cforge/assets/css/styles.5a56e4b0.css">
<script src="/cforge/assets/js/runtime~main.76be665e.js" defer="defer"></script>
<script src="/cforge/assets/js/runtime~main.6fa7163a.js" defer="defer"></script>
<script src="/cforge/assets/js/main.50450276.js" defer="defer"></script>
</head>
<body class="navigation-with-keyboard">
+1 -1
View File
@@ -5,7 +5,7 @@
<meta name="generator" content="Docusaurus v3.7.0">
<title data-rh="true">Testing | CForge</title><meta data-rh="true" name="viewport" content="width=device-width,initial-scale=1"><meta data-rh="true" name="twitter:card" content="summary_large_image"><meta data-rh="true" property="og:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" name="twitter:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" property="og:url" content="https://chasesunstrom.github.io/cforge/docs/testing"><meta data-rh="true" property="og:locale" content="en"><meta data-rh="true" name="docusaurus_locale" content="en"><meta data-rh="true" name="docsearch:language" content="en"><meta data-rh="true" name="docusaurus_version" content="current"><meta data-rh="true" name="docusaurus_tag" content="docs-default-current"><meta data-rh="true" name="docsearch:version" content="current"><meta data-rh="true" name="docsearch:docusaurus_tag" content="docs-default-current"><meta data-rh="true" property="og:title" content="Testing | CForge"><meta data-rh="true" name="description" content="🧪 Testing"><meta data-rh="true" property="og:description" content="🧪 Testing"><link data-rh="true" rel="icon" href="/cforge/img/logo.svg"><link data-rh="true" rel="canonical" href="https://chasesunstrom.github.io/cforge/docs/testing"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/docs/testing" hreflang="en"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/docs/testing" hreflang="x-default"><link rel="alternate" type="application/rss+xml" href="/cforge/blog/rss.xml" title="CForge RSS Feed">
<link rel="alternate" type="application/atom+xml" href="/cforge/blog/atom.xml" title="CForge Atom Feed"><link rel="stylesheet" href="/cforge/assets/css/styles.5a56e4b0.css">
<script src="/cforge/assets/js/runtime~main.76be665e.js" defer="defer"></script>
<script src="/cforge/assets/js/runtime~main.6fa7163a.js" defer="defer"></script>
<script src="/cforge/assets/js/main.50450276.js" defer="defer"></script>
</head>
<body class="navigation-with-keyboard">
+1 -1
View File
@@ -5,7 +5,7 @@
<meta name="generator" content="Docusaurus v3.7.0">
<title data-rh="true">Troubleshooting | CForge</title><meta data-rh="true" name="viewport" content="width=device-width,initial-scale=1"><meta data-rh="true" name="twitter:card" content="summary_large_image"><meta data-rh="true" property="og:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" name="twitter:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" property="og:url" content="https://chasesunstrom.github.io/cforge/docs/troubleshooting"><meta data-rh="true" property="og:locale" content="en"><meta data-rh="true" name="docusaurus_locale" content="en"><meta data-rh="true" name="docsearch:language" content="en"><meta data-rh="true" name="docusaurus_version" content="current"><meta data-rh="true" name="docusaurus_tag" content="docs-default-current"><meta data-rh="true" name="docsearch:version" content="current"><meta data-rh="true" name="docsearch:docusaurus_tag" content="docs-default-current"><meta data-rh="true" property="og:title" content="Troubleshooting | CForge"><meta data-rh="true" name="description" content="Common issues and their solutions."><meta data-rh="true" property="og:description" content="Common issues and their solutions."><link data-rh="true" rel="icon" href="/cforge/img/logo.svg"><link data-rh="true" rel="canonical" href="https://chasesunstrom.github.io/cforge/docs/troubleshooting"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/docs/troubleshooting" hreflang="en"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/docs/troubleshooting" hreflang="x-default"><link rel="alternate" type="application/rss+xml" href="/cforge/blog/rss.xml" title="CForge RSS Feed">
<link rel="alternate" type="application/atom+xml" href="/cforge/blog/atom.xml" title="CForge Atom Feed"><link rel="stylesheet" href="/cforge/assets/css/styles.5a56e4b0.css">
<script src="/cforge/assets/js/runtime~main.76be665e.js" defer="defer"></script>
<script src="/cforge/assets/js/runtime~main.6fa7163a.js" defer="defer"></script>
<script src="/cforge/assets/js/main.50450276.js" defer="defer"></script>
</head>
<body class="navigation-with-keyboard">
+1 -1
View File
@@ -5,7 +5,7 @@
<meta name="generator" content="Docusaurus v3.7.0">
<title data-rh="true">Workspaces | CForge</title><meta data-rh="true" name="viewport" content="width=device-width,initial-scale=1"><meta data-rh="true" name="twitter:card" content="summary_large_image"><meta data-rh="true" property="og:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" name="twitter:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" property="og:url" content="https://chasesunstrom.github.io/cforge/docs/workspaces"><meta data-rh="true" property="og:locale" content="en"><meta data-rh="true" name="docusaurus_locale" content="en"><meta data-rh="true" name="docsearch:language" content="en"><meta data-rh="true" name="docusaurus_version" content="current"><meta data-rh="true" name="docusaurus_tag" content="docs-default-current"><meta data-rh="true" name="docsearch:version" content="current"><meta data-rh="true" name="docsearch:docusaurus_tag" content="docs-default-current"><meta data-rh="true" property="og:title" content="Workspaces | CForge"><meta data-rh="true" name="description" content="Workspaces"><meta data-rh="true" property="og:description" content="Workspaces"><link data-rh="true" rel="icon" href="/cforge/img/logo.svg"><link data-rh="true" rel="canonical" href="https://chasesunstrom.github.io/cforge/docs/workspaces"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/docs/workspaces" hreflang="en"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/docs/workspaces" hreflang="x-default"><link rel="alternate" type="application/rss+xml" href="/cforge/blog/rss.xml" title="CForge RSS Feed">
<link rel="alternate" type="application/atom+xml" href="/cforge/blog/atom.xml" title="CForge Atom Feed"><link rel="stylesheet" href="/cforge/assets/css/styles.5a56e4b0.css">
<script src="/cforge/assets/js/runtime~main.76be665e.js" defer="defer"></script>
<script src="/cforge/assets/js/runtime~main.6fa7163a.js" defer="defer"></script>
<script src="/cforge/assets/js/main.50450276.js" defer="defer"></script>
</head>
<body class="navigation-with-keyboard">
+1 -1
View File
@@ -5,7 +5,7 @@
<meta name="generator" content="Docusaurus v3.7.0">
<title data-rh="true">CForge - Modern C/C++ Build System | CForge</title><meta data-rh="true" name="viewport" content="width=device-width,initial-scale=1"><meta data-rh="true" name="twitter:card" content="summary_large_image"><meta data-rh="true" property="og:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" name="twitter:image" content="https://chasesunstrom.github.io/cforge/img/cforge.png"><meta data-rh="true" property="og:url" content="https://chasesunstrom.github.io/cforge/"><meta data-rh="true" property="og:locale" content="en"><meta data-rh="true" name="docusaurus_locale" content="en"><meta data-rh="true" name="docusaurus_tag" content="default"><meta data-rh="true" name="docsearch:language" content="en"><meta data-rh="true" name="docsearch:docusaurus_tag" content="default"><meta data-rh="true" property="og:title" content="CForge - Modern C/C++ Build System | CForge"><meta data-rh="true" name="description" content="A modern TOML-based build system for C/C++ with CMake &amp; vcpkg integration. Cargo-style output, dependency management, and developer tools."><meta data-rh="true" property="og:description" content="A modern TOML-based build system for C/C++ with CMake &amp; vcpkg integration. Cargo-style output, dependency management, and developer tools."><link data-rh="true" rel="icon" href="/cforge/img/logo.svg"><link data-rh="true" rel="canonical" href="https://chasesunstrom.github.io/cforge/"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/" hreflang="en"><link data-rh="true" rel="alternate" href="https://chasesunstrom.github.io/cforge/" hreflang="x-default"><script data-rh="true">function insertBanner(){var n=document.createElement("div");n.id="__docusaurus-base-url-issue-banner-container";n.innerHTML='\n<div id="__docusaurus-base-url-issue-banner" style="border: thick solid red; background-color: rgb(255, 230, 179); margin: 20px; padding: 20px; font-size: 20px;">\n <p style="font-weight: bold; font-size: 30px;">Your Docusaurus site did not load properly.</p>\n <p>A very common reason is a wrong site <a href="https://docusaurus.io/docs/docusaurus.config.js/#baseUrl" style="font-weight: bold;">baseUrl configuration</a>.</p>\n <p>Current configured baseUrl = <span style="font-weight: bold; color: red;">/cforge/</span> </p>\n <p>We suggest trying baseUrl = <span id="__docusaurus-base-url-issue-banner-suggestion-container" style="font-weight: bold; color: green;"></span></p>\n</div>\n',document.body.prepend(n);var e=document.getElementById("__docusaurus-base-url-issue-banner-suggestion-container"),s=window.location.pathname,o="/"===s.substr(-1)?s:s+"/";e.innerHTML=o}document.addEventListener("DOMContentLoaded",(function(){void 0===window.docusaurus&&insertBanner()}))</script><link rel="alternate" type="application/rss+xml" href="/cforge/blog/rss.xml" title="CForge RSS Feed">
<link rel="alternate" type="application/atom+xml" href="/cforge/blog/atom.xml" title="CForge Atom Feed"><link rel="stylesheet" href="/cforge/assets/css/styles.5a56e4b0.css">
<script src="/cforge/assets/js/runtime~main.76be665e.js" defer="defer"></script>
<script src="/cforge/assets/js/runtime~main.6fa7163a.js" defer="defer"></script>
<script src="/cforge/assets/js/main.50450276.js" defer="defer"></script>
</head>
<body class="navigation-with-keyboard">
+88 -171
View File
@@ -7,10 +7,13 @@
#include "cforge/log.hpp"
#include "core/constants.h"
#include "core/platform.hpp"
#include "core/process_utils.hpp"
#include "core/toml_reader.hpp"
#include "core/types.h"
#include <filesystem>
#include <fstream>
#include <string>
#include <vector>
@@ -37,13 +40,7 @@ inline bool is_multi_config_generator(const std::string &generator) {
* @param gen Generator name to check
* @return true if generator is supported by CMake
*/
inline bool is_generator_valid(const std::string &gen) {
process_result pr =
execute_process("cmake", {"--help"}, "", nullptr, nullptr, 10);
if (!pr.success)
return true; // Assume valid if we can't check
return pr.stdout_output.find(gen) != std::string::npos;
}
bool is_generator_valid(const std::string &gen);
/**
* @brief Get the appropriate CMake generator for the current platform
@@ -58,34 +55,7 @@ inline bool is_generator_valid(const std::string &gen) {
*
* @return CMake generator string
*/
inline std::string get_cmake_generator() {
#ifdef _WIN32
// Prefer Ninja Multi-Config if available and supported
if (is_command_available("ninja", 15) &&
is_generator_valid("Ninja Multi-Config")) {
logger::print_verbose("Using Ninja Multi-Config generator");
return "Ninja Multi-Config";
}
// Try Visual Studio 17 2022
if (is_generator_valid("Visual Studio 17 2022")) {
logger::print_verbose("Using Visual Studio 17 2022 generator");
return "Visual Studio 17 2022";
}
// Fallback to Visual Studio 16 2019 if available
if (is_generator_valid("Visual Studio 16 2019")) {
logger::print_verbose("Using Visual Studio 16 2019 generator");
return "Visual Studio 16 2019";
}
// Last resort: Ninja Multi-Config
logger::print_verbose("Falling back to Ninja Multi-Config generator");
return "Ninja Multi-Config";
#else
return "Unix Makefiles";
#endif
}
std::string get_cmake_generator();
/**
* @brief Get the build directory path for a given configuration
@@ -98,33 +68,9 @@ inline std::string get_cmake_generator() {
* @param create_if_missing Create directory if it doesn't exist
* @return Build directory path
*/
inline std::filesystem::path
std::filesystem::path
get_build_dir_for_config(const std::string &base_dir, const std::string &config,
bool create_if_missing = true) {
std::filesystem::path build_path;
// For multi-config generators, use single build directory
std::string generator = get_cmake_generator();
if (is_multi_config_generator(generator) || config.empty()) {
build_path = base_dir;
} else {
// Transform config name to lowercase for directory naming
std::string config_lower = string_to_lower(config);
build_path = base_dir + "-" + config_lower;
}
// Create directory if requested and doesn't exist
if (create_if_missing && !std::filesystem::exists(build_path)) {
try {
std::filesystem::create_directories(build_path);
} catch (const std::exception &e) {
logger::print_warning("Failed to create build directory: " +
std::string(e.what()));
}
}
return build_path;
}
bool create_if_missing = true);
/**
* @brief Get build configuration from various sources
@@ -141,53 +87,9 @@ get_build_dir_for_config(const std::string &base_dir, const std::string &config,
* @param project_config TOML reader for project config
* @return Build configuration string
*/
inline std::string get_build_config(const char *explicit_config, int arg_count,
char *const *args,
const toml_reader *project_config) {
// Priority 1: Direct configuration argument
if (explicit_config != nullptr && strlen(explicit_config) > 0) {
logger::print_verbose("Using build configuration from direct argument: " +
std::string(explicit_config));
return std::string(explicit_config);
}
// Priority 2: Command line argument
if (arg_count > 0 && args != nullptr) {
for (int i = 0; i < arg_count; ++i) {
if (args[i] == nullptr)
continue;
std::string arg = args[i];
if (arg == "--config" || arg == "-c") {
if (i + 1 < arg_count && args[i + 1] != nullptr) {
std::string config = args[i + 1];
logger::print_verbose(
"Using build configuration from command line: " + config);
return config;
}
} else if (arg.length() > 9 && arg.substr(0, 9) == "--config=") {
std::string config = arg.substr(9);
logger::print_verbose("Using build configuration from command line: " +
config);
return config;
}
}
}
// Priority 3: Configuration from cforge.toml
if (project_config != nullptr) {
std::string config = project_config->get_string("build.build_type", "");
if (!config.empty()) {
logger::print_verbose("Using build configuration from cforge.toml: " +
config);
return config;
}
}
// Priority 4: Default to Release
logger::print_verbose(
"No build configuration specified, defaulting to Release");
return "Release";
}
std::string get_build_config(const char *explicit_config, cforge_int_t arg_count,
char *const *args,
const toml_reader *project_config);
/**
* @brief Find the project output binary path
@@ -199,31 +101,10 @@ inline std::string get_build_config(const char *explicit_config, int arg_count,
* static_library)
* @return Path to the binary, or empty if not found
*/
inline std::filesystem::path
std::filesystem::path
find_project_binary(const std::filesystem::path &build_dir,
const std::string &project_name, const std::string &config,
const std::string &binary_type = "executable") {
(void)binary_type; // Reserved for future use
std::vector<std::filesystem::path> search_paths;
// Common output locations
search_paths.push_back(build_dir / config / project_name);
search_paths.push_back(build_dir / config /
(project_name + ".exe")); // Windows
search_paths.push_back(build_dir / "bin" / config / project_name);
search_paths.push_back(build_dir / "bin" / config /
(project_name + ".exe")); // Windows
search_paths.push_back(build_dir / project_name);
search_paths.push_back(build_dir / (project_name + ".exe")); // Windows
for (const auto &path : search_paths) {
if (std::filesystem::exists(path)) {
return path;
}
}
return {};
}
const std::string &binary_type = "executable");
/**
* @brief Ensure CMake is configured for a project
@@ -235,30 +116,10 @@ find_project_binary(const std::filesystem::path &build_dir,
* @param extra_args Additional CMake arguments
* @return true if configuration succeeded
*/
inline bool
ensure_cmake_configured(const std::filesystem::path &project_dir,
const std::filesystem::path &build_dir,
const std::string &config, bool verbose,
const std::vector<std::string> &extra_args = {}) {
std::vector<std::string> cmake_args = {"-B", build_dir.string(),
"-S", project_dir.string(),
"-G", get_cmake_generator()};
// Add config for multi-config generators
std::string generator = get_cmake_generator();
if (!is_multi_config_generator(generator)) {
cmake_args.push_back("-DCMAKE_BUILD_TYPE=" + config);
}
// Add any extra arguments
for (const auto &arg : extra_args) {
cmake_args.push_back(arg);
}
return execute_tool("cmake", cmake_args, project_dir.string(), "CMake",
verbose, 120);
}
bool ensure_cmake_configured(const std::filesystem::path &project_dir,
const std::filesystem::path &build_dir,
const std::string &config, bool verbose,
const std::vector<std::string> &extra_args = {});
/**
* @brief Run CMake build for a project
@@ -270,25 +131,81 @@ ensure_cmake_configured(const std::filesystem::path &project_dir,
* @param verbose Verbose output
* @return true if build succeeded
*/
inline bool run_cmake_build(const std::filesystem::path &build_dir,
const std::string &config,
const std::string &target = "", int num_jobs = 0,
bool verbose = false) {
bool run_cmake_build(const std::filesystem::path &build_dir,
const std::string &config,
const std::string &target = "", cforge_int_t num_jobs = 0,
bool verbose = false);
std::vector<std::string> build_args = {"--build", build_dir.string(),
"--config", config};
// =============================================================================
// Smart Rebuild Utilities
// =============================================================================
if (!target.empty()) {
build_args.push_back("--target");
build_args.push_back(target);
}
/**
* @brief Check if a file is newer than another file
*
* @param source Source file to check
* @param target Target file to compare against
* @return true if source is newer than target, or if target doesn't exist
*/
bool is_file_newer(const std::filesystem::path &source,
const std::filesystem::path &target);
if (num_jobs > 0) {
build_args.push_back("-j");
build_args.push_back(std::to_string(num_jobs));
}
/**
* @brief Check if CMakeLists.txt needs regeneration from cforge.toml
*
* @param project_dir Project directory containing cforge.toml
* @return true if CMakeLists.txt needs to be regenerated
*/
bool needs_cmakelists_regeneration(const std::filesystem::path &project_dir);
return execute_tool("cmake", build_args, "", "CMake Build", verbose, 600);
}
/**
* @brief Check if CMake reconfiguration is needed
*
* @param project_dir Project directory
* @param build_dir Build directory
* @return true if CMake needs to be reconfigured
*/
bool needs_cmake_reconfigure(const std::filesystem::path &project_dir,
const std::filesystem::path &build_dir);
/**
* @brief Result of prepare_project_for_build
*/
struct build_preparation_result {
bool success = false; ///< True if preparation succeeded
bool cmakelists_regenerated = false; ///< True if CMakeLists.txt was regenerated
bool cmake_reconfigured = false; ///< True if CMake was reconfigured
std::string error_message; ///< Error message if failed
};
// Forward declaration - implemented in workspace.cpp
bool generate_cmakelists_from_toml(const std::filesystem::path &project_dir,
const toml_reader &project_config,
bool verbose);
/**
* @brief Prepare a project for building with smart rebuild detection
*
* This function implements the smart rebuild pipeline:
* 1. Check if CMakeLists.txt needs regeneration from cforge.toml
* 2. Regenerate if needed (using generate_cmakelists_from_toml)
* 3. Check if CMake needs reconfiguration
* 4. Reconfigure if needed
*
* @param project_dir Project directory
* @param build_dir Build directory
* @param config Build configuration (Debug, Release, etc.)
* @param verbose Verbose output
* @param force_regenerate Force CMakeLists.txt regeneration
* @param force_reconfigure Force CMake reconfiguration
* @return build_preparation_result with status information
*/
build_preparation_result
prepare_project_for_build(const std::filesystem::path &project_dir,
const std::filesystem::path &build_dir,
const std::string &config,
bool verbose,
bool force_regenerate = false,
bool force_reconfigure = false);
} // namespace cforge
+298
View File
@@ -0,0 +1,298 @@
/**
* @file platform.hpp
* @brief Centralized platform detection and platform-specific utilities
*
* This header provides compile-time platform detection constants and
* runtime utility functions for cross-platform compatibility.
*/
#pragma once
#include <string>
#include <vector>
#include <filesystem>
namespace cforge {
namespace platform {
// =============================================================================
// Compile-time Platform Detection
// =============================================================================
#if defined(_WIN32) || defined(_WIN64)
inline constexpr bool is_windows = true;
#ifndef NOMINMAX
#define NOMINMAX
#endif
#else
inline constexpr bool is_windows = false;
#endif
#if defined(__APPLE__) && defined(__MACH__)
inline constexpr bool is_macos = true;
#else
inline constexpr bool is_macos = false;
#endif
#if defined(__linux__)
inline constexpr bool is_linux = true;
#else
inline constexpr bool is_linux = false;
#endif
// Unix-like platforms (Linux + macOS)
inline constexpr bool is_unix = is_linux || is_macos;
// =============================================================================
// Compile-time Compiler Detection
// =============================================================================
#if defined(_MSC_VER) && !defined(__clang__)
inline constexpr bool is_msvc = true;
#else
inline constexpr bool is_msvc = false;
#endif
#if defined(__MINGW32__) || defined(__MINGW64__)
inline constexpr bool is_mingw = true;
#else
inline constexpr bool is_mingw = false;
#endif
#if defined(__clang__)
inline constexpr bool is_clang = true;
#if defined(__apple_build_version__)
inline constexpr bool is_apple_clang = true;
#else
inline constexpr bool is_apple_clang = false;
#endif
#else
inline constexpr bool is_clang = false;
inline constexpr bool is_apple_clang = false;
#endif
#if defined(__GNUC__) && !defined(__clang__)
inline constexpr bool is_gcc = true;
#else
inline constexpr bool is_gcc = false;
#endif
// =============================================================================
// Platform Name Strings
// =============================================================================
/**
* @brief Get the current platform name as a string
* @return "windows", "macos", or "linux"
*/
inline std::string get_platform_name() {
if constexpr (is_windows) {
return "windows";
} else if constexpr (is_macos) {
return "macos";
} else {
return "linux";
}
}
/**
* @brief Get the current compiler name as a string
* @return "msvc", "mingw", "apple_clang", "clang", "gcc", or "unknown"
*/
inline std::string get_compiler_name() {
if constexpr (is_msvc) {
return "msvc";
} else if constexpr (is_mingw) {
return "mingw";
} else if constexpr (is_apple_clang) {
return "apple_clang";
} else if constexpr (is_clang) {
return "clang";
} else if constexpr (is_gcc) {
return "gcc";
} else {
return "unknown";
}
}
// =============================================================================
// Platform-specific Path Utilities
// =============================================================================
/**
* @brief Get the path separator for the current platform
* @return "\\" on Windows, "/" on Unix-like systems
*/
inline constexpr const char* path_separator() {
if constexpr (is_windows) {
return "\\";
} else {
return "/";
}
}
/**
* @brief Get the executable extension for the current platform
* @return ".exe" on Windows, "" on Unix-like systems
*/
inline constexpr const char* executable_extension() {
if constexpr (is_windows) {
return ".exe";
} else {
return "";
}
}
/**
* @brief Get the shared library extension for the current platform
* @return ".dll" on Windows, ".dylib" on macOS, ".so" on Linux
*/
inline constexpr const char* shared_library_extension() {
if constexpr (is_windows) {
return ".dll";
} else if constexpr (is_macos) {
return ".dylib";
} else {
return ".so";
}
}
/**
* @brief Get the static library extension for the current platform
* @return ".lib" on Windows (MSVC), ".a" on Unix-like systems
*/
inline constexpr const char* static_library_extension() {
if constexpr (is_windows && is_msvc) {
return ".lib";
} else {
return ".a";
}
}
// =============================================================================
// Visual Studio Detection (Windows only)
// =============================================================================
/**
* @brief Get common Visual Studio installation paths
* @return Vector of paths to check for VS installation
*/
inline std::vector<std::string> get_visual_studio_paths() {
return {
"C:\\Program Files\\Microsoft Visual Studio\18\\Community\\Common7\\IDE\\devenv.exe",
"C:\\Program Files\\Microsoft Visual Studio\18\\Professional\\Common7\\IDE\\devenv.exe",
"C:\\Program Files\\Microsoft Visual Studio\18\\Enterprise\\Common7\\IDE\\devenv.exe",
"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\Common7\\IDE\\devenv.exe",
"C:\\Program Files\\Microsoft Visual Studio\\2022\\Professional\\Common7\\IDE\\devenv.exe",
"C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\Common7\\IDE\\devenv.exe",
"C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\Common7\\IDE\\devenv.exe",
"C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Professional\\Common7\\IDE\\devenv.exe",
"C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise\\Common7\\IDE\\devenv.exe",
"C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\Common7\\IDE\\devenv.exe",
"C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Professional\\Common7\\IDE\\devenv.exe",
"C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Enterprise\\Common7\\IDE\\devenv.exe"
};
}
// =============================================================================
// Doxygen Detection
// =============================================================================
/**
* @brief Get common Doxygen installation paths for the current platform
* @return Vector of paths to check for Doxygen
*/
inline std::vector<std::string> get_doxygen_paths() {
if constexpr (is_windows) {
return {
"C:\\Program Files\\doxygen\\bin\\doxygen.exe",
"C:\\Program Files (x86)\\doxygen\\bin\\doxygen.exe"
};
} else if constexpr (is_macos) {
return {
"/opt/homebrew/bin/doxygen", // Apple Silicon (M1/M2/M3)
"/usr/local/bin/doxygen", // Intel Mac / Homebrew
"/usr/bin/doxygen", // System installation
"/opt/local/bin/doxygen" // MacPorts
};
} else {
return {
"/usr/bin/doxygen",
"/usr/local/bin/doxygen"
};
}
}
// =============================================================================
// Terminal Emulator Detection (Linux)
// =============================================================================
/**
* @brief Get terminal emulator commands in preference order (Linux)
* @return Vector of terminal emulator commands to try
*/
inline std::vector<std::string> get_linux_terminals() {
return {
"x-terminal-emulator", // Debian/Ubuntu default
"gnome-terminal", // GNOME
"konsole", // KDE
"xfce4-terminal", // XFCE
"mate-terminal", // MATE
"lxterminal", // LXDE
"tilix", // Tilix
"terminator", // Terminator
"alacritty", // Alacritty
"kitty", // Kitty
"xterm" // Fallback
};
}
/**
* @brief Build a command to spawn a new terminal with the given command
* @param cmd Command to run in the terminal
* @param terminal_emulator Optional specific terminal to use (empty = auto-detect)
* @return Full command string to spawn terminal
*/
inline std::string build_terminal_command(const std::string& cmd,
const std::string& terminal_emulator = "") {
if constexpr (is_windows) {
return "start \"CForge Run\" cmd /k \"" + cmd + "\"";
} else if constexpr (is_macos) {
return "osascript -e 'tell application \"Terminal\" to do script \"" + cmd + "\"'";
} else {
// Linux: use specified terminal or default
std::string term = terminal_emulator.empty() ? "x-terminal-emulator" : terminal_emulator;
// Different terminals have different argument formats
if (term == "gnome-terminal" || term == "mate-terminal") {
return term + " -- " + cmd + " &";
} else if (term == "konsole") {
return term + " -e " + cmd + " &";
} else {
// Default format works for most terminals
return term + " -e '" + cmd + "' &";
}
}
}
// =============================================================================
// CMake Generator Detection
// =============================================================================
/**
* @brief Get the default CMake generator for the current platform
* @return CMake generator string
* @note This is a simple default; actual detection may use cmake --help
*/
inline std::string get_default_cmake_generator() {
if constexpr (is_windows) {
return "Visual Studio 17 2022"; // Default to VS 2022, with fallback logic elsewhere
} else if constexpr (is_macos) {
return "Xcode"; // Or "Unix Makefiles" depending on preference
} else {
return "Unix Makefiles";
}
}
} // namespace platform
} // namespace cforge
+216
View File
@@ -0,0 +1,216 @@
/**
* @file portable_flags.hpp
* @brief Portable compiler flags abstraction for cross-platform builds
*
* This module provides an abstraction layer for compiler flags, allowing users
* to specify intent-based options (like "optimize = speed") that automatically
* translate to the correct flags for each compiler (MSVC, GCC, Clang).
*/
#pragma once
#include <map>
#include <string>
#include <vector>
#include "core/types.h"
namespace cforge {
// Forward declaration
class toml_reader;
/**
* @brief Portable build options that map to compiler-specific flags
*
* These options can be specified in:
* - [build.config.<config>] sections (per-configuration)
* - [platform.<name>] sections (per-platform)
* - [compiler.<name>] sections (per-compiler)
*/
struct portable_options {
// Optimization level: "none", "debug", "size", "speed", "aggressive"
std::string optimize;
// Warning level: "none", "default", "all", "strict", "pedantic"
std::string warnings;
// Treat warnings as errors
bool warnings_as_errors = false;
// Include debug symbols
bool debug_info = false;
// Runtime sanitizers: "address", "undefined", "thread", "memory", "leak"
std::vector<std::string> sanitizers;
// Link-time optimization
bool lto = false;
// C++ exceptions (true = enabled)
bool exceptions = true;
// Runtime type information (true = enabled)
bool rtti = true;
// Standard library: "default", "libc++", "libstdc++"
std::string stdlib;
// Security hardening: "none", "basic", "full"
std::string hardening;
// Symbol visibility: "default", "hidden"
std::string visibility;
// Check if any options are set
bool has_any() const;
};
/**
* @brief CMake-level options from [build] section
*/
struct cmake_options {
// CMAKE_EXPORT_COMPILE_COMMANDS
bool export_compile_commands = false;
// CMAKE_POSITION_INDEPENDENT_CODE
bool position_independent_code = false;
// CMAKE_INTERPROCEDURAL_OPTIMIZATION
bool interprocedural_optimization = false;
// CMAKE_CXX_VISIBILITY_PRESET + CMAKE_VISIBILITY_INLINES_HIDDEN
bool visibility_hidden = false;
// Custom CMake variables from [build.cmake_variables]
std::map<std::string, std::string> variables;
// Check if any options are set
bool has_any() const;
};
/**
* @brief Compiler type for flag translation
*/
enum class compiler_type { msvc, gcc, clang, apple_clang, mingw, unknown };
/**
* @brief Parse portable options from a TOML section
*
* Can parse from any section that may contain portable options:
* - "build.config.debug", "build.config.release", etc.
* - "platform.windows", "platform.linux", etc.
* - "compiler.msvc", "compiler.gcc", etc.
*
* @param config TOML reader instance
* @param section Section path (e.g., "build.config.release")
* @return Parsed portable options
*/
portable_options parse_portable_options(const toml_reader &config,
const std::string &section);
/**
* @brief Parse CMake options from [build] section
*
* @param config TOML reader instance
* @return Parsed CMake options
*/
cmake_options parse_cmake_options(const toml_reader &config);
/**
* @brief Translate portable options to MSVC flags
*
* @param opts Portable options
* @return Vector of MSVC compiler flags
*/
std::vector<std::string> translate_to_msvc(const portable_options &opts);
/**
* @brief Translate portable options to MSVC linker flags
*
* @param opts Portable options
* @return Vector of MSVC linker flags
*/
std::vector<std::string> translate_to_msvc_link(const portable_options &opts);
/**
* @brief Translate portable options to GCC flags
*
* @param opts Portable options
* @return Vector of GCC compiler flags
*/
std::vector<std::string> translate_to_gcc(const portable_options &opts);
/**
* @brief Translate portable options to GCC linker flags
*
* @param opts Portable options
* @return Vector of GCC linker flags
*/
std::vector<std::string> translate_to_gcc_link(const portable_options &opts);
/**
* @brief Translate portable options to Clang flags
*
* @param opts Portable options
* @return Vector of Clang compiler flags
*/
std::vector<std::string> translate_to_clang(const portable_options &opts);
/**
* @brief Translate portable options to Clang linker flags
*
* @param opts Portable options
* @return Vector of Clang linker flags
*/
std::vector<std::string> translate_to_clang_link(const portable_options &opts);
/**
* @brief Generate CMake code for portable options
*
* Generates compiler-agnostic CMake code that applies the correct flags
* based on the detected compiler.
*
* @param opts Portable options
* @param target_name CMake target name (use ${PROJECT_NAME} for main target)
* @param indent Indentation string (default: "")
* @return CMake code string
*/
std::string generate_portable_flags_cmake(const portable_options &opts,
const std::string &target_name,
const std::string &indent = "");
/**
* @brief Generate CMake code for CMake options
*
* Generates CMake variable settings from cmake_options struct.
*
* @param opts CMake options
* @return CMake code string
*/
std::string generate_cmake_options(const cmake_options &opts);
/**
* @brief Generate CMake code for configuration-specific portable options
*
* Wraps the portable flags in CMAKE_BUILD_TYPE conditionals.
*
* @param config_name Configuration name (e.g., "Debug", "Release")
* @param opts Portable options for this configuration
* @param target_name CMake target name
* @return CMake code string
*/
std::string
generate_config_portable_flags_cmake(const std::string &config_name,
const portable_options &opts,
const std::string &target_name);
/**
* @brief Join a vector of flags into a space-separated string
*
* @param flags Vector of flags
* @return Space-separated string
*/
std::string join_flags(const std::vector<std::string> &flags);
} // namespace cforge
+3 -1
View File
@@ -6,6 +6,8 @@
#ifndef CFORGE_REGISTRY_HPP
#define CFORGE_REGISTRY_HPP
#include "core/types.h"
#include <filesystem>
#include <map>
#include <optional>
@@ -218,7 +220,7 @@ public:
* @return Vector of matching package names
*/
std::vector<std::string> search(const std::string &query,
size_t limit = 20) const;
cforge_size_t limit = 20) const;
/**
* @brief Get package information
+322
View File
@@ -0,0 +1,322 @@
#!/usr/bin/env python3
"""
Refactoring script for cforge codebase.
This script:
1. Analyzes source files for declarations that should be in headers
2. Replaces bare C/C++ types with cforge predefined types
3. Ensures code is in the cforge namespace
"""
import os
import re
import sys
from pathlib import Path
from typing import Dict, List, Tuple, Set
# Type mappings from bare types to cforge types
TYPE_MAPPINGS = {
# Only replace standalone types, not parts of other types
r'\bint\b': 'cforge_int_t',
r'\bunsigned int\b': 'cforge_uint_t',
r'\bsigned int\b': 'cforge_int_t',
r'\bunsigned char\b': 'cforge_byte_t',
r'\bsigned char\b': 'cforge_sbyte_t',
r'\bunsigned short\b': 'cforge_ushort_t',
r'\bsigned short\b': 'cforge_short_t',
r'\bunsigned long long\b': 'cforge_ulong_t',
r'\bsigned long long\b': 'cforge_long_t',
r'\blong long\b': 'cforge_long_t',
r'\bfloat\b': 'cforge_float_t',
r'\bdouble\b': 'cforge_double_t',
r'\blong double\b': 'cforge_ldouble_t',
r'\bsize_t\b': 'cforge_size_t',
}
# Patterns to skip (don't replace in these contexts)
SKIP_PATTERNS = [
r'#include',
r'#define',
r'typedef',
r'using\s+',
r'static_cast<',
r'dynamic_cast<',
r'reinterpret_cast<',
r'const_cast<',
r'std::',
r'fmt::',
r'toml::',
r'cforge_', # Already using cforge types
]
# Files/directories to skip
SKIP_DIRS = {'vendor', 'build', 'deps', '.git', '__pycache__'}
SKIP_FILES = {'types.h', 'constants.h'} # Don't modify type definitions
def should_skip_file(filepath: Path) -> bool:
"""Check if file should be skipped."""
for part in filepath.parts:
if part in SKIP_DIRS:
return True
if filepath.name in SKIP_FILES:
return True
return False
def should_skip_line(line: str) -> bool:
"""Check if line should be skipped for type replacement."""
for pattern in SKIP_PATTERNS:
if re.search(pattern, line):
return True
return False
def find_bare_types(content: str) -> List[Tuple[int, str, str]]:
"""Find bare types that should be replaced."""
results = []
lines = content.split('\n')
for line_num, line in enumerate(lines, 1):
if should_skip_line(line):
continue
for pattern, replacement in TYPE_MAPPINGS.items():
matches = list(re.finditer(pattern, line))
for match in matches:
# Check context - don't replace in certain situations
before = line[:match.start()]
after = line[match.end():]
# Skip if it's part of a template parameter
if '<' in before and '>' not in before:
continue
# Skip if it's already a cforge type
if 'cforge_' in before[-20:]:
continue
results.append((line_num, match.group(), replacement))
return results
def find_declarations_in_source(content: str, filepath: Path) -> List[Tuple[int, str]]:
"""Find declarations in source files that should be in headers."""
results = []
lines = content.split('\n')
# Patterns for declarations that should be in headers
declaration_patterns = [
# Class declarations
r'^class\s+\w+\s*[{;]',
# Struct declarations (not definitions)
r'^struct\s+\w+\s*;',
# Enum declarations
r'^enum\s+(class\s+)?\w+\s*[{;]',
# Global function declarations (not definitions)
r'^(static\s+)?(inline\s+)?(const\s+)?(\w+::)?(\w+)\s+(\w+)\s*\([^)]*\)\s*;',
# Type aliases
r'^using\s+\w+\s*=',
r'^typedef\s+',
]
in_namespace = False
namespace_depth = 0
for line_num, line in enumerate(lines, 1):
stripped = line.strip()
# Track namespace depth
if re.match(r'^namespace\s+\w+\s*{', stripped):
in_namespace = True
namespace_depth += 1
if stripped == '}' and in_namespace:
namespace_depth -= 1
if namespace_depth == 0:
in_namespace = False
# Check for declarations
for pattern in declaration_patterns:
if re.match(pattern, stripped):
# Skip if it's a definition (has body)
if '{' in stripped and '}' not in stripped:
continue
results.append((line_num, stripped[:80]))
break
return results
def find_missing_namespace(content: str, filepath: Path) -> List[str]:
"""Find code that's not in the cforge namespace."""
issues = []
# Check if file uses cforge namespace
if 'namespace cforge' not in content:
if filepath.suffix in ['.cpp', '.hpp']:
# Check if it should be in cforge namespace
if '#include "cforge/' in content or '#include "core/' in content:
issues.append(f"File may need cforge namespace")
return issues
def analyze_file(filepath: Path) -> Dict:
"""Analyze a single file for refactoring needs."""
try:
content = filepath.read_text(encoding='utf-8', errors='ignore')
except Exception as e:
return {'error': str(e)}
result = {
'filepath': str(filepath),
'bare_types': [],
'declarations_in_source': [],
'namespace_issues': [],
}
# Only check for bare types in .cpp and .c files
if filepath.suffix in ['.cpp', '.c']:
result['bare_types'] = find_bare_types(content)
result['declarations_in_source'] = find_declarations_in_source(content, filepath)
result['namespace_issues'] = find_missing_namespace(content, filepath)
return result
def analyze_codebase(root_dir: Path) -> Dict[str, Dict]:
"""Analyze entire codebase."""
results = {}
for ext in ['*.cpp', '*.c', '*.hpp', '*.h']:
for filepath in root_dir.rglob(ext):
if should_skip_file(filepath):
continue
rel_path = filepath.relative_to(root_dir)
results[str(rel_path)] = analyze_file(filepath)
return results
def print_report(results: Dict[str, Dict]):
"""Print analysis report."""
print("=" * 80)
print("CFORGE CODEBASE ANALYSIS REPORT")
print("=" * 80)
# Count issues
total_bare_types = 0
total_declarations = 0
total_namespace = 0
files_with_issues = []
for filepath, analysis in results.items():
if 'error' in analysis:
continue
bare_types = len(analysis.get('bare_types', []))
declarations = len(analysis.get('declarations_in_source', []))
namespace = len(analysis.get('namespace_issues', []))
if bare_types or declarations or namespace:
files_with_issues.append({
'path': filepath,
'bare_types': bare_types,
'declarations': declarations,
'namespace': namespace,
'details': analysis
})
total_bare_types += bare_types
total_declarations += declarations
total_namespace += namespace
print(f"\nSummary:")
print(f" - Files analyzed: {len(results)}")
print(f" - Files with issues: {len(files_with_issues)}")
print(f" - Bare types to replace: {total_bare_types}")
print(f" - Declarations in sources: {total_declarations}")
print(f" - Namespace issues: {total_namespace}")
print("\n" + "-" * 80)
print("FILES WITH BARE TYPES (top 20):")
print("-" * 80)
sorted_files = sorted(files_with_issues, key=lambda x: x['bare_types'], reverse=True)
for f in sorted_files[:20]:
if f['bare_types'] > 0:
print(f"\n{f['path']} ({f['bare_types']} bare types)")
for line_num, old, new in f['details']['bare_types'][:5]:
print(f" Line {line_num}: {old} -> {new}")
if len(f['details']['bare_types']) > 5:
print(f" ... and {len(f['details']['bare_types']) - 5} more")
def replace_types_in_file(filepath: Path, dry_run: bool = True) -> int:
"""Replace bare types with cforge types in a file."""
try:
content = filepath.read_text(encoding='utf-8')
except Exception as e:
print(f"Error reading {filepath}: {e}")
return 0
changes = 0
lines = content.split('\n')
new_lines = []
for line in lines:
new_line = line
if not should_skip_line(line):
# Replace types in order of specificity (longer patterns first)
sorted_mappings = sorted(TYPE_MAPPINGS.items(), key=lambda x: len(x[0]), reverse=True)
for pattern, replacement in sorted_mappings:
# Check if we should replace
if re.search(pattern, new_line):
# More careful replacement - avoid function parameters and return types in STL
# Skip lines with std:: that use these types
if 'std::' in new_line:
continue
old_line = new_line
new_line = re.sub(pattern, replacement, new_line)
if old_line != new_line:
changes += 1
new_lines.append(new_line)
if changes > 0:
new_content = '\n'.join(new_lines)
if not dry_run:
filepath.write_text(new_content, encoding='utf-8')
print(f"{'Would modify' if dry_run else 'Modified'} {filepath}: {changes} replacements")
return changes
def main():
"""Main entry point."""
import argparse
parser = argparse.ArgumentParser(description='Refactor cforge codebase')
parser.add_argument('--analyze', action='store_true', help='Analyze codebase and print report')
parser.add_argument('--replace-types', action='store_true', help='Replace bare types with cforge types')
parser.add_argument('--dry-run', action='store_true', help='Don\'t modify files, just show what would change')
parser.add_argument('--root', default='.', help='Root directory of codebase')
args = parser.parse_args()
root = Path(args.root).resolve()
if args.analyze:
results = analyze_codebase(root)
print_report(results)
elif args.replace_types:
total_changes = 0
for ext in ['*.cpp', '*.c']:
for filepath in root.rglob(ext):
if should_skip_file(filepath):
continue
changes = replace_types_in_file(filepath, dry_run=args.dry_run)
total_changes += changes
print(f"\nTotal changes: {total_changes}")
else:
parser.print_help()
if __name__ == '__main__':
main()
+334
View File
@@ -0,0 +1,334 @@
/**
* @file build_utils.cpp
* @brief Implementation of shared build utilities
*/
#include "core/build_utils.hpp"
#include "core/constants.h"
#include "core/types.h"
namespace cforge {
bool is_generator_valid(const std::string &gen) {
process_result pr =
execute_process("cmake", {"--help"}, "", nullptr, nullptr, 10);
if (!pr.success)
return true; // Assume valid if we can't check
return pr.stdout_output.find(gen) != std::string::npos;
}
std::string get_cmake_generator() {
#ifdef _WIN32
// Prefer Ninja Multi-Config if available and supported
if (is_command_available("ninja", 15) &&
is_generator_valid("Ninja Multi-Config")) {
logger::print_verbose("Using Ninja Multi-Config generator");
return "Ninja Multi-Config";
}
// Try Visual Studio 17 2022
if (is_generator_valid("Visual Studio 17 2022")) {
logger::print_verbose("Using Visual Studio 17 2022 generator");
return "Visual Studio 17 2022";
}
// Fallback to Visual Studio 16 2019 if available
if (is_generator_valid("Visual Studio 16 2019")) {
logger::print_verbose("Using Visual Studio 16 2019 generator");
return "Visual Studio 16 2019";
}
// Last resort: Ninja Multi-Config
logger::print_verbose("Falling back to Ninja Multi-Config generator");
return "Ninja Multi-Config";
#else
return "Unix Makefiles";
#endif
}
std::filesystem::path
get_build_dir_for_config(const std::string &base_dir, const std::string &config,
bool create_if_missing) {
std::filesystem::path build_path;
// For multi-config generators, use single build directory
std::string generator = get_cmake_generator();
if (is_multi_config_generator(generator) || config.empty()) {
build_path = base_dir;
} else {
// Transform config name to lowercase for directory naming
std::string config_lower = string_to_lower(config);
build_path = base_dir + "-" + config_lower;
}
// Create directory if requested and doesn't exist
if (create_if_missing && !std::filesystem::exists(build_path)) {
try {
std::filesystem::create_directories(build_path);
} catch (const std::exception &e) {
logger::print_warning("Failed to create build directory: " +
std::string(e.what()));
}
}
return build_path;
}
std::string get_build_config(const char *explicit_config, cforge_int_t arg_count,
char *const *args,
const toml_reader *project_config) {
// Priority 1: Direct configuration argument
if (explicit_config != nullptr && strlen(explicit_config) > 0) {
logger::print_verbose("Using build configuration from direct argument: " +
std::string(explicit_config));
return std::string(explicit_config);
}
// Priority 2: Command line argument
if (arg_count > 0 && args != nullptr) {
for (int i = 0; i < arg_count; ++i) {
if (args[i] == nullptr)
continue;
std::string arg = args[i];
if (arg == "--config" || arg == "-c") {
if (i + 1 < arg_count && args[i + 1] != nullptr) {
std::string config = args[i + 1];
logger::print_verbose(
"Using build configuration from command line: " + config);
return config;
}
} else if (arg.length() > 9 && arg.substr(0, 9) == "--config=") {
std::string config = arg.substr(9);
logger::print_verbose("Using build configuration from command line: " +
config);
return config;
}
}
}
// Priority 3: Configuration from cforge.toml
if (project_config != nullptr) {
std::string config = project_config->get_string("build.build_type", "");
if (!config.empty()) {
logger::print_verbose("Using build configuration from cforge.toml: " +
config);
return config;
}
}
// Priority 4: Default to Release
logger::print_verbose(
"No build configuration specified, defaulting to Release");
return "Release";
}
std::filesystem::path
find_project_binary(const std::filesystem::path &build_dir,
const std::string &project_name, const std::string &config,
const std::string &binary_type) {
(void)binary_type; // Reserved for future use
std::vector<std::filesystem::path> search_paths;
// Common output locations
search_paths.push_back(build_dir / config / project_name);
search_paths.push_back(build_dir / config /
(project_name + ".exe")); // Windows
search_paths.push_back(build_dir / "bin" / config / project_name);
search_paths.push_back(build_dir / "bin" / config /
(project_name + ".exe")); // Windows
search_paths.push_back(build_dir / project_name);
search_paths.push_back(build_dir / (project_name + ".exe")); // Windows
for (const auto &path : search_paths) {
if (std::filesystem::exists(path)) {
return path;
}
}
return {};
}
bool ensure_cmake_configured(const std::filesystem::path &project_dir,
const std::filesystem::path &build_dir,
const std::string &config, bool verbose,
const std::vector<std::string> &extra_args) {
std::vector<std::string> cmake_args = {"-B", build_dir.string(),
"-S", project_dir.string(),
"-G", get_cmake_generator()};
// Add config for multi-config generators
std::string generator = get_cmake_generator();
if (!is_multi_config_generator(generator)) {
cmake_args.push_back("-DCMAKE_BUILD_TYPE=" + config);
}
// Add any extra arguments
for (const auto &arg : extra_args) {
cmake_args.push_back(arg);
}
return execute_tool("cmake", cmake_args, project_dir.string(), "CMake",
verbose, 120);
}
bool run_cmake_build(const std::filesystem::path &build_dir,
const std::string &config, const std::string &target,
cforge_int_t num_jobs, bool verbose) {
std::vector<std::string> build_args = {"--build", build_dir.string(),
"--config", config};
if (!target.empty()) {
build_args.push_back("--target");
build_args.push_back(target);
}
if (num_jobs > 0) {
build_args.push_back("-j");
build_args.push_back(std::to_string(num_jobs));
}
return execute_tool("cmake", build_args, "", "CMake Build", verbose, 600);
}
bool is_file_newer(const std::filesystem::path &source,
const std::filesystem::path &target) {
if (!std::filesystem::exists(target)) {
return true; // Target doesn't exist, so source is "newer"
}
if (!std::filesystem::exists(source)) {
return false; // Source doesn't exist
}
try {
auto source_time = std::filesystem::last_write_time(source);
auto target_time = std::filesystem::last_write_time(target);
return source_time > target_time;
} catch (const std::exception &e) {
logger::print_verbose("Error comparing file times: " + std::string(e.what()));
return true; // Assume rebuild needed on error
}
}
bool needs_cmakelists_regeneration(const std::filesystem::path &project_dir) {
std::filesystem::path toml_path = project_dir / CFORGE_FILE;
std::filesystem::path cmake_path = project_dir / "CMakeLists.txt";
// No cforge.toml = nothing to regenerate from
if (!std::filesystem::exists(toml_path)) {
return false;
}
// CMakeLists.txt doesn't exist = needs generation
if (!std::filesystem::exists(cmake_path)) {
logger::print_verbose("CMakeLists.txt doesn't exist, regeneration needed");
return true;
}
// Check if cforge.toml is newer than CMakeLists.txt
if (is_file_newer(toml_path, cmake_path)) {
logger::print_verbose("cforge.toml is newer than CMakeLists.txt, regeneration needed");
return true;
}
return false;
}
bool needs_cmake_reconfigure(const std::filesystem::path &project_dir,
const std::filesystem::path &build_dir) {
std::filesystem::path cmake_cache = build_dir / "CMakeCache.txt";
std::filesystem::path cmake_path = project_dir / "CMakeLists.txt";
// No CMakeCache.txt = needs configuration
if (!std::filesystem::exists(cmake_cache)) {
logger::print_verbose("CMakeCache.txt doesn't exist, reconfiguration needed");
return true;
}
// Check if CMakeLists.txt is newer than CMakeCache.txt
if (is_file_newer(cmake_path, cmake_cache)) {
logger::print_verbose("CMakeLists.txt is newer than CMakeCache.txt, reconfiguration needed");
return true;
}
// Also check cforge.toml
std::filesystem::path toml_path = project_dir / CFORGE_FILE;
if (std::filesystem::exists(toml_path) && is_file_newer(toml_path, cmake_cache)) {
logger::print_verbose("cforge.toml is newer than CMakeCache.txt, reconfiguration needed");
return true;
}
return false;
}
build_preparation_result
prepare_project_for_build(const std::filesystem::path &project_dir,
const std::filesystem::path &build_dir,
const std::string &config,
bool verbose,
bool force_regenerate,
bool force_reconfigure) {
build_preparation_result result;
result.success = true;
std::filesystem::path toml_path = project_dir / CFORGE_FILE;
// Step 1: Check if CMakeLists.txt needs regeneration
bool need_regen = force_regenerate || needs_cmakelists_regeneration(project_dir);
if (need_regen && std::filesystem::exists(toml_path)) {
logger::print_action("Regenerating", "CMakeLists.txt from cforge.toml");
// Load the project config
try {
toml::table config_table = toml::parse_file(toml_path.string());
toml_reader project_config(config_table);
// Generate CMakeLists.txt
if (!generate_cmakelists_from_toml(project_dir, project_config, verbose)) {
result.success = false;
result.error_message = "Failed to generate CMakeLists.txt";
return result;
}
result.cmakelists_regenerated = true;
} catch (const std::exception &e) {
result.success = false;
result.error_message = "Failed to parse cforge.toml: " + std::string(e.what());
return result;
}
}
// Step 2: Check if CMake needs reconfiguration
// Note: If we regenerated CMakeLists.txt, we likely need to reconfigure
bool need_reconfig = force_reconfigure ||
result.cmakelists_regenerated ||
needs_cmake_reconfigure(project_dir, build_dir);
if (need_reconfig) {
logger::print_action("Configuring", "project with CMake");
// Create build directory if it doesn't exist
if (!std::filesystem::exists(build_dir)) {
try {
std::filesystem::create_directories(build_dir);
} catch (const std::exception &e) {
result.success = false;
result.error_message = "Failed to create build directory: " + std::string(e.what());
return result;
}
}
// Run CMake configuration
if (!ensure_cmake_configured(project_dir, build_dir, config, verbose)) {
result.success = false;
result.error_message = "CMake configuration failed";
return result;
}
result.cmake_reconfigured = true;
}
return result;
}
} // namespace cforge
-2
View File
@@ -9,8 +9,6 @@
#include "core/command.h"
#include "core/commands.hpp"
using namespace cforge; // Add namespace for logger
// Function to check if the current directory is a workspace
bool cforge_is_workspace_dir(void) {
// Check if workspace file exists
-718
View File
@@ -1,718 +0,0 @@
/**
* @file command_help.cpp
* @brief Implementation of the 'help' command to provide usage information
*/
#include "cforge/log.hpp"
#include "core/commands.hpp"
#include "core/constants.h"
#include <algorithm>
#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>
using namespace cforge;
/**
* @brief Handle the 'help' command
*
* @param ctx Context containing parsed arguments
* @return cforge_int_t Exit code (0 for success)
*/
cforge_int_t cforge_cmd_help(const cforge_context_t *ctx) {
std::string specific_command;
// Check if a specific command was requested
if (ctx->args.args && ctx->args.args[0] && ctx->args.args[0][0] != '-') {
specific_command = ctx->args.args[0];
}
if (specific_command.empty()) {
logger::print_plain("cforge - C++ project management tool");
logger::print_plain("");
logger::print_plain("Available commands:");
logger::print_plain(" init Initialize a new project or workspace");
logger::print_plain(" build Build the project");
logger::print_plain(" clean Clean build artifacts");
logger::print_plain(" run Build and run the project");
logger::print_plain(" test Run tests");
logger::print_plain(" package Create a package for distribution");
logger::print_plain(" pack Alias for package");
logger::print_plain(" deps Manage Git dependencies");
logger::print_plain(" vcpkg Manage vcpkg dependencies");
logger::print_plain(" install Install a cforge project to the system");
logger::print_plain(" search Search for packages in the registry");
logger::print_plain(" info Show detailed package information");
logger::print_plain(" add Add a dependency to the project");
logger::print_plain(" remove Remove a dependency from the project");
logger::print_plain(" update Update cforge or packages");
logger::print_plain(" ide Generate IDE project files");
logger::print_plain(" list List dependencies or projects");
logger::print_plain(
" lock Manage dependency lock file for reproducible builds");
logger::print_plain(" fmt Format source code with clang-format");
logger::print_plain(" lint Run static analysis with clang-tidy");
logger::print_plain(" watch Watch for changes and auto-rebuild");
logger::print_plain(" completions Generate shell completion scripts");
logger::print_plain(" doc Generate documentation with Doxygen");
logger::print_plain(" tree Visualize dependency tree");
logger::print_plain(" new Create files from templates");
logger::print_plain(" bench Run benchmarks");
logger::print_plain(" version Show version information");
logger::print_plain(" help Show help for a specific command");
logger::print_plain("");
logger::print_plain("Usage: cforge <command> [options]");
logger::print_plain("");
logger::print_plain("For more information on a specific command, run "
"'cforge help <command>'");
} else if (specific_command == "init") {
logger::print_plain("cforge init - Initialize a new C++ project");
logger::print_plain("");
logger::print_plain("Usage: cforge init [name] [options]");
logger::print_plain("");
logger::print_plain("Arguments:");
logger::print_plain(
" name Project name (default: current directory name)");
logger::print_plain("");
logger::print_plain("Options:");
logger::print_plain(" --std=c++XX Set C++ standard "
"(11, 14, 17, 20) (default: 17)");
logger::print_plain(" --git Initialize git "
"repository (disabled by default)");
logger::print_plain(" --workspace [name] Create a new "
"workspace with the given name");
logger::print_plain(
" --projects [name1] [name2]... Create multiple projects");
logger::print_plain(" --template [name] Use specific project "
"template (app, lib, header-only)");
logger::print_plain(" --with-tests Add test "
"infrastructure to the project");
logger::print_plain(
" --with-git Initialize git repository");
logger::print_plain(
" --type=[type] Set project binary type (executable, "
"shared_lib, static_lib, header_only)");
logger::print_plain(" -v, --verbose Show verbose output");
logger::print_plain("");
logger::print_plain("Configuration:");
logger::print_plain(" Projects are initialized with a cforge.toml file "
"containing project settings:");
logger::print_plain(" - Project metadata (name, version, C++ standard)");
logger::print_plain(
" - Build configuration (build type, source directories)");
logger::print_plain(" - Packaging settings");
logger::print_plain(" - Dependency management");
logger::print_plain(" - Test configuration");
logger::print_plain("");
logger::print_plain(" Run 'cforge help config' for detailed "
"configuration format information");
logger::print_plain("");
logger::print_plain("Examples:");
logger::print_plain(" cforge init Initialize project "
"in current directory");
logger::print_plain(" cforge init my_project Create new project "
"in a new directory");
logger::print_plain(
" cforge init --type=shared_lib Create a shared library project");
logger::print_plain(
" cforge init --projects a b c Create multiple standalone projects");
logger::print_plain(" cforge init --workspace myws Create a workspace");
logger::print_plain(" cforge init --workspace myws --projects app lib "
"Create workspace with projects");
logger::print_plain("");
logger::print_plain("Notes:");
logger::print_plain(
" - Hyphens in project names are replaced with underscores in code");
logger::print_plain(" - If no name is provided, the current directory "
"name is used as the project name");
} else if (specific_command == "build") {
logger::print_plain("cforge build - Build the project");
logger::print_plain("");
logger::print_plain("Usage: cforge build [options]");
logger::print_plain("");
logger::print_plain("Options:");
logger::print_plain(" -c, --config <config> Set build configuration "
"(Debug, Release, etc.)");
logger::print_plain(
" -j, --jobs <n> Set number of parallel build jobs");
logger::print_plain(" -v, --verbose Enable verbose output");
logger::print_plain(" -t, --target <target> Build specific target");
logger::print_plain(
" -p, --project <project> Build specific project in workspace");
logger::print_plain(
" --gen-workspace-cmake Generate a workspace-level CMakeLists.txt");
logger::print_plain(" --force-regenerate Force regeneration of "
"CMakeLists.txt and clean build");
logger::print_plain(
" --skip-deps, --no-deps Skip updating Git dependencies");
logger::print_plain(
" -P, --profile <name> Use cross-compilation profile");
logger::print_plain("");
logger::print_plain("Examples:");
logger::print_plain(
" cforge build Build with default configuration (Debug)");
logger::print_plain(
" cforge build -c Release Build with Release configuration");
logger::print_plain(
" cforge build -j 4 Build with 4 parallel jobs");
logger::print_plain(
" cforge build -p mylib Build only 'mylib' project in workspace");
logger::print_plain(" cforge build --gen-workspace-cmake Generate a "
"workspace CMakeLists.txt without building");
logger::print_plain(" cforge build --force-regenerate Rebuild with "
"fresh configuration");
logger::print_plain(" cforge build --skip-deps Build without "
"updating Git dependencies");
logger::print_plain(" cforge build --profile android-arm64 Build using "
"cross-compilation profile");
} else if (specific_command == "clean") {
logger::print_plain("cforge clean - Clean build artifacts");
logger::print_plain("");
logger::print_plain("Usage: cforge clean [options]");
logger::print_plain("");
logger::print_plain("Options:");
logger::print_plain(
" -c, --config <config> Clean specific configuration");
logger::print_plain(" --all Clean all configurations");
logger::print_plain(
" --cmake-files Also clean CMake temporary files");
logger::print_plain(
" --regenerate Regenerate CMake files after cleaning");
logger::print_plain(
" --deep Remove dependencies directory (deep clean)");
logger::print_plain(" -v, --verbose Show verbose output");
} else if (specific_command == "run") {
logger::print_plain("cforge run - Build and run the project");
logger::print_plain("");
logger::print_plain("Usage: cforge run [options] [-- <app arguments>]");
logger::print_plain("");
logger::print_plain("Options:");
logger::print_plain(
" -c, --config <config> Build configuration (Debug, Release, etc.)");
logger::print_plain(
" -p, --project <name> Run specific project in workspace");
logger::print_plain(
" --no-build Skip building before running");
logger::print_plain(" -v, --verbose Show verbose output");
logger::print_plain("");
logger::print_plain("Examples:");
logger::print_plain(
" cforge run Build and run with default config");
logger::print_plain(
" cforge run -c Debug Build and run with Debug config");
logger::print_plain(
" cforge run -- arg1 arg2 Pass arguments to the executable");
logger::print_plain(
" cforge run -p app1 -- arg1 Run 'app1' from workspace with args");
logger::print_plain("");
logger::print_plain("Arguments after -- are passed to the application");
} else if (specific_command == "test") {
logger::print_plain("cforge test - Build and run unit tests");
logger::print_plain("");
logger::print_plain(
"Usage: cforge test [options] [<category> [<test1> <test2> ...]] [--]");
logger::print_plain("");
logger::print_plain("Options:");
logger::print_plain(" -c, --config <config> Build configuration "
"(Debug, Release, etc.)");
logger::print_plain(
" -v, --verbose Show verbose build & test output");
logger::print_plain("");
logger::print_plain("Positional arguments:");
logger::print_plain(
" <category> Optional test category to run");
logger::print_plain(
" <test_name> ... Optional test names under the category");
logger::print_plain("");
logger::print_plain("Examples:");
logger::print_plain(" cforge test");
logger::print_plain(" cforge test Math");
logger::print_plain(" cforge test -c Release Math Add");
logger::print_plain(" cforge test -c Release -- Math Add");
logger::print_plain("");
logger::print_plain(
"Use `--` to explicitly separate CForge flags from test filters");
} else if (specific_command == "package") {
logger::print_plain("cforge package - Create distributable packages");
logger::print_plain("");
logger::print_plain("Usage: cforge package [options]");
logger::print_plain("");
logger::print_plain("Options:");
logger::print_plain(" -c, --config <name> Specify the build "
"configuration (Debug/Release)");
logger::print_plain(" -p, --project <name> In a workspace, specify "
"which project to package");
logger::print_plain(
" -t, --type <generator> Specify the package generator to use");
logger::print_plain(" --no-build Skip building the project "
"before packaging");
logger::print_plain(" -v, --verbose Enable verbose output");
logger::print_plain("");
logger::print_plain("Examples:");
logger::print_plain(" cforge package Package the "
"current project with default settings");
logger::print_plain(" cforge package -c Release Package using the "
"Release configuration");
logger::print_plain(" cforge package --no-build Skip building and "
"package with existing binaries");
logger::print_plain(" cforge package -t ZIP Package using the "
"ZIP generator only");
logger::print_plain(" cforge package -p mylib In a workspace, "
"package the 'mylib' project");
logger::print_plain("");
logger::print_plain("Notes:");
logger::print_plain(" - When run in a workspace:");
logger::print_plain(" - By default, packages the main project "
"specified in workspace.toml");
logger::print_plain(" - Use -p to package a specific project");
logger::print_plain(" - Set package.all_projects=true in "
"workspace.toml to package all projects");
logger::print_plain("");
logger::print_plain(
" - Package settings can be configured in cforge.toml:");
logger::print_plain(" [package]");
logger::print_plain(
" enabled = true # Enable/disable packaging");
logger::print_plain(
" generators = [\"ZIP\", \"TGZ\"] # List of generators to use");
logger::print_plain("");
logger::print_plain(" - Workspace package settings in workspace.toml:");
logger::print_plain(" [package]");
logger::print_plain(
" all_projects = false # Whether to package all projects");
logger::print_plain(" generators = [\"ZIP\"] # Default "
"generators for all projects");
} else if (specific_command == "add") {
logger::print_plain("cforge add - Add a dependency");
logger::print_plain("");
logger::print_plain("Usage: cforge add <package>[@version] [options]");
logger::print_plain("");
logger::print_plain("Arguments:");
logger::print_plain(" package Package name (e.g., fmt, spdlog)");
logger::print_plain(" @version Optional version (e.g., @11.1.4, @1.*)");
logger::print_plain("");
logger::print_plain("Source Options (default: registry):");
logger::print_plain(" --git <url> Add as Git dependency");
logger::print_plain(" --tag <tag> Git tag (with --git)");
logger::print_plain(" --branch <name> Git branch (with --git)");
logger::print_plain(" --vcpkg Add as vcpkg package");
logger::print_plain(" --index Add from registry (default)");
logger::print_plain("");
logger::print_plain("Other Options:");
logger::print_plain(" --features <f> Comma-separated features to enable");
logger::print_plain(" --header-only Mark as header-only library");
logger::print_plain(" -v, --verbose Show verbose output");
logger::print_plain("");
logger::print_plain("Examples:");
logger::print_plain(" cforge add fmt Add fmt from registry");
logger::print_plain(" cforge add fmt@11.1.4 Add specific version");
logger::print_plain(" cforge add spdlog --features async");
logger::print_plain(" cforge add boost --vcpkg Add from vcpkg");
logger::print_plain(" cforge add mylib --git https://github.com/user/mylib --tag v1.0");
} else if (specific_command == "remove") {
logger::print_plain("cforge remove - Remove a dependency");
logger::print_plain("");
logger::print_plain("Usage: cforge remove <package> [options]");
logger::print_plain("");
logger::print_plain("Arguments:");
logger::print_plain(" package Package to remove");
logger::print_plain("");
logger::print_plain("Options:");
logger::print_plain(" -v, --verbose Show verbose output");
} else if (specific_command == "search") {
logger::print_plain("cforge search - Search for packages in the registry");
logger::print_plain("");
logger::print_plain("Usage: cforge search <query> [options]");
logger::print_plain("");
logger::print_plain("Arguments:");
logger::print_plain(" query Search term to find packages");
logger::print_plain("");
logger::print_plain("Options:");
logger::print_plain(" --limit <n> Maximum results to show (default: 20)");
logger::print_plain(" --update Force update the package index first");
logger::print_plain(" -v, --verbose Show verbose output");
logger::print_plain("");
logger::print_plain("Examples:");
logger::print_plain(" cforge search json Find JSON-related packages");
logger::print_plain(" cforge search logging Find logging libraries");
logger::print_plain(" cforge search --limit 5 ui Show top 5 UI packages");
} else if (specific_command == "info") {
logger::print_plain("cforge info - Show detailed package information");
logger::print_plain("");
logger::print_plain("Usage: cforge info <package> [options]");
logger::print_plain("");
logger::print_plain("Arguments:");
logger::print_plain(" package Name of package to get info for");
logger::print_plain("");
logger::print_plain("Options:");
logger::print_plain(" --versions Show all available versions");
logger::print_plain(" --update Force update the package index first");
logger::print_plain(" -v, --verbose Show verbose output");
logger::print_plain("");
logger::print_plain("Examples:");
logger::print_plain(" cforge info fmt Show fmt package details");
logger::print_plain(" cforge info spdlog --versions Show all spdlog versions");
} else if (specific_command == "update") {
logger::print_plain("cforge update - Update cforge or packages");
logger::print_plain("");
logger::print_plain("Usage: cforge update <--self|--packages> [options]");
logger::print_plain("");
logger::print_plain("Options:");
logger::print_plain(" -s, --self Update cforge itself to latest version");
logger::print_plain(" -p, --packages Update the package registry index");
logger::print_plain(" -v, --verbose Show verbose output");
logger::print_plain("");
logger::print_plain("Examples:");
logger::print_plain(" cforge update --self Update cforge to latest");
logger::print_plain(" cforge update --packages Refresh package registry");
logger::print_plain("");
logger::print_plain("Note: You must specify either --self or --packages");
} else if (specific_command == "vcpkg") {
logger::print_plain("cforge vcpkg - Run vcpkg commands");
logger::print_plain("");
logger::print_plain("Usage: cforge vcpkg <args...>");
logger::print_plain("");
logger::print_plain("Arguments:");
logger::print_plain(" args... Arguments to pass to vcpkg");
} else if (specific_command == "version") {
logger::print_plain("cforge version - Show cforge version");
logger::print_plain("");
logger::print_plain("Usage: cforge version");
} else if (specific_command == "help") {
logger::print_plain("cforge help - Show help information");
logger::print_plain("");
logger::print_plain("Usage: cforge help [command]");
logger::print_plain("");
logger::print_plain("Arguments:");
logger::print_plain(" command Command to show help for");
} else if (specific_command == "install") {
logger::print_plain(
"cforge install - Install a cforge project to the system");
logger::print_plain("");
logger::print_plain("Usage: cforge install [options]");
logger::print_plain("");
logger::print_plain("Options:");
logger::print_plain(" -c, --config <config> Build configuration to "
"install (Debug, Release)");
logger::print_plain(" --from <path|URL> Source directory or Git "
"URL to install from");
logger::print_plain(
" --to <path> Target install directory");
logger::print_plain(
" --add-to-path Add the install/bin directory to PATH");
logger::print_plain(
" -n, --name <name> Override project name for installation");
logger::print_plain(" --env <VAR> Environment variable to "
"set to install path");
logger::print_plain(" -v, --verbose Show verbose output");
logger::print_plain("");
logger::print_plain("Examples:");
logger::print_plain(" cforge install Install current "
"project to default location");
logger::print_plain(
" cforge install --to C:/Apps/Proj Install to a custom path");
logger::print_plain(
" cforge install --from https://github.com/org/repo.git");
logger::print_plain(
" cforge install --add-to-path Add install to PATH");
} else if (specific_command == "ide") {
logger::print_plain("cforge ide - Generate IDE project files");
logger::print_plain("");
logger::print_plain("Usage: cforge ide [options]");
logger::print_plain("");
logger::print_plain("Options:");
logger::print_plain(" -p, --project <name> Generate files for "
"specified project in workspace");
logger::print_plain(" -v, --verbose Show verbose output");
logger::print_plain("");
} else if (specific_command == "list") {
logger::print_plain("cforge list - List projects or dependencies");
logger::print_plain("");
logger::print_plain("Usage: cforge list [options]");
logger::print_plain("");
logger::print_plain("Options:");
logger::print_plain(" -p, --project <name> List dependencies for a "
"specific project");
logger::print_plain(" -v, --verbose Show verbose output");
logger::print_plain("");
} else if (specific_command == "deps") {
logger::print_plain("cforge deps - Manage Git dependencies");
logger::print_plain("");
logger::print_plain("Usage: cforge deps <command> [options]");
logger::print_plain("");
logger::print_plain("Commands:");
logger::print_plain(" fetch Fetch updates for all Git dependencies");
logger::print_plain(
" checkout Checkout each dependency to its configured ref");
logger::print_plain(" list Show configured Git dependencies");
logger::print_plain("");
logger::print_plain("Options:");
logger::print_plain(" -v, --verbose Show verbose output");
logger::print_plain("");
} else if (specific_command == "pack") {
logger::print_plain("cforge pack - Alias for 'package'");
logger::print_plain("");
logger::print_plain("Usage: cforge pack [options]");
logger::print_plain("");
logger::print_plain("See 'cforge help package' for details");
} else if (specific_command == "lock") {
logger::print_plain("cforge lock - Manage dependency lock file");
logger::print_plain("");
logger::print_plain("Usage: cforge lock [options]");
logger::print_plain("");
logger::print_plain("Options:");
logger::print_plain(" --verify, -v Verify dependencies match lock file");
logger::print_plain(" --clean, -c Remove the lock file");
logger::print_plain(
" --force, -f Force regeneration even if lock exists");
logger::print_plain(" --help, -h Show this help message");
logger::print_plain("");
logger::print_plain(
"The lock file (cforge.lock) ensures reproducible builds by");
logger::print_plain(
"tracking exact versions (commit hashes) of all dependencies.");
logger::print_plain("");
logger::print_plain("Examples:");
logger::print_plain(" cforge lock Generate/update lock file");
logger::print_plain(
" cforge lock --verify Check if deps match lock file");
logger::print_plain(" cforge lock --force Regenerate lock file");
logger::print_plain(" cforge lock --clean Remove lock file");
logger::print_plain("");
logger::print_plain("Best practices:");
logger::print_plain(" 1. Commit cforge.lock to version control");
logger::print_plain(" 2. Run 'cforge lock --verify' in CI pipelines");
logger::print_plain(
" 3. Run 'cforge lock' after adding/updating dependencies");
} else if (specific_command == "fmt" || specific_command == "format") {
logger::print_plain("cforge fmt - Format source code with clang-format");
logger::print_plain("");
logger::print_plain("Usage: cforge fmt [options]");
logger::print_plain("");
logger::print_plain("Options:");
logger::print_plain(
" --check Check formatting without modifying files");
logger::print_plain(" --diff Show diff of formatting changes");
logger::print_plain(" --style <style> Use specific style (llvm, google, "
"chromium, mozilla, webkit)");
logger::print_plain(" -v, --verbose Show verbose output");
logger::print_plain("");
logger::print_plain("Examples:");
logger::print_plain(
" cforge fmt Format all source files in place");
logger::print_plain(
" cforge fmt --check Check if files need formatting");
logger::print_plain(" cforge fmt --diff Show what would change");
logger::print_plain(" cforge fmt --style=google Use Google C++ style");
logger::print_plain("");
logger::print_plain("Notes:");
logger::print_plain(" - Requires clang-format to be installed");
logger::print_plain(
" - Uses .clang-format file if present, otherwise uses --style");
logger::print_plain(
" - Formats .cpp, .cc, .cxx, .c, .hpp, .hxx, .h files");
} else if (specific_command == "lint" || specific_command == "check") {
logger::print_plain("cforge lint - Run static analysis with clang-tidy");
logger::print_plain("");
logger::print_plain("Usage: cforge lint [options]");
logger::print_plain("");
logger::print_plain("Options:");
logger::print_plain(
" --fix Apply suggested fixes automatically");
logger::print_plain(" --checks <list> Specify checks to run (e.g., "
"'modernize-*,bugprone-*')");
logger::print_plain(
" -c, --config <cfg> Build configuration for compile_commands.json");
logger::print_plain(" -v, --verbose Show verbose output");
logger::print_plain("");
logger::print_plain("Examples:");
logger::print_plain(" cforge lint Run all enabled checks");
logger::print_plain(
" cforge lint --fix Run and apply automatic fixes");
logger::print_plain(
" cforge lint --checks='modernize-*' Run only modernize checks");
logger::print_plain("");
logger::print_plain("Notes:");
logger::print_plain(" - Requires clang-tidy to be installed");
logger::print_plain(" - Uses .clang-tidy file if present");
logger::print_plain(
" - Requires compile_commands.json (generated during build)");
} else if (specific_command == "watch") {
logger::print_plain("cforge watch - Watch for changes and auto-rebuild");
logger::print_plain("");
logger::print_plain("Usage: cforge watch [options]");
logger::print_plain("");
logger::print_plain("Options:");
logger::print_plain(
" -c, --config <cfg> Build configuration (Debug, Release)");
logger::print_plain(
" --run, -r Run the executable after successful build");
logger::print_plain(
" --interval <ms> Poll interval in milliseconds (default: 500)");
logger::print_plain(" --release Use Release configuration");
logger::print_plain(" --debug Use Debug configuration");
logger::print_plain(" -v, --verbose Show verbose output");
logger::print_plain("");
logger::print_plain("Examples:");
logger::print_plain(
" cforge watch Watch and rebuild on changes");
logger::print_plain(
" cforge watch --run Watch, rebuild, and run executable");
logger::print_plain(
" cforge watch -c Release Watch with Release configuration");
logger::print_plain("");
logger::print_plain("Notes:");
logger::print_plain(
" - Watches .cpp, .cc, .cxx, .c, .hpp, .hxx, .h, .toml files");
logger::print_plain(
" - Ignores build/, .git/, deps/, vendor/ directories");
logger::print_plain(" - Press Ctrl+C to stop watching");
} else if (specific_command == "completions") {
logger::print_plain(
"cforge completions - Generate shell completion scripts");
logger::print_plain("");
logger::print_plain("Usage: cforge completions <shell>");
logger::print_plain("");
logger::print_plain("Shells:");
logger::print_plain(" bash Generate bash completions");
logger::print_plain(" zsh Generate zsh completions");
logger::print_plain(" powershell Generate PowerShell completions");
logger::print_plain(" fish Generate fish completions");
logger::print_plain("");
logger::print_plain("Examples:");
logger::print_plain(" cforge completions bash >> ~/.bashrc");
logger::print_plain(" cforge completions zsh >> ~/.zshrc");
logger::print_plain(" cforge completions powershell >> $PROFILE");
logger::print_plain(
" cforge completions fish > ~/.config/fish/completions/cforge.fish");
} else if (specific_command == "doc" || specific_command == "docs") {
logger::print_plain("cforge doc - Generate documentation with Doxygen");
logger::print_plain("");
logger::print_plain("Usage: cforge doc [options]");
logger::print_plain("");
logger::print_plain("Options:");
logger::print_plain(
" --init Generate Doxyfile without building docs");
logger::print_plain(" --open Open generated docs in browser");
logger::print_plain(" -o, --output Output directory (default: docs)");
logger::print_plain(" -v, --verbose Show verbose output");
logger::print_plain("");
logger::print_plain("Examples:");
logger::print_plain(" cforge doc Generate documentation");
logger::print_plain(
" cforge doc --init Create Doxyfile for customization");
logger::print_plain(
" cforge doc --open Generate and open in browser");
logger::print_plain(
" cforge doc -o api-docs Output to api-docs/ directory");
logger::print_plain("");
logger::print_plain("Notes:");
logger::print_plain(" - Requires Doxygen to be installed");
logger::print_plain(" - Creates Doxyfile if not present");
logger::print_plain(
" - Edit Doxyfile to customize documentation settings");
} else if (specific_command == "tree") {
logger::print_plain("cforge tree - Visualize dependency tree");
logger::print_plain("");
logger::print_plain("Usage: cforge tree [options]");
logger::print_plain("");
logger::print_plain("Options:");
logger::print_plain(
" -a, --all Show all dependencies including transitive");
logger::print_plain(
" -d, --depth <n> Maximum depth to display (default: 10)");
logger::print_plain(" -i, --inverted Show inverted tree (dependents)");
logger::print_plain("");
logger::print_plain("Examples:");
logger::print_plain(" cforge tree Show dependency tree");
logger::print_plain(" cforge tree -d 2 Limit to 2 levels deep");
logger::print_plain(
" cforge tree --all Show all transitive dependencies");
logger::print_plain("");
logger::print_plain("Output:");
logger::print_plain(" Dependencies are color-coded by type:");
logger::print_plain(" - Cyan: Git dependencies");
logger::print_plain(" - Magenta: vcpkg dependencies");
logger::print_plain(" - Yellow: System dependencies");
logger::print_plain(" - Green: Project dependencies (workspace)");
} else if (specific_command == "new") {
logger::print_plain("cforge new - Create files from templates");
logger::print_plain("");
logger::print_plain("Usage: cforge new <template> <name> [options]");
logger::print_plain("");
logger::print_plain("Templates:");
logger::print_plain(
" class Create a class with header and source files");
logger::print_plain(" header Create a header-only file");
logger::print_plain(" struct Create a struct header file");
logger::print_plain(" interface Create an interface (abstract class)");
logger::print_plain(" test Create a test file");
logger::print_plain(" main Create a main.cpp file");
logger::print_plain("");
logger::print_plain("Options:");
logger::print_plain(" -n, --namespace <name> Wrap in namespace");
logger::print_plain(" -o, --output <dir> Output directory");
logger::print_plain(" -f, --force Overwrite existing files");
logger::print_plain("");
logger::print_plain("Examples:");
logger::print_plain(" cforge new class MyClass Create "
"MyClass.hpp and MyClass.cpp");
logger::print_plain(
" cforge new class MyClass -n myproj With namespace 'myproj'");
logger::print_plain(
" cforge new header utils Create utils.hpp");
logger::print_plain(
" cforge new interface IService Create IService interface");
logger::print_plain(
" cforge new test MyClass Create test_my_class.cpp");
logger::print_plain("");
logger::print_plain("Notes:");
logger::print_plain(" - Headers go to include/, sources go to src/");
logger::print_plain(" - Tests go to tests/");
logger::print_plain(" - Names are converted to appropriate case");
} else if (specific_command == "bench" || specific_command == "benchmark") {
logger::print_plain("cforge bench - Run benchmarks");
logger::print_plain("");
logger::print_plain("Usage: cforge bench [options] [benchmark-name]");
logger::print_plain("");
logger::print_plain("Options:");
logger::print_plain(
" -c, --config <cfg> Build configuration (default: Release)");
logger::print_plain(" --no-build Skip building before running");
logger::print_plain(
" --filter <pattern> Run only benchmarks matching pattern");
logger::print_plain(" --json Output in JSON format");
logger::print_plain(" --csv Output in CSV format");
logger::print_plain(" -v, --verbose Show verbose output");
logger::print_plain("");
logger::print_plain("Examples:");
logger::print_plain(
" cforge bench Run all benchmarks");
logger::print_plain(
" cforge bench --filter 'BM_Sort' Run only Sort benchmarks");
logger::print_plain(
" cforge bench --no-build Run without rebuilding");
logger::print_plain(" cforge bench --json > results.json");
logger::print_plain("");
logger::print_plain("Configuration (cforge.toml):");
logger::print_plain(" [benchmark]");
logger::print_plain(
" directory = \"bench\" # Benchmark source directory");
logger::print_plain(
" target = \"my_benchmarks\" # Specific target to run");
logger::print_plain("");
logger::print_plain("Notes:");
logger::print_plain(" - Benchmarks run in Release mode by default");
logger::print_plain(" - Supports Google Benchmark output format");
logger::print_plain(
" - Look for executables with 'bench' or 'benchmark' in name");
} else {
logger::print_error("Unknown command: " + specific_command);
logger::print_plain("Run 'cforge help' for a list of available commands");
return 1;
}
return 0;
}
@@ -10,6 +10,7 @@
#include "core/process_utils.hpp"
#include "core/registry.hpp"
#include "core/toml_reader.hpp"
#include "core/types.h"
#include "core/workspace.hpp"
#include "core/workspace_utils.hpp"
#include <atomic>
@@ -20,8 +21,6 @@
#include <sstream>
#include <thread>
using namespace cforge;
/**
* @brief Add a dependency to the project configuration
*
@@ -40,7 +39,7 @@ using namespace cforge;
std::string content;
std::ifstream file(config_file);
if (!file) {
logger::print_error("Failed to read configuration file: " +
cforge::logger::print_error("Failed to read configuration file: " +
config_file.string());
return false;
}
@@ -63,7 +62,7 @@ using namespace cforge;
std::ofstream outfile(config_file, std::ios::app);
if (!outfile) {
logger::print_error("Failed to open configuration file for writing: " +
cforge::logger::print_error("Failed to open configuration file for writing: " +
config_file.string());
return false;
}
@@ -76,7 +75,7 @@ using namespace cforge;
outfile.close();
if (verbose) {
logger::print_action("Added", entry);
cforge::logger::print_action("Added", entry);
}
return true;
@@ -122,10 +121,10 @@ static bool install_package_with_vcpkg(const std::filesystem::path &project_dir,
if (!global_dir.empty() && std::filesystem::exists(global_exe)) {
vcpkg_exe = global_exe;
} else {
logger::print_error(
cforge::logger::print_error(
"vcpkg not found. Checked: " + project_vcpkg_exe.string() + " and " +
global_exe.string());
logger::print_action("Run",
cforge::logger::print_action("Run",
"cforge vcpkg setup to set up vcpkg integration");
return false;
}
@@ -142,20 +141,20 @@ static bool install_package_with_vcpkg(const std::filesystem::path &project_dir,
std::vector<std::string> args = {"install", package_spec};
// Run the command
logger::installing(package_spec);
cforge::logger::installing(package_spec);
auto result = execute_process(
auto result = cforge::execute_process(
command, args,
"", // working directory
[verbose](const std::string &line) {
if (verbose) {
logger::print_verbose(line);
cforge::logger::print_verbose(line);
}
},
[](const std::string &line) { logger::print_error(line); });
[](const std::string &line) { cforge::logger::print_error(line); });
if (!result.success) {
logger::print_error("Failed to install package with vcpkg. Exit code: " +
cforge::logger::print_error("Failed to install package with vcpkg. Exit code: " +
std::to_string(result.exit_code));
return false;
}
@@ -178,7 +177,7 @@ static bool clone_git_repo(const std::string &url,
// Check if directory already exists
if (std::filesystem::exists(target_dir)) {
logger::print_action("Updating", target_dir);
cforge::logger::print_action("Updating", target_dir);
// Fetch updates
std::vector<std::string> fetch_args = {"fetch", "--tags"};
@@ -186,56 +185,56 @@ static bool clone_git_repo(const std::string &url,
fetch_args.push_back("--quiet");
}
auto fetch_result = execute_process(
auto fetch_result = cforge::execute_process(
"git", fetch_args, target_dir,
[verbose](const std::string &line) {
if (verbose)
logger::print_verbose(line);
cforge::logger::print_verbose(line);
},
[](const std::string &line) { logger::print_error(line); },
[](const std::string &line) { cforge::logger::print_error(line); },
30 // 30 second timeout
);
if (!fetch_result.success) {
logger::print_warning(
cforge::logger::print_warning(
"Failed to fetch updates, continuing with existing version");
}
// If a tag is specified, check it out
if (!tag.empty() && tag != "") {
logger::print_action("Checking out", "tag " + tag);
cforge::logger::print_action("Checking out", "tag " + tag);
std::string tag_ref = "v" + tag; // Try with 'v' prefix first
std::vector<std::string> checkout_args = {"checkout", tag_ref};
if (!verbose) {
checkout_args.push_back("--quiet");
}
auto checkout_result = execute_process(
auto checkout_result = cforge::execute_process(
"git", checkout_args, target_dir,
[verbose](const std::string &line) {
if (verbose)
logger::print_verbose(line);
cforge::logger::print_verbose(line);
},
[](const std::string &line) { logger::print_error(line); },
[](const std::string &line) { cforge::logger::print_error(line); },
30 // 30 second timeout
);
// If checkout failed with 'v' prefix, try without it
if (!checkout_result.success) {
checkout_args[1] = tag; // Use tag without 'v' prefix
checkout_result = execute_process(
checkout_result = cforge::execute_process(
"git", checkout_args, target_dir,
[verbose](const std::string &line) {
if (verbose)
logger::print_verbose(line);
cforge::logger::print_verbose(line);
},
[](const std::string &line) { logger::print_error(line); },
[](const std::string &line) { cforge::logger::print_error(line); },
30 // 30 second timeout
);
}
if (!checkout_result.success) {
logger::print_error("Failed to checkout tag: " + tag);
cforge::logger::print_error("Failed to checkout tag: " + tag);
return false;
}
}
@@ -248,12 +247,12 @@ static bool clone_git_repo(const std::string &url,
std::filesystem::create_directories(
std::filesystem::path(target_dir).parent_path());
} catch (const std::exception &e) {
logger::print_error("Failed to create target directory: " +
cforge::logger::print_error("Failed to create target directory: " +
std::string(e.what()));
return false;
}
logger::print_action("Cloning", https_url);
cforge::logger::print_action("Cloning", https_url);
// Build the Git command arguments
std::vector<std::string> args = {"clone", "--recursive"};
@@ -270,32 +269,32 @@ static bool clone_git_repo(const std::string &url,
}
// Execute git clone
auto result = execute_process(
auto result = cforge::execute_process(
"git", args, "",
[verbose](const std::string &line) {
if (verbose)
logger::print_verbose(line);
cforge::logger::print_verbose(line);
},
[](const std::string &line) { logger::print_error(line); },
[](const std::string &line) { cforge::logger::print_error(line); },
120 // 120 second timeout for initial clone
);
// If clone failed with 'v' prefix, try without it
if (!result.success && !tag.empty() && tag != "") {
args[args.size() - 1] = tag; // Use tag without 'v' prefix
result = execute_process(
result = cforge::execute_process(
"git", args, "",
[verbose](const std::string &line) {
if (verbose)
logger::print_verbose(line);
cforge::logger::print_verbose(line);
},
[](const std::string &line) { logger::print_error(line); },
[](const std::string &line) { cforge::logger::print_error(line); },
120 // 120 second timeout for initial clone
);
}
if (!result.success) {
logger::print_error("Git clone failed with exit code: " +
cforge::logger::print_error("Git clone failed with exit code: " +
std::to_string(result.exit_code));
return false;
}
@@ -310,7 +309,7 @@ static bool add_dependency_to_section(const std::filesystem::path &config_file,
// Read existing config file
std::ifstream file(config_file);
if (!file) {
logger::print_error("Failed to read configuration file: " +
cforge::logger::print_error("Failed to read configuration file: " +
config_file.string());
return false;
}
@@ -351,7 +350,7 @@ static bool add_dependency_to_section(const std::filesystem::path &config_file,
// If section not found, create it
if (!section_found) {
if (verbose) {
logger::print_verbose("Creating new section: [" + section + "]");
cforge::logger::print_verbose("Creating new section: [" + section + "]");
}
// Add blank line before new section if file doesn't end with blank line
if (!lines.empty() && !lines.back().empty()) {
@@ -372,7 +371,7 @@ static bool add_dependency_to_section(const std::filesystem::path &config_file,
// Write back to file
std::ofstream outfile(config_file);
if (!outfile) {
logger::print_error("Failed to write configuration file: " +
cforge::logger::print_error("Failed to write configuration file: " +
config_file.string());
return false;
}
@@ -383,7 +382,7 @@ static bool add_dependency_to_section(const std::filesystem::path &config_file,
outfile.close();
if (verbose) {
logger::print_verbose("Added dependency to section [" + section +
cforge::logger::print_verbose("Added dependency to section [" + section +
"]: " + entry);
}
@@ -410,7 +409,7 @@ static bool add_git_dependency_to_config(
const std::string &package_url, const std::string &tag, bool verbose) {
// For git dependencies, we need both name and URL
if (package_url.empty()) {
logger::print_error("URL for git dependency not specified");
cforge::logger::print_error("URL for git dependency not specified");
return false;
}
@@ -439,7 +438,7 @@ static bool add_index_dependency_to_config(
if (!features.empty()) {
entry += ", features = [";
for (size_t i = 0; i < features.size(); ++i) {
for (cforge_size_t i = 0; i < features.size(); ++i) {
if (i > 0) entry += ", ";
entry += "\"" + features[i] + "\"";
}
@@ -470,9 +469,9 @@ cforge_int_t cforge_cmd_add(const cforge_context_t *ctx) {
std::filesystem::path config_file = project_dir / CFORGE_FILE;
// If not in workspace root, ensure this is a project directory
if (!in_workspace_root && !std::filesystem::exists(config_file)) {
logger::print_error(
cforge::logger::print_error(
"not a cforge project directory (cforge.toml not found)");
logger::print_action("Run", "cforge init to create a new project");
cforge::logger::print_action("Run", "cforge init to create a new project");
return 1;
}
@@ -482,10 +481,10 @@ cforge_int_t cforge_cmd_add(const cforge_context_t *ctx) {
std::string tag_value;
std::vector<std::string> features;
std::vector<std::string> args;
for (int i = 0; i < ctx->args.arg_count; ++i)
for (cforge_int_t i = 0; i < ctx->args.arg_count; ++i)
args.push_back(ctx->args.args[i]);
std::vector<std::string> filtered;
for (size_t i = 0; i < args.size(); ++i) {
for (cforge_size_t i = 0; i < args.size(); ++i) {
if (args[i] == "--git")
mode_git = true;
else if (args[i] == "--vcpkg")
@@ -499,7 +498,7 @@ cforge_int_t cforge_cmd_add(const cforge_context_t *ctx) {
tag_value = args[i + 1];
++i; // Skip the next argument since it's the tag value
} else {
logger::print_error("--tag flag requires a value");
cforge::logger::print_error("--tag flag requires a value");
return 1;
}
} else if (args[i] == "--features" || args[i] == "-f") {
@@ -518,7 +517,7 @@ cforge_int_t cforge_cmd_add(const cforge_context_t *ctx) {
}
++i;
} else {
logger::print_error("--features flag requires a value");
cforge::logger::print_error("--features flag requires a value");
return 1;
}
} else
@@ -527,9 +526,9 @@ cforge_int_t cforge_cmd_add(const cforge_context_t *ctx) {
args.swap(filtered);
// Count modes
int mode_count = (mode_git ? 1 : 0) + (mode_vcpkg ? 1 : 0) + (mode_index ? 1 : 0);
cforge_int_t mode_count = (mode_git ? 1 : 0) + (mode_vcpkg ? 1 : 0) + (mode_index ? 1 : 0);
if (mode_count > 1) {
logger::print_error("Cannot use multiple source flags (--git, --vcpkg, --index)");
cforge::logger::print_error("Cannot use multiple source flags (--git, --vcpkg, --index)");
return 1;
}
@@ -540,12 +539,12 @@ cforge_int_t cforge_cmd_add(const cforge_context_t *ctx) {
// Check if package name was provided
if (args.empty() || args[0].empty() || args[0][0] == '-') {
logger::print_error("package name not specified");
logger::print_action("Usage", "cforge add <package>[@version] [options]");
logger::print_action("From registry", "cforge add fmt@11.1.4");
logger::print_action("With features", "cforge add spdlog --features async,fmt_external");
logger::print_action("Git dependency", "cforge add --git mylib https://github.com/user/lib --tag v1.0");
logger::print_action("vcpkg package", "cforge add openssl --vcpkg");
cforge::logger::print_error("package name not specified");
cforge::logger::print_action("Usage", "cforge add <package>[@version] [options]");
cforge::logger::print_action("From registry", "cforge add fmt@11.1.4");
cforge::logger::print_action("With features", "cforge add spdlog --features async,fmt_external");
cforge::logger::print_action("Git dependency", "cforge add --git mylib https://github.com/user/lib --tag v1.0");
cforge::logger::print_action("vcpkg package", "cforge add openssl --vcpkg");
return 1;
}
@@ -556,15 +555,15 @@ cforge_int_t cforge_cmd_add(const cforge_context_t *ctx) {
if (mode_git) {
if (args.size() < 2) {
logger::print_error("URL for git dependency not specified");
logger::print_action("Usage",
cforge::logger::print_error("URL for git dependency not specified");
cforge::logger::print_action("Usage",
"cforge add --git <name> <url> [--tag <version>]");
return 1;
}
package_url = args[1];
} else {
// Parse name@version format
size_t at_pos = package_name.find('@');
cforge_size_t at_pos = package_name.find('@');
if (at_pos != std::string::npos) {
package_version = package_name.substr(at_pos + 1);
package_name = package_name.substr(0, at_pos);
@@ -572,23 +571,23 @@ cforge_int_t cforge_cmd_add(const cforge_context_t *ctx) {
}
// Check for verbosity
bool verbose = logger::get_verbosity() == log_verbosity::VERBOSITY_VERBOSE;
bool verbose = cforge::logger::get_verbosity() == cforge::log_verbosity::VERBOSITY_VERBOSE;
// For index mode, verify the package exists in the registry
if (mode_index) {
registry reg;
cforge::registry reg;
// Update index if needed
if (reg.needs_update()) {
logger::print_status("Updating package index...");
cforge::logger::print_status("Updating package index...");
reg.update();
}
auto pkg = reg.get_package(package_name);
if (!pkg) {
logger::print_error("Package '" + package_name + "' not found in registry");
logger::print_action("Hint", "Run 'cforge search " + package_name + "' to search for packages");
logger::print_action("Hint", "Use --git or --vcpkg to add from other sources");
cforge::logger::print_error("Package '" + package_name + "' not found in registry");
cforge::logger::print_action("Hint", "Run 'cforge search " + package_name + "' to search for packages");
cforge::logger::print_action("Hint", "Use --git or --vcpkg to add from other sources");
return 1;
}
@@ -602,14 +601,14 @@ cforge_int_t cforge_cmd_add(const cforge_context_t *ctx) {
// Validate version exists
std::string resolved = reg.resolve_version(package_name, package_version);
if (resolved.empty()) {
logger::print_error("Version '" + package_version + "' not found for package '" + package_name + "'");
logger::print_action("Hint", "Run 'cforge info " + package_name + " --versions' to see available versions");
cforge::logger::print_error("Version '" + package_version + "' not found for package '" + package_name + "'");
cforge::logger::print_action("Hint", "Run 'cforge info " + package_name + " --versions' to see available versions");
return 1;
}
package_version = resolved;
}
logger::print_action("Found", package_name + " " + package_version);
cforge::logger::print_action("Found", package_name + " " + package_version);
}
// Helper lambda to add dependency to a single project
@@ -620,9 +619,9 @@ cforge_int_t cforge_cmd_add(const cforge_context_t *ctx) {
if (mode_git) {
// Get the configured dependency directory from cforge.toml
toml_reader project_config;
cforge::toml_reader project_config;
if (!project_config.load(proj_config.string())) {
logger::print_error("Failed to read project configuration");
cforge::logger::print_error("Failed to read project configuration");
return false;
}
@@ -651,18 +650,18 @@ cforge_int_t cforge_cmd_add(const cforge_context_t *ctx) {
}
if (!cfg_ok) {
logger::print_error("Failed to update configuration for dependency: " +
cforge::logger::print_error("Failed to update configuration for dependency: " +
package_name);
return false;
}
if (!inst_ok) {
if (mode_vcpkg) {
logger::print_warning("Dependency '" + package_name +
cforge::logger::print_warning("Dependency '" + package_name +
"' added to config, but installation failed. "
"Run 'cforge vcpkg setup' then rebuild");
} else if (mode_git) {
logger::print_warning("Dependency '" + package_name +
cforge::logger::print_warning("Dependency '" + package_name +
"' added to config, but clone failed");
}
}
@@ -687,11 +686,11 @@ cforge_int_t cforge_cmd_add(const cforge_context_t *ctx) {
}
if (!all_ok) {
logger::print_error("Failed to add dependency to some workspace projects");
cforge::logger::print_error("Failed to add dependency to some workspace projects");
return 1;
}
logger::print_action("Added", package_name + " to workspace projects");
cforge::logger::print_action("Added", package_name + " to workspace projects");
return 0;
}
@@ -700,6 +699,6 @@ cforge_int_t cforge_cmd_add(const cforge_context_t *ctx) {
return 1;
}
logger::print_action("Added", package_name + " " + package_version);
cforge::logger::print_action("Added", package_name + " " + package_version);
return 0;
}
@@ -8,6 +8,7 @@
#include "core/commands.hpp"
#include "core/process_utils.hpp"
#include "core/toml_reader.hpp"
#include "core/types.h"
#include "core/workspace.hpp"
#include <chrono>
@@ -25,9 +26,9 @@ namespace {
struct benchmark_result {
std::string name;
double iterations;
double time_ns;
double time_per_op_ns;
cforge_double_t iterations;
cforge_double_t time_ns;
cforge_double_t time_per_op_ns;
std::string unit;
};
@@ -129,7 +130,7 @@ parse_google_benchmark_output(const std::string &output) {
/**
* @brief Format time duration for display
*/
std::string format_duration(double ns) {
std::string format_duration(cforge_double_t ns) {
if (ns < 1000) {
return fmt::format("{:.2f} ns", ns);
} else if (ns < 1000000) {
@@ -145,24 +146,22 @@ std::string format_duration(double ns) {
* @brief Run a simple benchmark on a function
*/
[[maybe_unused]] void run_simple_benchmark(const std::string &name, std::function<void()> func,
int iterations = 1000) {
using namespace cforge;
cforge_int_t iterations = 1000) {
// Warmup
for (int i = 0; i < 10; i++) {
for (cforge_int_t i = 0; i < 10; i++) {
func();
}
// Actual benchmark
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < iterations; i++) {
for (cforge_int_t i = 0; i < iterations; i++) {
func();
}
auto end = std::chrono::high_resolution_clock::now();
auto duration =
std::chrono::duration_cast<std::chrono::nanoseconds>(end - start);
double ns_per_op = static_cast<double>(duration.count()) / iterations;
cforge_double_t ns_per_op = static_cast<cforge_double_t>(duration.count()) / iterations;
fmt::print(" {:<40} {:>15} ({} iterations)\n", name,
format_duration(ns_per_op), iterations);
@@ -174,8 +173,6 @@ std::string format_duration(double ns) {
* @brief Handle the 'bench' command for running benchmarks
*/
cforge_int_t cforge_cmd_bench(const cforge_context_t *ctx) {
using namespace cforge;
fs::path project_dir = ctx->working_dir;
// Parse arguments
@@ -186,7 +183,7 @@ cforge_int_t cforge_cmd_bench(const cforge_context_t *ctx) {
std::string output_format;
std::string specific_bench;
for (int i = 0; i < ctx->args.arg_count; i++) {
for (cforge_int_t i = 0; i < ctx->args.arg_count; i++) {
std::string arg = ctx->args.args[i];
if (arg == "-c" || arg == "--config") {
if (i + 1 < ctx->args.arg_count) {
@@ -210,12 +207,12 @@ cforge_int_t cforge_cmd_bench(const cforge_context_t *ctx) {
// Check for cforge.toml
fs::path config_file = project_dir / "cforge.toml";
if (!fs::exists(config_file)) {
logger::print_error("No cforge.toml found in current directory");
cforge::logger::print_error("No cforge.toml found in current directory");
return 1;
}
// Load project config
toml_reader reader;
cforge::toml_reader reader;
reader.load(config_file.string());
// Check for benchmark configuration
@@ -224,7 +221,7 @@ cforge_int_t cforge_cmd_bench(const cforge_context_t *ctx) {
// Build first if needed
if (build_first) {
logger::print_action("Building",
cforge::logger::print_action("Building",
"project in " + config + " mode for benchmarks");
// Create a context for build
@@ -236,14 +233,14 @@ cforge_int_t cforge_cmd_bench(const cforge_context_t *ctx) {
build_args_struct.args = nullptr;
build_ctx.args = build_args_struct;
int build_result = cforge_cmd_build(&build_ctx);
cforge_int_t build_result = cforge_cmd_build(&build_ctx);
// Clean up
free(build_args_struct.command);
free(build_args_struct.config);
if (build_result != 0) {
logger::print_error("Build failed. Cannot run benchmarks.");
cforge::logger::print_error("Build failed. Cannot run benchmarks.");
return 1;
}
}
@@ -273,36 +270,36 @@ cforge_int_t cforge_cmd_bench(const cforge_context_t *ctx) {
if (it != bench_executables.end()) {
bench_executables = {*it};
} else {
logger::print_error("No benchmark matching '" + specific_bench +
cforge::logger::print_error("No benchmark matching '" + specific_bench +
"' found");
return 1;
}
}
if (bench_executables.empty()) {
logger::print_warning("No benchmark executables found");
logger::print_plain("");
logger::print_plain("To add benchmarks:");
logger::print_plain(
cforge::logger::print_warning("No benchmark executables found");
cforge::logger::print_plain("");
cforge::logger::print_plain("To add benchmarks:");
cforge::logger::print_plain(
" 1. Create a bench/ directory with benchmark source files");
logger::print_plain(
cforge::logger::print_plain(
" 2. Name the target with 'bench' or 'benchmark' in the name");
logger::print_plain(" 3. Or configure in cforge.toml:");
logger::print_plain(" [benchmark]");
logger::print_plain(" target = \"my_benchmarks\"");
logger::print_plain(" directory = \"bench\"");
cforge::logger::print_plain(" 3. Or configure in cforge.toml:");
cforge::logger::print_plain(" [benchmark]");
cforge::logger::print_plain(" target = \"my_benchmarks\"");
cforge::logger::print_plain(" directory = \"bench\"");
return 0;
}
// Run benchmarks
logger::print_header("Running benchmarks");
cforge::logger::print_header("Running benchmarks");
fmt::print("\n");
int total_benchmarks = 0;
cforge_int_t total_benchmarks = 0;
std::vector<benchmark_result> all_results;
for (const auto &bench_exe : bench_executables) {
logger::print_action("Running", bench_exe.filename().string());
cforge::logger::print_action("Running", bench_exe.filename().string());
std::vector<std::string> args;
if (!filter.empty()) {
@@ -314,7 +311,7 @@ cforge_int_t cforge_cmd_bench(const cforge_context_t *ctx) {
args.push_back("--benchmark_format=csv");
}
auto result = execute_process(
auto result = cforge::execute_process(
bench_exe.string(), args, project_dir.string(),
[&verbose, &all_results, &total_benchmarks](const std::string &line) {
fmt::print("{}\n", line);
@@ -330,7 +327,7 @@ cforge_int_t cforge_cmd_bench(const cforge_context_t *ctx) {
});
if (result.exit_code != 0) {
logger::print_warning("Benchmark " + bench_exe.filename().string() +
cforge::logger::print_warning("Benchmark " + bench_exe.filename().string() +
" exited with code " +
std::to_string(result.exit_code));
}
@@ -339,7 +336,7 @@ cforge_int_t cforge_cmd_bench(const cforge_context_t *ctx) {
}
// Summary
logger::print_header("Benchmark Summary");
cforge::logger::print_header("Benchmark Summary");
fmt::print(" Ran {} benchmark executable(s)\n", bench_executables.size());
if (total_benchmarks > 0) {
fmt::print(" Completed {} benchmark(s)\n", total_benchmarks);
File diff suppressed because it is too large Load Diff
@@ -7,6 +7,7 @@
#include "core/commands.hpp"
#include "core/constants.h"
#include "core/include_analyzer.hpp"
#include "core/types.h"
#include "core/workspace.hpp"
#include <filesystem>
@@ -21,25 +22,25 @@ static void print_help() {
fmt::print("Detect and display circular include dependencies\n\n");
fmt::print("Options:\n");
fmt::print(" --include-deps Also check dependency headers\n");
fmt::print(" --workspace Check all workspace projects\n");
fmt::print(" --cforge::workspace Check all cforge::workspaceprojects\n");
fmt::print(" --json Output as JSON\n");
fmt::print(" --limit N Limit output to first N chains\n");
fmt::print(" -h, --help Show this help message\n");
}
static int analyze_project(const std::filesystem::path &project_dir,
bool include_deps, bool json_output, int limit) {
logger::print_action("Analyzing", project_dir.string());
static cforge_int_t analyze_project(const std::filesystem::path &project_dir,
bool include_deps, bool json_output, cforge_int_t limit) {
cforge::logger::print_action("Analyzing", project_dir.string());
include_analyzer analyzer(project_dir);
include_analysis_result result = analyzer.analyze(include_deps);
cforge::include_analyzer analyzer(project_dir);
cforge::include_analysis_result result = analyzer.analyze(include_deps);
logger::print_verbose("Analyzed " + std::to_string(result.total_files_analyzed) +
cforge::logger::print_verbose("Analyzed " + std::to_string(result.total_files_analyzed) +
" files");
if (!result.has_cycles) {
if (json_output) {
fmt::print("{}\n", format_circular_chains_json(result.chains));
fmt::print("{}\n", cforge::format_circular_chains_json(result.chains));
} else {
fmt::print(fg(fmt::color::green), "{:>12}", "No cycles");
fmt::print(" found in {}\n", project_dir.string());
@@ -49,16 +50,16 @@ static int analyze_project(const std::filesystem::path &project_dir,
// Apply limit if specified
std::vector<circular_chain> chains_to_show = result.chains;
if (limit > 0 && static_cast<size_t>(limit) < chains_to_show.size()) {
if (limit > 0 && static_cast<cforge_size_t>(limit) < chains_to_show.size()) {
chains_to_show.resize(limit);
}
if (json_output) {
fmt::print("{}\n", format_circular_chains_json(chains_to_show));
fmt::print("{}\n", cforge::format_circular_chains_json(chains_to_show));
} else {
fmt::print("\n{}", format_circular_chains(chains_to_show));
fmt::print("\n{}", cforge::format_circular_chains(chains_to_show));
if (limit > 0 && static_cast<size_t>(limit) < result.chains.size()) {
if (limit > 0 && static_cast<cforge_size_t>(limit) < result.chains.size()) {
fmt::print(fg(fmt::color::yellow),
"... and {} more chains (use --limit to see more)\n",
result.chains.size() - limit);
@@ -71,31 +72,29 @@ static int analyze_project(const std::filesystem::path &project_dir,
} // namespace cforge
cforge_int_t cforge_cmd_circular(const cforge_context_t *ctx) {
using namespace cforge;
bool include_deps = false;
bool check_workspace = false;
bool check_workspace= false;
bool json_output = false;
int limit = 0;
cforge_int_t limit = 0;
// Parse arguments
for (int i = 0; i < ctx->args.arg_count; ++i) {
for (cforge_int_t i = 0; i < ctx->args.arg_count; ++i) {
std::string arg = ctx->args.args[i];
if (arg == "-h" || arg == "--help") {
print_help();
cforge::print_help();
return 0;
} else if (arg == "--include-deps") {
include_deps = true;
} else if (arg == "--workspace") {
check_workspace = true;
check_workspace= true;
} else if (arg == "--json") {
json_output = true;
} else if (arg == "--limit" && i + 1 < ctx->args.arg_count) {
try {
limit = std::stoi(ctx->args.args[++i]);
} catch (...) {
logger::print_error("Invalid limit value");
cforge::logger::print_error("Invalid limit value");
return 1;
}
}
@@ -104,33 +103,33 @@ cforge_int_t cforge_cmd_circular(const cforge_context_t *ctx) {
std::filesystem::path current_dir = std::filesystem::current_path();
// Check if we're in a workspace
auto [is_ws, workspace_dir] = is_in_workspace(current_dir);
auto [is_ws, workspace_dir] = cforge::is_in_workspace(current_dir);
// Auto-detect workspace: if we're in a workspace and at the workspace root, enable workspace mode
// Auto-detect workspace: if we're in a cforge::workspaceand at the cforge::workspaceroot, enable cforge::workspacemode
if (is_ws && !check_workspace) {
// Check if current directory is the workspace root
// Check if current directory is the cforge::workspaceroot
if (current_dir == workspace_dir) {
logger::print_verbose("Auto-detected workspace root, enabling workspace mode");
check_workspace = true;
cforge::logger::print_verbose("Auto-detected cforge::workspaceroot, enabling cforge::workspacemode");
check_workspace= true;
}
}
if (check_workspace && is_ws) {
if (check_workspace&& is_ws) {
// Analyze all projects in workspace
workspace ws;
cforge::workspace ws;
if (!ws.load(workspace_dir)) {
logger::print_error("Failed to load workspace");
cforge::logger::print_error("Failed to load workspace");
return 1;
}
logger::print_action("Checking", "workspace " + ws.get_name());
cforge::logger::print_action("Checking", "workspace " + ws.get_name());
int total_cycles = 0;
cforge_int_t total_cycles = 0;
auto projects = ws.get_projects();
for (const auto &project : projects) {
if (std::filesystem::exists(project.path)) {
int result = analyze_project(project.path, include_deps, json_output, limit);
cforge_int_t result = cforge::analyze_project(project.path, include_deps, json_output, limit);
if (result > 0) {
total_cycles++;
}
@@ -138,12 +137,12 @@ cforge_int_t cforge_cmd_circular(const cforge_context_t *ctx) {
}
if (total_cycles > 0) {
logger::print_warning(std::to_string(total_cycles) +
cforge::logger::print_warning(std::to_string(total_cycles) +
" project(s) have circular dependencies");
return 1;
}
logger::print_success("No circular dependencies in workspace");
cforge::logger::print_success("No circular dependencies in workspace");
return 0;
}
@@ -163,10 +162,10 @@ cforge_int_t cforge_cmd_circular(const cforge_context_t *ctx) {
}
if (!std::filesystem::exists(project_dir / CFORGE_FILE)) {
logger::print_error("Not in a cforge project directory");
cforge::logger::print_error("Not in a cforge project directory");
return 1;
}
}
return analyze_project(project_dir, include_deps, json_output, limit);
return cforge::analyze_project(project_dir, include_deps, json_output, limit);
}
@@ -11,6 +11,7 @@
#include "core/process_utils.hpp"
#include "core/script_runner.hpp"
#include "core/toml_reader.hpp"
#include "core/types.h"
#include "core/workspace.hpp"
#include <algorithm>
@@ -22,8 +23,6 @@
#include <windows.h>
#endif
using namespace cforge;
/**
* @brief Make all files in a directory writable (removes read-only attribute)
* This is needed on Windows to delete .git directories that have
@@ -79,7 +78,7 @@ static bool force_remove_directory(const std::filesystem::path &dir) {
#ifdef _WIN32
// Third attempt on Windows: use system command
std::string cmd = "cmd /c \"rmdir /s /q \"" + dir.string() + "\"\" 2>nul";
int result = std::system(cmd.c_str());
cforge_int_t result = std::system(cmd.c_str());
if (result == 0 || !std::filesystem::exists(dir)) {
return true;
}
@@ -108,7 +107,7 @@ static bool force_remove_directory(const std::filesystem::path &dir) {
return !std::filesystem::exists(dir);
}
// Note: get_build_dir_for_config() is now in build_utils.hpp
// Note: cforge::get_build_dir_for_config() is now in build_utils.hpp
/**
* @brief Find all configuration-specific build directories
@@ -127,7 +126,7 @@ find_all_build_dirs(const std::string &base_dir) {
std::vector<std::string> configs = {"debug", "relwithdebinfo", "minsizerel"};
for (const auto &config : configs) {
std::filesystem::path config_dir =
get_build_dir_for_config(base_dir, config);
cforge::get_build_dir_for_config(base_dir, config);
if (std::filesystem::exists(config_dir)) {
build_dirs.push_back(config_dir);
}
@@ -146,18 +145,18 @@ find_all_build_dirs(const std::string &base_dir) {
static bool clean_build_directory(const std::filesystem::path &build_dir,
bool /*verbose*/) {
if (!std::filesystem::exists(build_dir)) {
logger::print_status("Build directory does not exist, nothing to clean: " +
cforge::logger::print_status("Build directory does not exist, nothing to clean: " +
build_dir.string());
return true;
}
logger::removing(build_dir.string());
cforge::logger::removing(build_dir.string());
if (force_remove_directory(build_dir)) {
logger::print_action("Removed", build_dir.string());
cforge::logger::print_action("Removed", build_dir.string());
return true;
} else {
logger::print_error("Failed to remove build directory: " +
cforge::logger::print_error("Failed to remove build directory: " +
build_dir.string());
return false;
}
@@ -170,7 +169,7 @@ static bool clean_build_directory(const std::filesystem::path &build_dir,
* @return bool Success flag
*/
static bool clean_cmake_files(bool verbose) {
logger::cleaning("CMake temporary files");
cforge::logger::cleaning("CMake temporary files");
std::vector<std::string> cmake_files = {
"CMakeCache.txt", "CMakeFiles",
@@ -180,7 +179,7 @@ static bool clean_cmake_files(bool verbose) {
"cforge.hash"};
bool success = true;
int count = 0;
cforge_int_t count = 0;
// Remove CMake files from current directory
for (const auto &file : cmake_files) {
@@ -196,13 +195,13 @@ static bool clean_cmake_files(bool verbose) {
std::filesystem::remove(filepath);
count++;
if (verbose) {
logger::print_verbose("Removed: " + filepath.string() +
cforge::logger::print_verbose("Removed: " + filepath.string() +
" (will be regenerated from cforge.toml)");
}
} else {
// Skip CMakeLists.txt if no cforge.toml exists
if (verbose) {
logger::print_verbose("Preserving: " + filepath.string() +
cforge::logger::print_verbose("Preserving: " + filepath.string() +
" (no cforge.toml found)");
}
}
@@ -216,11 +215,11 @@ static bool clean_cmake_files(bool verbose) {
}
if (verbose) {
logger::print_verbose("Removed: " + filepath.string());
cforge::logger::print_verbose("Removed: " + filepath.string());
}
}
} catch (const std::exception &e) {
logger::print_error("Failed to remove " + filepath.string() + ": " +
cforge::logger::print_error("Failed to remove " + filepath.string() + ": " +
std::string(e.what()));
success = false;
}
@@ -228,17 +227,17 @@ static bool clean_cmake_files(bool verbose) {
}
if (count > 0) {
logger::print_action("Cleaned",
cforge::logger::print_action("Cleaned",
std::to_string(count) + " CMake files/directories");
// Check if CMakeLists.txt was removed and cforge.toml exists
if (std::filesystem::exists(std::filesystem::current_path() /
CFORGE_FILE)) {
logger::print_status("CMakeLists.txt has been deleted. It will be "
cforge::logger::print_status("CMakeLists.txt has been deleted. It will be "
"regenerated from cforge.toml when you run build");
}
} else {
logger::print_status("No CMake files found to clean");
cforge::logger::print_status("No CMake files found to clean");
}
return success;
@@ -262,7 +261,7 @@ static bool regenerate_cmake_files(const std::filesystem::path &project_dir,
try {
std::filesystem::create_directories(build_dir);
} catch (const std::exception &e) {
logger::print_error("Failed to create build directory: " +
cforge::logger::print_error("Failed to create build directory: " +
std::string(e.what()));
return false;
}
@@ -302,17 +301,17 @@ static bool regenerate_cmake_files(const std::filesystem::path &project_dir,
#endif
// Execute cmake command
logger::print_status("Running CMake configure");
cforge::logger::print_status("Running CMake configure");
bool result =
execute_tool(cmake_cmd, cmake_args, build_dir.string(), "CMake", verbose);
cforge::execute_tool(cmake_cmd, cmake_args, build_dir.string(), "CMake", verbose);
if (!result) {
logger::print_error("Failed to regenerate CMake files");
cforge::logger::print_error("Failed to regenerate CMake files");
return false;
}
logger::print_action("Regenerated", "CMake files");
cforge::logger::print_action("Regenerated", "CMake files");
return true;
}
@@ -323,19 +322,19 @@ static bool regenerate_cmake_files(const std::filesystem::path &project_dir,
* @return cforge_int_t Exit code (0 for success)
*/
cforge_int_t cforge_cmd_clean(const cforge_context_t *ctx) {
// Check for workspace clean
// Check for cforge::workspaceclean
std::filesystem::path current_dir(ctx->working_dir);
if (std::filesystem::exists(current_dir / WORKSPACE_FILE)) {
// Workspace cleaning: only clean root workspace build outputs
logger::cleaning("workspace build outputs");
// Workspace cleaning: only clean root cforge::workspacebuild outputs
cforge::logger::cleaning("cforge::workspacebuild outputs");
// Parse clean arguments
bool clean_all = false;
bool clean_cmake = true;
bool regenerate = false;
// bool deep = false; // Reserved for future deep clean functionality
std::string config_name;
bool verbose = logger::get_verbosity() == log_verbosity::VERBOSITY_VERBOSE;
for (int i = 0; i < ctx->args.arg_count; ++i) {
bool verbose = cforge::logger::get_verbosity() == cforge::log_verbosity::VERBOSITY_VERBOSE;
for (cforge_int_t i = 0; i < ctx->args.arg_count; ++i) {
std::string arg = ctx->args.args[i];
if (arg == "--all")
clean_all = true;
@@ -349,7 +348,7 @@ cforge_int_t cforge_cmd_clean(const cforge_context_t *ctx) {
config_name = ctx->args.args[++i];
}
}
// Determine workspace build directory(s)
// Determine cforge::workspacebuild directory(s)
std::filesystem::path base_build = current_dir / DEFAULT_BUILD_DIR;
std::vector<std::filesystem::path> build_dirs;
if (clean_all) {
@@ -359,35 +358,35 @@ cforge_int_t cforge_cmd_clean(const cforge_context_t *ctx) {
}
} else {
// Always clean the base build directory in workspace
logger::cleaning(base_build.string());
cforge::logger::cleaning(base_build.string());
build_dirs.push_back(base_build);
}
// Clean CMake files in workspace root
// Clean CMake files in cforge::workspaceroot
if (clean_cmake) {
auto old_cwd = std::filesystem::current_path();
std::filesystem::current_path(current_dir);
clean_cmake_files(verbose);
std::filesystem::current_path(old_cwd);
// Also remove workspace CMakeLists.txt if present
// Also remove cforge::workspaceCMakeLists.txt if present
std::filesystem::path ws_cmake = current_dir / "CMakeLists.txt";
if (std::filesystem::exists(ws_cmake)) {
logger::removing(ws_cmake.string());
cforge::logger::removing(ws_cmake.string());
try {
std::filesystem::remove(ws_cmake);
logger::print_action("Removed", "workspace CMakeLists.txt");
cforge::logger::print_action("Removed", "cforge::workspaceCMakeLists.txt");
} catch (const std::exception &e) {
logger::print_error("Failed to remove workspace CMakeLists.txt: " +
cforge::logger::print_error("Failed to remove cforge::workspaceCMakeLists.txt: " +
std::string(e.what()));
}
}
// Also clean CMake files in each project directory
workspace ws;
cforge::workspace ws;
if (ws.load(current_dir)) {
auto projects = ws.get_projects();
for (const auto &proj : projects) {
std::filesystem::path proj_path = proj.path;
logger::cleaning("CMake files in " + proj_path.string());
cforge::logger::cleaning("CMake files in " + proj_path.string());
auto p_old = std::filesystem::current_path();
std::filesystem::current_path(proj_path);
clean_cmake_files(verbose);
@@ -402,20 +401,20 @@ cforge_int_t cforge_cmd_clean(const cforge_context_t *ctx) {
regenerate_cmake_files(current_dir, bd, config_name, verbose);
}
}
logger::print_action("Finished", "workspace clean");
cforge::logger::print_action("Finished", "cforge::workspaceclean");
return 0;
}
// Check if cforge.toml exists
if (!std::filesystem::exists(CFORGE_FILE)) {
logger::print_error("Not a valid cforge project (missing " +
cforge::logger::print_error("Not a valid cforge project (missing " +
std::string(CFORGE_FILE) + ")");
return 1;
}
// Load project configuration
toml_reader config;
cforge::toml_reader config;
if (!config.load(CFORGE_FILE)) {
logger::print_error("Failed to parse " + std::string(CFORGE_FILE));
cforge::logger::print_error("Failed to parse " + std::string(CFORGE_FILE));
return 1;
}
@@ -433,10 +432,10 @@ cforge_int_t cforge_cmd_clean(const cforge_context_t *ctx) {
bool regenerate = false; // Regenerate CMake files after cleaning
bool deep = false; // Deep clean: remove dependencies directory
bool verbose = logger::get_verbosity() == log_verbosity::VERBOSITY_VERBOSE;
bool verbose = cforge::logger::get_verbosity() == cforge::log_verbosity::VERBOSITY_VERBOSE;
// Parse arguments
for (int i = 0; i < ctx->args.arg_count; ++i) {
for (cforge_int_t i = 0; i < ctx->args.arg_count; ++i) {
std::string arg = ctx->args.args[i];
if (arg == "--all") {
@@ -462,12 +461,12 @@ cforge_int_t cforge_cmd_clean(const cforge_context_t *ctx) {
std::vector<std::filesystem::path> build_dirs;
if (clean_all) {
logger::cleaning("all build configurations");
cforge::logger::cleaning("all build configurations");
build_dirs = find_all_build_dirs(base_build_dir);
} else {
logger::cleaning("build configuration: " +
cforge::logger::cleaning("build configuration: " +
(config_name.empty() ? "Default" : config_name));
build_dirs.push_back(get_build_dir_for_config(base_build_dir, config_name));
build_dirs.push_back(cforge::get_build_dir_for_config(base_build_dir, config_name));
}
// Clean CMake files if requested (which is now the default)
@@ -488,17 +487,17 @@ cforge_int_t cforge_cmd_clean(const cforge_context_t *ctx) {
std::string deps_dir = config.get_string("dependencies.directory", "deps");
std::filesystem::path deps_path = project_dir / deps_dir;
if (std::filesystem::exists(deps_path)) {
logger::removing(deps_path.string());
cforge::logger::removing(deps_path.string());
try {
std::filesystem::remove_all(deps_path);
logger::print_action("Removed", deps_path.string());
cforge::logger::print_action("Removed", deps_path.string());
} catch (const std::exception &e) {
logger::print_error("Failed to remove dependencies directory: " +
cforge::logger::print_error("Failed to remove dependencies directory: " +
std::string(e.what()));
all_cleaned = false;
}
} else {
logger::print_status(
cforge::logger::print_status(
"Dependencies directory does not exist, nothing to clean: " +
deps_path.string());
}
@@ -507,18 +506,18 @@ cforge_int_t cforge_cmd_clean(const cforge_context_t *ctx) {
// Regenerate CMake files if requested
if (regenerate) {
std::filesystem::path build_dir =
get_build_dir_for_config(base_build_dir, config_name);
cforge::get_build_dir_for_config(base_build_dir, config_name);
if (!regenerate_cmake_files(project_dir, build_dir, config_name, verbose)) {
logger::print_error("Failed to regenerate CMake files");
cforge::logger::print_error("Failed to regenerate CMake files");
return 1;
}
}
if (all_cleaned) {
logger::print_action("Finished", "clean");
cforge::logger::print_action("Finished", "clean");
return 0;
} else {
logger::print_error("Some directories could not be cleaned");
cforge::logger::print_error("Some directories could not be cleaned");
return 1;
}
}
@@ -9,8 +9,6 @@
#include <string>
using namespace cforge;
/**
* @brief Handle the 'deps' command - redirects to vcpkg
*
@@ -18,9 +16,9 @@ using namespace cforge;
* @return cforge_int_t Exit code (0 for success)
*/
cforge_int_t cforge_cmd_deps(const cforge_context_t *ctx) {
logger::print_action("Info",
cforge::logger::print_action("Info",
"The 'deps' command is a shorthand for 'vcpkg install'");
logger::print_action("Redirecting", "to 'vcpkg install'");
cforge::logger::print_action("Redirecting", "to 'vcpkg install'");
// Simply redirect to vcpkg install command
return cforge_cmd_vcpkg(ctx);
@@ -1,13 +1,12 @@
#include "cforge/log.hpp"
#include "core/command.h"
#include "core/commands.hpp"
#include "core/types.h"
#include <filesystem>
#include <iostream>
#include <string>
#include <vector>
using namespace cforge;
/**
* @brief Parse command line arguments into context structure
*
@@ -35,7 +34,7 @@ bool parse_command_line(int argc, char *argv[], cforge_context_t *ctx) {
// Process remaining arguments as before
if (remaining_args.empty()) {
logger::print_error("No command specified");
cforge::logger::print_error("No command specified");
return false;
}
@@ -43,7 +42,7 @@ bool parse_command_line(int argc, char *argv[], cforge_context_t *ctx) {
ctx->args.command = strdup(remaining_args[0].c_str());
// Process flags
for (size_t i = 1; i < remaining_args.size(); i++) {
for (cforge_size_t i = 1; i < remaining_args.size(); i++) {
std::string arg = remaining_args[i];
if (arg == "-c" || arg == "--config") {
@@ -59,11 +58,11 @@ bool parse_command_line(int argc, char *argv[], cforge_context_t *ctx) {
}
// Store remaining arguments
ctx->args.arg_count = static_cast<int>(remaining_args.size());
ctx->args.arg_count = static_cast<cforge_int_t>(remaining_args.size());
if (ctx->args.arg_count > 0) {
ctx->args.args = (cforge_string_t *)malloc(ctx->args.arg_count *
sizeof(cforge_string_t));
for (int i = 0; i < ctx->args.arg_count; i++) {
for (cforge_int_t i = 0; i < ctx->args.arg_count; i++) {
ctx->args.args[i] = strdup(remaining_args[i].c_str());
}
}
@@ -152,8 +151,8 @@ extern "C" cforge_int_t cforge_dispatch_command(const cforge_context_t *ctx) {
strcmp(ctx->args.command, "-h") == 0) {
return cforge_cmd_help(ctx);
} else {
logger::print_error("Unknown command: " + std::string(ctx->args.command));
logger::print_status("Run 'cforge help' for usage information");
cforge::logger::print_error("Unknown command: " + std::string(ctx->args.command));
cforge::logger::print_status("Run 'cforge help' for usage information");
return 1;
}
}
@@ -6,6 +6,8 @@
#include "cforge/log.hpp"
#include "core/commands.hpp"
#include "core/types.h"
#include "core/platform.hpp"
#include "core/process_utils.hpp"
#include "core/toml_reader.hpp"
@@ -26,23 +28,25 @@ bool is_doxygen_available() {
/**
* @brief Find Doxygen executable
* Uses platform-specific paths including Homebrew on macOS
*/
std::string find_doxygen() {
// Try common locations
#ifdef _WIN32
std::vector<std::string> paths = {
"doxygen", "C:\\Program Files\\doxygen\\bin\\doxygen.exe",
"C:\\Program Files (x86)\\doxygen\\bin\\doxygen.exe"};
#else
std::vector<std::string> paths = {"doxygen", "/usr/bin/doxygen",
"/usr/local/bin/doxygen"};
#endif
// Get platform-specific doxygen paths from platform.hpp
auto paths = cforge::platform::get_doxygen_paths();
// Also try just "doxygen" in PATH
if (cforge::is_command_available("doxygen", 5)) {
return "doxygen";
}
for (const auto &path : paths) {
if (fs::exists(path) || cforge::is_command_available(path, 5)) {
if (fs::exists(path)) {
return path;
}
}
// Fallback to just the command name
return "doxygen";
}
@@ -106,8 +110,6 @@ bool generate_doxyfile(const fs::path &project_dir,
* @brief Handle the 'doc' command for generating documentation
*/
cforge_int_t cforge_cmd_doc(const cforge_context_t *ctx) {
using namespace cforge;
fs::path project_dir = ctx->working_dir;
// Parse arguments
@@ -116,7 +118,7 @@ cforge_int_t cforge_cmd_doc(const cforge_context_t *ctx) {
bool generate_doxyfile_only = false;
std::string output_dir = "docs";
for (int i = 0; i < ctx->args.arg_count; i++) {
for (cforge_int_t i = 0; i < ctx->args.arg_count; i++) {
std::string arg = ctx->args.args[i];
if (arg == "-v" || arg == "--verbose") {
verbose = true;
@@ -133,12 +135,12 @@ cforge_int_t cforge_cmd_doc(const cforge_context_t *ctx) {
// Check for cforge.toml
fs::path config_file = project_dir / "cforge.toml";
if (!fs::exists(config_file)) {
logger::print_error("No cforge.toml found in current directory");
cforge::logger::print_error("No cforge.toml found in current directory");
return 1;
}
// Load project config
toml_reader reader;
cforge::toml_reader reader;
reader.load(config_file.string());
std::string project_name = reader.get_string("project.name", "Project");
std::string version = reader.get_string("project.version", "1.0.0");
@@ -148,15 +150,15 @@ cforge_int_t cforge_cmd_doc(const cforge_context_t *ctx) {
bool has_doxyfile = fs::exists(doxyfile_path);
if (!has_doxyfile || generate_doxyfile_only) {
logger::print_action("Generating", "Doxyfile");
cforge::logger::print_action("Generating", "Doxyfile");
if (!generate_doxyfile(project_dir, project_name, version, output_dir)) {
logger::print_error("Failed to generate Doxyfile");
cforge::logger::print_error("Failed to generate Doxyfile");
return 1;
}
logger::print_action("Created", "Doxyfile");
cforge::logger::print_action("Created", "Doxyfile");
if (generate_doxyfile_only) {
logger::print_status(
cforge::logger::print_status(
"Doxyfile generated. Edit it to customize documentation settings.");
return 0;
}
@@ -164,10 +166,10 @@ cforge_int_t cforge_cmd_doc(const cforge_context_t *ctx) {
// Check if Doxygen is available
if (!is_doxygen_available()) {
logger::print_error("Doxygen not found. Please install Doxygen:");
logger::print_plain(" Windows: choco install doxygen.install");
logger::print_plain(" macOS: brew install doxygen");
logger::print_plain(" Linux: sudo apt install doxygen");
cforge::logger::print_error("Doxygen not found. Please install Doxygen:");
cforge::logger::print_plain(" Windows: choco install doxygen.install");
cforge::logger::print_plain(" macOS: brew install doxygen");
cforge::logger::print_plain(" Linux: sudo apt install doxygen");
return 1;
}
@@ -178,12 +180,12 @@ cforge_int_t cforge_cmd_doc(const cforge_context_t *ctx) {
}
// Run Doxygen
logger::print_action("Generating", "documentation with Doxygen");
cforge::logger::print_action("Generating", "documentation with Doxygen");
std::string doxygen_cmd = find_doxygen();
std::vector<std::string> args = {doxyfile_path.string()};
auto result = execute_process(
auto result = cforge::execute_process(
doxygen_cmd, args, project_dir.string(),
[verbose](const std::string &line) {
if (verbose) {
@@ -192,28 +194,28 @@ cforge_int_t cforge_cmd_doc(const cforge_context_t *ctx) {
},
[](const std::string &line) {
if (line.find("warning") != std::string::npos) {
logger::print_warning(line);
cforge::logger::print_warning(line);
} else if (line.find("error") != std::string::npos) {
logger::print_error(line);
cforge::logger::print_error(line);
}
});
if (result.exit_code != 0) {
logger::print_error("Doxygen failed with exit code " +
cforge::logger::print_error("Doxygen failed with exit code " +
std::to_string(result.exit_code));
return 1;
}
fs::path html_index = docs_path / "html" / "index.html";
if (fs::exists(html_index)) {
logger::print_action("Generated",
cforge::logger::print_action("Generated",
"documentation at " + html_index.string());
// Open in browser if requested
if (open_docs) {
#ifdef _WIN32
std::string open_cmd = "start \"\" \"" + html_index.string() + "\"";
#elif __APPLE__
#elif defined(__APPLE__)
std::string open_cmd = "open \"" + html_index.string() + "\"";
#else
std::string open_cmd = "xdg-open \"" + html_index.string() + "\"";
@@ -221,7 +223,7 @@ cforge_int_t cforge_cmd_doc(const cforge_context_t *ctx) {
std::system(open_cmd.c_str());
}
} else {
logger::print_action("Generated", "documentation in " + docs_path.string());
cforge::logger::print_action("Generated", "documentation in " + docs_path.string());
}
return 0;
@@ -5,6 +5,7 @@
#include "cforge/log.hpp"
#include "core/commands.hpp"
#include "core/types.h"
#include "core/process_utils.hpp"
#include <filesystem>
@@ -26,7 +27,7 @@ static std::string extract_version(const std::string &output) {
static std::string get_tool_version(const std::string &command,
const std::vector<std::string> &args) {
process_result result = execute_process(command, args, "", nullptr, nullptr, 10);
cforge::process_result result = cforge::execute_process(command, args, "", nullptr, nullptr, 10);
if (result.success) {
std::string output = result.stdout_output + result.stderr_output;
return extract_version(output);
@@ -56,12 +57,10 @@ static void print_check_result(const std::string &name, bool success,
} // namespace cforge
cforge_int_t cforge_cmd_doctor(const cforge_context_t *ctx) {
using namespace cforge;
bool verbose = false;
// Parse arguments
for (int i = 0; i < ctx->args.arg_count; ++i) {
for (cforge_int_t i = 0; i < ctx->args.arg_count; ++i) {
std::string arg = ctx->args.args[i];
if (arg == "-v" || arg == "--verbose") {
verbose = true;
@@ -77,13 +76,13 @@ cforge_int_t cforge_cmd_doctor(const cforge_context_t *ctx) {
fmt::print("\nChecking environment...\n\n");
int passed = 0;
int warnings = 0;
cforge_int_t passed = 0;
cforge_int_t warnings = 0;
// Check CMake
bool cmake_ok = is_command_available("cmake", 5);
std::string cmake_ver = cmake_ok ? get_tool_version("cmake", {"--version"}) : "";
print_check_result("CMake", cmake_ok, cmake_ver,
bool cmake_ok = cforge::is_command_available("cmake", 5);
std::string cmake_ver = cmake_ok ? cforge::get_tool_version("cmake", {"--version"}) : "";
cforge::print_check_result("CMake", cmake_ok, cmake_ver,
"install from https://cmake.org/download/");
if (cmake_ok)
passed++;
@@ -91,9 +90,9 @@ cforge_int_t cforge_cmd_doctor(const cforge_context_t *ctx) {
warnings++;
// Check Ninja
bool ninja_ok = is_command_available("ninja", 5);
std::string ninja_ver = ninja_ok ? get_tool_version("ninja", {"--version"}) : "";
print_check_result("Ninja", ninja_ok, ninja_ver,
bool ninja_ok = cforge::is_command_available("ninja", 5);
std::string ninja_ver = ninja_ok ? cforge::get_tool_version("ninja", {"--version"}) : "";
cforge::print_check_result("Ninja", ninja_ok, ninja_ver,
"install with 'choco install ninja' or 'apt install ninja-build'");
if (ninja_ok)
passed++;
@@ -107,31 +106,31 @@ cforge_int_t cforge_cmd_doctor(const cforge_context_t *ctx) {
#ifdef _WIN32
// Check for MSVC or MinGW
if (is_command_available("cl", 5)) {
if (cforge::is_command_available("cl", 5)) {
compiler_ok = true;
compiler_name = "MSVC";
} else if (is_command_available("g++", 5)) {
} else if (cforge::is_command_available("g++", 5)) {
compiler_ok = true;
compiler_name = "g++ (MinGW)";
compiler_ver = get_tool_version("g++", {"--version"});
} else if (is_command_available("clang++", 5)) {
compiler_ver = cforge::get_tool_version("g++", {"--version"});
} else if (cforge::is_command_available("clang++", 5)) {
compiler_ok = true;
compiler_name = "clang++";
compiler_ver = get_tool_version("clang++", {"--version"});
compiler_ver = cforge::get_tool_version("clang++", {"--version"});
}
#else
if (is_command_available("g++", 5)) {
if (cforge::is_command_available("g++", 5)) {
compiler_ok = true;
compiler_name = "g++";
compiler_ver = get_tool_version("g++", {"--version"});
} else if (is_command_available("clang++", 5)) {
compiler_ver = cforge::get_tool_version("g++", {"--version"});
} else if (cforge::is_command_available("clang++", 5)) {
compiler_ok = true;
compiler_name = "clang++";
compiler_ver = get_tool_version("clang++", {"--version"});
compiler_ver = cforge::get_tool_version("clang++", {"--version"});
}
#endif
print_check_result(compiler_ok ? compiler_name : "C++ Compiler", compiler_ok,
cforge::print_check_result(compiler_ok ? compiler_name : "C++ Compiler", compiler_ok,
compiler_ver, "install a C++ compiler (g++, clang++, or MSVC)");
if (compiler_ok)
passed++;
@@ -139,12 +138,12 @@ cforge_int_t cforge_cmd_doctor(const cforge_context_t *ctx) {
warnings++;
// Check ccache/sccache
bool cache_ok = is_command_available("ccache", 5) ||
is_command_available("sccache", 5);
std::string cache_name = is_command_available("ccache", 5) ? "ccache" : "sccache";
bool cache_ok = cforge::is_command_available("ccache", 5) ||
cforge::is_command_available("sccache", 5);
std::string cache_name = cforge::is_command_available("ccache", 5) ? "ccache" : "sccache";
std::string cache_ver =
cache_ok ? get_tool_version(cache_name, {"--version"}) : "";
print_check_result(cache_ok ? cache_name : "ccache/sccache", cache_ok, cache_ver,
cache_ok ? cforge::get_tool_version(cache_name, {"--version"}) : "";
cforge::print_check_result(cache_ok ? cache_name : "ccache/sccache", cache_ok, cache_ver,
"install with 'choco install ccache' or 'apt install ccache'");
if (cache_ok)
passed++;
@@ -166,7 +165,7 @@ cforge_int_t cforge_cmd_doctor(const cforge_context_t *ctx) {
vcpkg_path = vcpkg_root;
}
}
print_check_result("vcpkg", vcpkg_ok, vcpkg_ok ? vcpkg_path : "",
cforge::print_check_result("vcpkg", vcpkg_ok, vcpkg_ok ? vcpkg_path : "",
"set VCPKG_ROOT environment variable");
if (vcpkg_ok)
passed++;
@@ -174,9 +173,9 @@ cforge_int_t cforge_cmd_doctor(const cforge_context_t *ctx) {
warnings++;
// Check Git
bool git_ok = is_command_available("git", 5);
std::string git_ver = git_ok ? get_tool_version("git", {"--version"}) : "";
print_check_result("Git", git_ok, git_ver,
bool git_ok = cforge::is_command_available("git", 5);
std::string git_ver = git_ok ? cforge::get_tool_version("git", {"--version"}) : "";
cforge::print_check_result("Git", git_ok, git_ver,
"install from https://git-scm.com/downloads");
if (git_ok)
passed++;
@@ -184,10 +183,10 @@ cforge_int_t cforge_cmd_doctor(const cforge_context_t *ctx) {
warnings++;
// Check clang-format
bool clang_format_ok = is_command_available("clang-format", 5);
bool clang_format_ok = cforge::is_command_available("clang-format", 5);
std::string clang_format_ver =
clang_format_ok ? get_tool_version("clang-format", {"--version"}) : "";
print_check_result("clang-format", clang_format_ok, clang_format_ver,
clang_format_ok ? cforge::get_tool_version("clang-format", {"--version"}) : "";
cforge::print_check_result("clang-format", clang_format_ok, clang_format_ver,
"install LLVM or use 'cforge install clang-format'");
if (clang_format_ok)
passed++;
@@ -195,10 +194,10 @@ cforge_int_t cforge_cmd_doctor(const cforge_context_t *ctx) {
warnings++;
// Check clang-tidy
bool clang_tidy_ok = is_command_available("clang-tidy", 5);
bool clang_tidy_ok = cforge::is_command_available("clang-tidy", 5);
std::string clang_tidy_ver =
clang_tidy_ok ? get_tool_version("clang-tidy", {"--version"}) : "";
print_check_result("clang-tidy", clang_tidy_ok, clang_tidy_ver,
clang_tidy_ok ? cforge::get_tool_version("clang-tidy", {"--version"}) : "";
cforge::print_check_result("clang-tidy", clang_tidy_ok, clang_tidy_ver,
"install LLVM or use 'cforge install clang-tidy'");
if (clang_tidy_ok)
passed++;
+716
View File
@@ -0,0 +1,716 @@
/**
* @file command_help.cpp
* @brief Implementation of the 'help' command to provide usage information
*/
#include "cforge/log.hpp"
#include "core/commands.hpp"
#include "core/constants.h"
#include <algorithm>
#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>
/**
* @brief Handle the 'help' command
*
* @param ctx Context containing parsed arguments
* @return cforge_int_t Exit code (0 for success)
*/
cforge_int_t cforge_cmd_help(const cforge_context_t *ctx) {
std::string specific_command;
// Check if a specific command was requested
if (ctx->args.args && ctx->args.args[0] && ctx->args.args[0][0] != '-') {
specific_command = ctx->args.args[0];
}
if (specific_command.empty()) {
cforge::logger::print_plain("cforge - C++ project management tool");
cforge::logger::print_plain("");
cforge::logger::print_plain("Available commands:");
cforge::logger::print_plain(" init Initialize a new project or workspace");
cforge::logger::print_plain(" build Build the project");
cforge::logger::print_plain(" clean Clean build artifacts");
cforge::logger::print_plain(" run Build and run the project");
cforge::logger::print_plain(" test Run tests");
cforge::logger::print_plain(" package Create a package for distribution");
cforge::logger::print_plain(" pack Alias for package");
cforge::logger::print_plain(" deps Manage Git dependencies");
cforge::logger::print_plain(" vcpkg Manage vcpkg dependencies");
cforge::logger::print_plain(" install Install a cforge project to the system");
cforge::logger::print_plain(" search Search for packages in the registry");
cforge::logger::print_plain(" info Show detailed package information");
cforge::logger::print_plain(" add Add a dependency to the project");
cforge::logger::print_plain(" remove Remove a dependency from the project");
cforge::logger::print_plain(" update Update cforge or packages");
cforge::logger::print_plain(" ide Generate IDE project files");
cforge::logger::print_plain(" list List dependencies or projects");
cforge::logger::print_plain(
" lock Manage dependency lock file for reproducible builds");
cforge::logger::print_plain(" fmt Format source code with clang-format");
cforge::logger::print_plain(" lint Run static analysis with clang-tidy");
cforge::logger::print_plain(" watch Watch for changes and auto-rebuild");
cforge::logger::print_plain(" completions Generate shell completion scripts");
cforge::logger::print_plain(" doc Generate documentation with Doxygen");
cforge::logger::print_plain(" tree Visualize dependency tree");
cforge::logger::print_plain(" new Create files from templates");
cforge::logger::print_plain(" bench Run benchmarks");
cforge::logger::print_plain(" version Show version information");
cforge::logger::print_plain(" help Show help for a specific command");
cforge::logger::print_plain("");
cforge::logger::print_plain("Usage: cforge <command> [options]");
cforge::logger::print_plain("");
cforge::logger::print_plain("For more information on a specific command, run "
"'cforge help <command>'");
} else if (specific_command == "init") {
cforge::logger::print_plain("cforge init - Initialize a new C++ project");
cforge::logger::print_plain("");
cforge::logger::print_plain("Usage: cforge init [name] [options]");
cforge::logger::print_plain("");
cforge::logger::print_plain("Arguments:");
cforge::logger::print_plain(
" name Project name (default: current directory name)");
cforge::logger::print_plain("");
cforge::logger::print_plain("Options:");
cforge::logger::print_plain(" --std=c++XX Set C++ standard "
"(11, 14, 17, 20) (default: 17)");
cforge::logger::print_plain(" --git Initialize git "
"repository (disabled by default)");
cforge::logger::print_plain(" --workspace [name] Create a new "
"workspace with the given name");
cforge::logger::print_plain(
" --projects [name1] [name2]... Create multiple projects");
cforge::logger::print_plain(" --template [name] Use specific project "
"template (app, lib, header-only)");
cforge::logger::print_plain(" --with-tests Add test "
"infrastructure to the project");
cforge::logger::print_plain(
" --with-git Initialize git repository");
cforge::logger::print_plain(
" --type=[type] Set project binary type (executable, "
"shared_lib, static_lib, header_only)");
cforge::logger::print_plain(" -v, --verbose Show verbose output");
cforge::logger::print_plain("");
cforge::logger::print_plain("Configuration:");
cforge::logger::print_plain(" Projects are initialized with a cforge.toml file "
"containing project settings:");
cforge::logger::print_plain(" - Project metadata (name, version, C++ standard)");
cforge::logger::print_plain(
" - Build configuration (build type, source directories)");
cforge::logger::print_plain(" - Packaging settings");
cforge::logger::print_plain(" - Dependency management");
cforge::logger::print_plain(" - Test configuration");
cforge::logger::print_plain("");
cforge::logger::print_plain(" Run 'cforge help config' for detailed "
"configuration format information");
cforge::logger::print_plain("");
cforge::logger::print_plain("Examples:");
cforge::logger::print_plain(" cforge init Initialize project "
"in current directory");
cforge::logger::print_plain(" cforge init my_project Create new project "
"in a new directory");
cforge::logger::print_plain(
" cforge init --type=shared_lib Create a shared library project");
cforge::logger::print_plain(
" cforge init --projects a b c Create multiple standalone projects");
cforge::logger::print_plain(" cforge init --workspace myws Create a workspace");
cforge::logger::print_plain(" cforge init --workspace myws --projects app lib "
"Create workspace with projects");
cforge::logger::print_plain("");
cforge::logger::print_plain("Notes:");
cforge::logger::print_plain(
" - Hyphens in project names are replaced with underscores in code");
cforge::logger::print_plain(" - If no name is provided, the current directory "
"name is used as the project name");
} else if (specific_command == "build") {
cforge::logger::print_plain("cforge build - Build the project");
cforge::logger::print_plain("");
cforge::logger::print_plain("Usage: cforge build [options]");
cforge::logger::print_plain("");
cforge::logger::print_plain("Options:");
cforge::logger::print_plain(" -c, --config <config> Set build configuration "
"(Debug, Release, etc.)");
cforge::logger::print_plain(
" -j, --jobs <n> Set number of parallel build jobs");
cforge::logger::print_plain(" -v, --verbose Enable verbose output");
cforge::logger::print_plain(" -t, --target <target> Build specific target");
cforge::logger::print_plain(
" -p, --project <project> Build specific project in workspace");
cforge::logger::print_plain(
" --gen-workspace-cmake Generate a workspace-level CMakeLists.txt");
cforge::logger::print_plain(" --force-regenerate Force regeneration of "
"CMakeLists.txt and clean build");
cforge::logger::print_plain(
" --skip-deps, --no-deps Skip updating Git dependencies");
cforge::logger::print_plain(
" -P, --profile <name> Use cross-compilation profile");
cforge::logger::print_plain("");
cforge::logger::print_plain("Examples:");
cforge::logger::print_plain(
" cforge build Build with default configuration (Debug)");
cforge::logger::print_plain(
" cforge build -c Release Build with Release configuration");
cforge::logger::print_plain(
" cforge build -j 4 Build with 4 parallel jobs");
cforge::logger::print_plain(
" cforge build -p mylib Build only 'mylib' project in workspace");
cforge::logger::print_plain(" cforge build --gen-workspace-cmake Generate a "
"workspace CMakeLists.txt without building");
cforge::logger::print_plain(" cforge build --force-regenerate Rebuild with "
"fresh configuration");
cforge::logger::print_plain(" cforge build --skip-deps Build without "
"updating Git dependencies");
cforge::logger::print_plain(" cforge build --profile android-arm64 Build using "
"cross-compilation profile");
} else if (specific_command == "clean") {
cforge::logger::print_plain("cforge clean - Clean build artifacts");
cforge::logger::print_plain("");
cforge::logger::print_plain("Usage: cforge clean [options]");
cforge::logger::print_plain("");
cforge::logger::print_plain("Options:");
cforge::logger::print_plain(
" -c, --config <config> Clean specific configuration");
cforge::logger::print_plain(" --all Clean all configurations");
cforge::logger::print_plain(
" --cmake-files Also clean CMake temporary files");
cforge::logger::print_plain(
" --regenerate Regenerate CMake files after cleaning");
cforge::logger::print_plain(
" --deep Remove dependencies directory (deep clean)");
cforge::logger::print_plain(" -v, --verbose Show verbose output");
} else if (specific_command == "run") {
cforge::logger::print_plain("cforge run - Build and run the project");
cforge::logger::print_plain("");
cforge::logger::print_plain("Usage: cforge run [options] [-- <app arguments>]");
cforge::logger::print_plain("");
cforge::logger::print_plain("Options:");
cforge::logger::print_plain(
" -c, --config <config> Build configuration (Debug, Release, etc.)");
cforge::logger::print_plain(
" -p, --project <name> Run specific project in workspace");
cforge::logger::print_plain(
" --no-build Skip building before running");
cforge::logger::print_plain(" -v, --verbose Show verbose output");
cforge::logger::print_plain("");
cforge::logger::print_plain("Examples:");
cforge::logger::print_plain(
" cforge run Build and run with default config");
cforge::logger::print_plain(
" cforge run -c Debug Build and run with Debug config");
cforge::logger::print_plain(
" cforge run -- arg1 arg2 Pass arguments to the executable");
cforge::logger::print_plain(
" cforge run -p app1 -- arg1 Run 'app1' from workspace with args");
cforge::logger::print_plain("");
cforge::logger::print_plain("Arguments after -- are passed to the application");
} else if (specific_command == "test") {
cforge::logger::print_plain("cforge test - Build and run unit tests");
cforge::logger::print_plain("");
cforge::logger::print_plain(
"Usage: cforge test [options] [<category> [<test1> <test2> ...]] [--]");
cforge::logger::print_plain("");
cforge::logger::print_plain("Options:");
cforge::logger::print_plain(" -c, --config <config> Build configuration "
"(Debug, Release, etc.)");
cforge::logger::print_plain(
" -v, --verbose Show verbose build & test output");
cforge::logger::print_plain("");
cforge::logger::print_plain("Positional arguments:");
cforge::logger::print_plain(
" <category> Optional test category to run");
cforge::logger::print_plain(
" <test_name> ... Optional test names under the category");
cforge::logger::print_plain("");
cforge::logger::print_plain("Examples:");
cforge::logger::print_plain(" cforge test");
cforge::logger::print_plain(" cforge test Math");
cforge::logger::print_plain(" cforge test -c Release Math Add");
cforge::logger::print_plain(" cforge test -c Release -- Math Add");
cforge::logger::print_plain("");
cforge::logger::print_plain(
"Use `--` to explicitly separate CForge flags from test filters");
} else if (specific_command == "package") {
cforge::logger::print_plain("cforge package - Create distributable packages");
cforge::logger::print_plain("");
cforge::logger::print_plain("Usage: cforge package [options]");
cforge::logger::print_plain("");
cforge::logger::print_plain("Options:");
cforge::logger::print_plain(" -c, --config <name> Specify the build "
"configuration (Debug/Release)");
cforge::logger::print_plain(" -p, --project <name> In a workspace, specify "
"which project to package");
cforge::logger::print_plain(
" -t, --type <generator> Specify the package generator to use");
cforge::logger::print_plain(" --no-build Skip building the project "
"before packaging");
cforge::logger::print_plain(" -v, --verbose Enable verbose output");
cforge::logger::print_plain("");
cforge::logger::print_plain("Examples:");
cforge::logger::print_plain(" cforge package Package the "
"current project with default settings");
cforge::logger::print_plain(" cforge package -c Release Package using the "
"Release configuration");
cforge::logger::print_plain(" cforge package --no-build Skip building and "
"package with existing binaries");
cforge::logger::print_plain(" cforge package -t ZIP Package using the "
"ZIP generator only");
cforge::logger::print_plain(" cforge package -p mylib In a workspace, "
"package the 'mylib' project");
cforge::logger::print_plain("");
cforge::logger::print_plain("Notes:");
cforge::logger::print_plain(" - When run in a workspace:");
cforge::logger::print_plain(" - By default, packages the main project "
"specified in workspace.toml");
cforge::logger::print_plain(" - Use -p to package a specific project");
cforge::logger::print_plain(" - Set package.all_projects=true in "
"workspace.toml to package all projects");
cforge::logger::print_plain("");
cforge::logger::print_plain(
" - Package settings can be configured in cforge.toml:");
cforge::logger::print_plain(" [package]");
cforge::logger::print_plain(
" enabled = true # Enable/disable packaging");
cforge::logger::print_plain(
" generators = [\"ZIP\", \"TGZ\"] # List of generators to use");
cforge::logger::print_plain("");
cforge::logger::print_plain(" - Workspace package settings in workspace.toml:");
cforge::logger::print_plain(" [package]");
cforge::logger::print_plain(
" all_projects = false # Whether to package all projects");
cforge::logger::print_plain(" generators = [\"ZIP\"] # Default "
"generators for all projects");
} else if (specific_command == "add") {
cforge::logger::print_plain("cforge add - Add a dependency");
cforge::logger::print_plain("");
cforge::logger::print_plain("Usage: cforge add <package>[@version] [options]");
cforge::logger::print_plain("");
cforge::logger::print_plain("Arguments:");
cforge::logger::print_plain(" package Package name (e.g., fmt, spdlog)");
cforge::logger::print_plain(" @version Optional version (e.g., @11.1.4, @1.*)");
cforge::logger::print_plain("");
cforge::logger::print_plain("Source Options (default: registry):");
cforge::logger::print_plain(" --git <url> Add as Git dependency");
cforge::logger::print_plain(" --tag <tag> Git tag (with --git)");
cforge::logger::print_plain(" --branch <name> Git branch (with --git)");
cforge::logger::print_plain(" --vcpkg Add as vcpkg package");
cforge::logger::print_plain(" --index Add from registry (default)");
cforge::logger::print_plain("");
cforge::logger::print_plain("Other Options:");
cforge::logger::print_plain(" --features <f> Comma-separated features to enable");
cforge::logger::print_plain(" --header-only Mark as header-only library");
cforge::logger::print_plain(" -v, --verbose Show verbose output");
cforge::logger::print_plain("");
cforge::logger::print_plain("Examples:");
cforge::logger::print_plain(" cforge add fmt Add fmt from registry");
cforge::logger::print_plain(" cforge add fmt@11.1.4 Add specific version");
cforge::logger::print_plain(" cforge add spdlog --features async");
cforge::logger::print_plain(" cforge add boost --vcpkg Add from vcpkg");
cforge::logger::print_plain(" cforge add mylib --git https://github.com/user/mylib --tag v1.0");
} else if (specific_command == "remove") {
cforge::logger::print_plain("cforge remove - Remove a dependency");
cforge::logger::print_plain("");
cforge::logger::print_plain("Usage: cforge remove <package> [options]");
cforge::logger::print_plain("");
cforge::logger::print_plain("Arguments:");
cforge::logger::print_plain(" package Package to remove");
cforge::logger::print_plain("");
cforge::logger::print_plain("Options:");
cforge::logger::print_plain(" -v, --verbose Show verbose output");
} else if (specific_command == "search") {
cforge::logger::print_plain("cforge search - Search for packages in the registry");
cforge::logger::print_plain("");
cforge::logger::print_plain("Usage: cforge search <query> [options]");
cforge::logger::print_plain("");
cforge::logger::print_plain("Arguments:");
cforge::logger::print_plain(" query Search term to find packages");
cforge::logger::print_plain("");
cforge::logger::print_plain("Options:");
cforge::logger::print_plain(" --limit <n> Maximum results to show (default: 20)");
cforge::logger::print_plain(" --update Force update the package index first");
cforge::logger::print_plain(" -v, --verbose Show verbose output");
cforge::logger::print_plain("");
cforge::logger::print_plain("Examples:");
cforge::logger::print_plain(" cforge search json Find JSON-related packages");
cforge::logger::print_plain(" cforge search logging Find logging libraries");
cforge::logger::print_plain(" cforge search --limit 5 ui Show top 5 UI packages");
} else if (specific_command == "info") {
cforge::logger::print_plain("cforge info - Show detailed package information");
cforge::logger::print_plain("");
cforge::logger::print_plain("Usage: cforge info <package> [options]");
cforge::logger::print_plain("");
cforge::logger::print_plain("Arguments:");
cforge::logger::print_plain(" package Name of package to get info for");
cforge::logger::print_plain("");
cforge::logger::print_plain("Options:");
cforge::logger::print_plain(" --versions Show all available versions");
cforge::logger::print_plain(" --update Force update the package index first");
cforge::logger::print_plain(" -v, --verbose Show verbose output");
cforge::logger::print_plain("");
cforge::logger::print_plain("Examples:");
cforge::logger::print_plain(" cforge info fmt Show fmt package details");
cforge::logger::print_plain(" cforge info spdlog --versions Show all spdlog versions");
} else if (specific_command == "update") {
cforge::logger::print_plain("cforge update - Update cforge or packages");
cforge::logger::print_plain("");
cforge::logger::print_plain("Usage: cforge update <--self|--packages> [options]");
cforge::logger::print_plain("");
cforge::logger::print_plain("Options:");
cforge::logger::print_plain(" -s, --self Update cforge itself to latest version");
cforge::logger::print_plain(" -p, --packages Update the package registry index");
cforge::logger::print_plain(" -v, --verbose Show verbose output");
cforge::logger::print_plain("");
cforge::logger::print_plain("Examples:");
cforge::logger::print_plain(" cforge update --self Update cforge to latest");
cforge::logger::print_plain(" cforge update --packages Refresh package registry");
cforge::logger::print_plain("");
cforge::logger::print_plain("Note: You must specify either --self or --packages");
} else if (specific_command == "vcpkg") {
cforge::logger::print_plain("cforge vcpkg - Run vcpkg commands");
cforge::logger::print_plain("");
cforge::logger::print_plain("Usage: cforge vcpkg <args...>");
cforge::logger::print_plain("");
cforge::logger::print_plain("Arguments:");
cforge::logger::print_plain(" args... Arguments to pass to vcpkg");
} else if (specific_command == "version") {
cforge::logger::print_plain("cforge version - Show cforge version");
cforge::logger::print_plain("");
cforge::logger::print_plain("Usage: cforge version");
} else if (specific_command == "help") {
cforge::logger::print_plain("cforge help - Show help information");
cforge::logger::print_plain("");
cforge::logger::print_plain("Usage: cforge help [command]");
cforge::logger::print_plain("");
cforge::logger::print_plain("Arguments:");
cforge::logger::print_plain(" command Command to show help for");
} else if (specific_command == "install") {
cforge::logger::print_plain(
"cforge install - Install a cforge project to the system");
cforge::logger::print_plain("");
cforge::logger::print_plain("Usage: cforge install [options]");
cforge::logger::print_plain("");
cforge::logger::print_plain("Options:");
cforge::logger::print_plain(" -c, --config <config> Build configuration to "
"install (Debug, Release)");
cforge::logger::print_plain(" --from <path|URL> Source directory or Git "
"URL to install from");
cforge::logger::print_plain(
" --to <path> Target install directory");
cforge::logger::print_plain(
" --add-to-path Add the install/bin directory to PATH");
cforge::logger::print_plain(
" -n, --name <name> Override project name for installation");
cforge::logger::print_plain(" --env <VAR> Environment variable to "
"set to install path");
cforge::logger::print_plain(" -v, --verbose Show verbose output");
cforge::logger::print_plain("");
cforge::logger::print_plain("Examples:");
cforge::logger::print_plain(" cforge install Install current "
"project to default location");
cforge::logger::print_plain(
" cforge install --to C:/Apps/Proj Install to a custom path");
cforge::logger::print_plain(
" cforge install --from https://github.com/org/repo.git");
cforge::logger::print_plain(
" cforge install --add-to-path Add install to PATH");
} else if (specific_command == "ide") {
cforge::logger::print_plain("cforge ide - Generate IDE project files");
cforge::logger::print_plain("");
cforge::logger::print_plain("Usage: cforge ide [options]");
cforge::logger::print_plain("");
cforge::logger::print_plain("Options:");
cforge::logger::print_plain(" -p, --project <name> Generate files for "
"specified project in workspace");
cforge::logger::print_plain(" -v, --verbose Show verbose output");
cforge::logger::print_plain("");
} else if (specific_command == "list") {
cforge::logger::print_plain("cforge list - List projects or dependencies");
cforge::logger::print_plain("");
cforge::logger::print_plain("Usage: cforge list [options]");
cforge::logger::print_plain("");
cforge::logger::print_plain("Options:");
cforge::logger::print_plain(" -p, --project <name> List dependencies for a "
"specific project");
cforge::logger::print_plain(" -v, --verbose Show verbose output");
cforge::logger::print_plain("");
} else if (specific_command == "deps") {
cforge::logger::print_plain("cforge deps - Manage Git dependencies");
cforge::logger::print_plain("");
cforge::logger::print_plain("Usage: cforge deps <command> [options]");
cforge::logger::print_plain("");
cforge::logger::print_plain("Commands:");
cforge::logger::print_plain(" fetch Fetch updates for all Git dependencies");
cforge::logger::print_plain(
" checkout Checkout each dependency to its configured ref");
cforge::logger::print_plain(" list Show configured Git dependencies");
cforge::logger::print_plain("");
cforge::logger::print_plain("Options:");
cforge::logger::print_plain(" -v, --verbose Show verbose output");
cforge::logger::print_plain("");
} else if (specific_command == "pack") {
cforge::logger::print_plain("cforge pack - Alias for 'package'");
cforge::logger::print_plain("");
cforge::logger::print_plain("Usage: cforge pack [options]");
cforge::logger::print_plain("");
cforge::logger::print_plain("See 'cforge help package' for details");
} else if (specific_command == "lock") {
cforge::logger::print_plain("cforge lock - Manage dependency lock file");
cforge::logger::print_plain("");
cforge::logger::print_plain("Usage: cforge lock [options]");
cforge::logger::print_plain("");
cforge::logger::print_plain("Options:");
cforge::logger::print_plain(" --verify, -v Verify dependencies match lock file");
cforge::logger::print_plain(" --clean, -c Remove the lock file");
cforge::logger::print_plain(
" --force, -f Force regeneration even if lock exists");
cforge::logger::print_plain(" --help, -h Show this help message");
cforge::logger::print_plain("");
cforge::logger::print_plain(
"The lock file (cforge.lock) ensures reproducible builds by");
cforge::logger::print_plain(
"tracking exact versions (commit hashes) of all dependencies.");
cforge::logger::print_plain("");
cforge::logger::print_plain("Examples:");
cforge::logger::print_plain(" cforge lock Generate/update lock file");
cforge::logger::print_plain(
" cforge lock --verify Check if deps match lock file");
cforge::logger::print_plain(" cforge lock --force Regenerate lock file");
cforge::logger::print_plain(" cforge lock --clean Remove lock file");
cforge::logger::print_plain("");
cforge::logger::print_plain("Best practices:");
cforge::logger::print_plain(" 1. Commit cforge.lock to version control");
cforge::logger::print_plain(" 2. Run 'cforge lock --verify' in CI pipelines");
cforge::logger::print_plain(
" 3. Run 'cforge lock' after adding/updating dependencies");
} else if (specific_command == "fmt" || specific_command == "format") {
cforge::logger::print_plain("cforge fmt - Format source code with clang-format");
cforge::logger::print_plain("");
cforge::logger::print_plain("Usage: cforge fmt [options]");
cforge::logger::print_plain("");
cforge::logger::print_plain("Options:");
cforge::logger::print_plain(
" --check Check formatting without modifying files");
cforge::logger::print_plain(" --diff Show diff of formatting changes");
cforge::logger::print_plain(" --style <style> Use specific style (llvm, google, "
"chromium, mozilla, webkit)");
cforge::logger::print_plain(" -v, --verbose Show verbose output");
cforge::logger::print_plain("");
cforge::logger::print_plain("Examples:");
cforge::logger::print_plain(
" cforge fmt Format all source files in place");
cforge::logger::print_plain(
" cforge fmt --check Check if files need formatting");
cforge::logger::print_plain(" cforge fmt --diff Show what would change");
cforge::logger::print_plain(" cforge fmt --style=google Use Google C++ style");
cforge::logger::print_plain("");
cforge::logger::print_plain("Notes:");
cforge::logger::print_plain(" - Requires clang-format to be installed");
cforge::logger::print_plain(
" - Uses .clang-format file if present, otherwise uses --style");
cforge::logger::print_plain(
" - Formats .cpp, .cc, .cxx, .c, .hpp, .hxx, .h files");
} else if (specific_command == "lint" || specific_command == "check") {
cforge::logger::print_plain("cforge lint - Run static analysis with clang-tidy");
cforge::logger::print_plain("");
cforge::logger::print_plain("Usage: cforge lint [options]");
cforge::logger::print_plain("");
cforge::logger::print_plain("Options:");
cforge::logger::print_plain(
" --fix Apply suggested fixes automatically");
cforge::logger::print_plain(" --checks <list> Specify checks to run (e.g., "
"'modernize-*,bugprone-*')");
cforge::logger::print_plain(
" -c, --config <cfg> Build configuration for compile_commands.json");
cforge::logger::print_plain(" -v, --verbose Show verbose output");
cforge::logger::print_plain("");
cforge::logger::print_plain("Examples:");
cforge::logger::print_plain(" cforge lint Run all enabled checks");
cforge::logger::print_plain(
" cforge lint --fix Run and apply automatic fixes");
cforge::logger::print_plain(
" cforge lint --checks='modernize-*' Run only modernize checks");
cforge::logger::print_plain("");
cforge::logger::print_plain("Notes:");
cforge::logger::print_plain(" - Requires clang-tidy to be installed");
cforge::logger::print_plain(" - Uses .clang-tidy file if present");
cforge::logger::print_plain(
" - Requires compile_commands.json (generated during build)");
} else if (specific_command == "watch") {
cforge::logger::print_plain("cforge watch - Watch for changes and auto-rebuild");
cforge::logger::print_plain("");
cforge::logger::print_plain("Usage: cforge watch [options]");
cforge::logger::print_plain("");
cforge::logger::print_plain("Options:");
cforge::logger::print_plain(
" -c, --config <cfg> Build configuration (Debug, Release)");
cforge::logger::print_plain(
" --run, -r Run the executable after successful build");
cforge::logger::print_plain(
" --interval <ms> Poll interval in milliseconds (default: 500)");
cforge::logger::print_plain(" --release Use Release configuration");
cforge::logger::print_plain(" --debug Use Debug configuration");
cforge::logger::print_plain(" -v, --verbose Show verbose output");
cforge::logger::print_plain("");
cforge::logger::print_plain("Examples:");
cforge::logger::print_plain(
" cforge watch Watch and rebuild on changes");
cforge::logger::print_plain(
" cforge watch --run Watch, rebuild, and run executable");
cforge::logger::print_plain(
" cforge watch -c Release Watch with Release configuration");
cforge::logger::print_plain("");
cforge::logger::print_plain("Notes:");
cforge::logger::print_plain(
" - Watches .cpp, .cc, .cxx, .c, .hpp, .hxx, .h, .toml files");
cforge::logger::print_plain(
" - Ignores build/, .git/, deps/, vendor/ directories");
cforge::logger::print_plain(" - Press Ctrl+C to stop watching");
} else if (specific_command == "completions") {
cforge::logger::print_plain(
"cforge completions - Generate shell completion scripts");
cforge::logger::print_plain("");
cforge::logger::print_plain("Usage: cforge completions <shell>");
cforge::logger::print_plain("");
cforge::logger::print_plain("Shells:");
cforge::logger::print_plain(" bash Generate bash completions");
cforge::logger::print_plain(" zsh Generate zsh completions");
cforge::logger::print_plain(" powershell Generate PowerShell completions");
cforge::logger::print_plain(" fish Generate fish completions");
cforge::logger::print_plain("");
cforge::logger::print_plain("Examples:");
cforge::logger::print_plain(" cforge completions bash >> ~/.bashrc");
cforge::logger::print_plain(" cforge completions zsh >> ~/.zshrc");
cforge::logger::print_plain(" cforge completions powershell >> $PROFILE");
cforge::logger::print_plain(
" cforge completions fish > ~/.config/fish/completions/cforge.fish");
} else if (specific_command == "doc" || specific_command == "docs") {
cforge::logger::print_plain("cforge doc - Generate documentation with Doxygen");
cforge::logger::print_plain("");
cforge::logger::print_plain("Usage: cforge doc [options]");
cforge::logger::print_plain("");
cforge::logger::print_plain("Options:");
cforge::logger::print_plain(
" --init Generate Doxyfile without building docs");
cforge::logger::print_plain(" --open Open generated docs in browser");
cforge::logger::print_plain(" -o, --output Output directory (default: docs)");
cforge::logger::print_plain(" -v, --verbose Show verbose output");
cforge::logger::print_plain("");
cforge::logger::print_plain("Examples:");
cforge::logger::print_plain(" cforge doc Generate documentation");
cforge::logger::print_plain(
" cforge doc --init Create Doxyfile for customization");
cforge::logger::print_plain(
" cforge doc --open Generate and open in browser");
cforge::logger::print_plain(
" cforge doc -o api-docs Output to api-docs/ directory");
cforge::logger::print_plain("");
cforge::logger::print_plain("Notes:");
cforge::logger::print_plain(" - Requires Doxygen to be installed");
cforge::logger::print_plain(" - Creates Doxyfile if not present");
cforge::logger::print_plain(
" - Edit Doxyfile to customize documentation settings");
} else if (specific_command == "tree") {
cforge::logger::print_plain("cforge tree - Visualize dependency tree");
cforge::logger::print_plain("");
cforge::logger::print_plain("Usage: cforge tree [options]");
cforge::logger::print_plain("");
cforge::logger::print_plain("Options:");
cforge::logger::print_plain(
" -a, --all Show all dependencies including transitive");
cforge::logger::print_plain(
" -d, --depth <n> Maximum depth to display (default: 10)");
cforge::logger::print_plain(" -i, --inverted Show inverted tree (dependents)");
cforge::logger::print_plain("");
cforge::logger::print_plain("Examples:");
cforge::logger::print_plain(" cforge tree Show dependency tree");
cforge::logger::print_plain(" cforge tree -d 2 Limit to 2 levels deep");
cforge::logger::print_plain(
" cforge tree --all Show all transitive dependencies");
cforge::logger::print_plain("");
cforge::logger::print_plain("Output:");
cforge::logger::print_plain(" Dependencies are color-coded by type:");
cforge::logger::print_plain(" - Cyan: Git dependencies");
cforge::logger::print_plain(" - Magenta: vcpkg dependencies");
cforge::logger::print_plain(" - Yellow: System dependencies");
cforge::logger::print_plain(" - Green: Project dependencies (workspace)");
} else if (specific_command == "new") {
cforge::logger::print_plain("cforge new - Create files from templates");
cforge::logger::print_plain("");
cforge::logger::print_plain("Usage: cforge new <template> <name> [options]");
cforge::logger::print_plain("");
cforge::logger::print_plain("Templates:");
cforge::logger::print_plain(
" class Create a class with header and source files");
cforge::logger::print_plain(" header Create a header-only file");
cforge::logger::print_plain(" struct Create a struct header file");
cforge::logger::print_plain(" interface Create an interface (abstract class)");
cforge::logger::print_plain(" test Create a test file");
cforge::logger::print_plain(" main Create a main.cpp file");
cforge::logger::print_plain("");
cforge::logger::print_plain("Options:");
cforge::logger::print_plain(" -n, --namespace <name> Wrap in namespace");
cforge::logger::print_plain(" -o, --output <dir> Output directory");
cforge::logger::print_plain(" -f, --force Overwrite existing files");
cforge::logger::print_plain("");
cforge::logger::print_plain("Examples:");
cforge::logger::print_plain(" cforge new class MyClass Create "
"MyClass.hpp and MyClass.cpp");
cforge::logger::print_plain(
" cforge new class MyClass -n myproj With namespace 'myproj'");
cforge::logger::print_plain(
" cforge new header utils Create utils.hpp");
cforge::logger::print_plain(
" cforge new interface IService Create IService interface");
cforge::logger::print_plain(
" cforge new test MyClass Create test_my_class.cpp");
cforge::logger::print_plain("");
cforge::logger::print_plain("Notes:");
cforge::logger::print_plain(" - Headers go to include/, sources go to src/");
cforge::logger::print_plain(" - Tests go to tests/");
cforge::logger::print_plain(" - Names are converted to appropriate case");
} else if (specific_command == "bench" || specific_command == "benchmark") {
cforge::logger::print_plain("cforge bench - Run benchmarks");
cforge::logger::print_plain("");
cforge::logger::print_plain("Usage: cforge bench [options] [benchmark-name]");
cforge::logger::print_plain("");
cforge::logger::print_plain("Options:");
cforge::logger::print_plain(
" -c, --config <cfg> Build configuration (default: Release)");
cforge::logger::print_plain(" --no-build Skip building before running");
cforge::logger::print_plain(
" --filter <pattern> Run only benchmarks matching pattern");
cforge::logger::print_plain(" --json Output in JSON format");
cforge::logger::print_plain(" --csv Output in CSV format");
cforge::logger::print_plain(" -v, --verbose Show verbose output");
cforge::logger::print_plain("");
cforge::logger::print_plain("Examples:");
cforge::logger::print_plain(
" cforge bench Run all benchmarks");
cforge::logger::print_plain(
" cforge bench --filter 'BM_Sort' Run only Sort benchmarks");
cforge::logger::print_plain(
" cforge bench --no-build Run without rebuilding");
cforge::logger::print_plain(" cforge bench --json > results.json");
cforge::logger::print_plain("");
cforge::logger::print_plain("Configuration (cforge.toml):");
cforge::logger::print_plain(" [benchmark]");
cforge::logger::print_plain(
" directory = \"bench\" # Benchmark source directory");
cforge::logger::print_plain(
" target = \"my_benchmarks\" # Specific target to run");
cforge::logger::print_plain("");
cforge::logger::print_plain("Notes:");
cforge::logger::print_plain(" - Benchmarks run in Release mode by default");
cforge::logger::print_plain(" - Supports Google Benchmark output format");
cforge::logger::print_plain(
" - Look for executables with 'bench' or 'benchmark' in name");
} else {
cforge::logger::print_error("Unknown command: " + specific_command);
cforge::logger::print_plain("Run 'cforge help' for a list of available commands");
return 1;
}
return 0;
}
@@ -6,6 +6,7 @@
#include "cforge/log.hpp"
#include "core/commands.hpp"
#include "core/constants.h"
#include "core/types.h"
#include "core/process_utils.hpp"
#include "core/toml_reader.hpp"
#include "core/workspace.hpp"
@@ -21,8 +22,6 @@
#include <string>
#include <vector>
using namespace cforge;
/**
* @brief Generate Visual Studio project files using CMake
*
@@ -34,14 +33,14 @@ using namespace cforge;
[[maybe_unused]] static bool generate_vs_project(const std::filesystem::path &project_dir,
const std::filesystem::path &build_dir,
bool verbose) {
logger::print_action("Generating", "Visual Studio project files");
cforge::logger::print_action("Generating", "Visual Studio project files");
// Create build directory if it doesn't exist
if (!std::filesystem::exists(build_dir)) {
try {
std::filesystem::create_directories(build_dir);
} catch (const std::exception &ex) {
logger::print_error("Failed to create build directory: " +
cforge::logger::print_error("Failed to create build directory: " +
std::string(ex.what()));
return false;
}
@@ -52,14 +51,14 @@ using namespace cforge;
"-B", build_dir.string(), "-S", project_dir.string(),
"-G", "Visual Studio 17 2022", "-A", "x64"};
bool success = execute_tool("cmake", cmake_args, "", "CMake", verbose);
bool success = cforge::execute_tool("cmake", cmake_args, "", "CMake", verbose);
if (success) {
logger::generated("Visual Studio project files");
logger::print_status("Open " + build_dir.string() +
cforge::logger::generated("Visual Studio project files");
cforge::logger::print_status("Open " + build_dir.string() +
"/*.sln to start working with the project");
} else {
logger::print_error("Failed to generate Visual Studio project files");
cforge::logger::print_error("Failed to generate Visual Studio project files");
}
return success;
@@ -77,14 +76,14 @@ static bool
generate_codeblocks_project(const std::filesystem::path &project_dir,
const std::filesystem::path &build_dir,
bool verbose) {
logger::print_action("Generating", "CodeBlocks project files");
cforge::logger::print_action("Generating", "CodeBlocks project files");
// Create build directory if it doesn't exist
if (!std::filesystem::exists(build_dir)) {
try {
std::filesystem::create_directories(build_dir);
} catch (const std::exception &ex) {
logger::print_error("Failed to create build directory: " +
cforge::logger::print_error("Failed to create build directory: " +
std::string(ex.what()));
return false;
}
@@ -95,14 +94,14 @@ generate_codeblocks_project(const std::filesystem::path &project_dir,
"-S", project_dir.string(),
"-G", "CodeBlocks - Ninja"};
bool success = execute_tool("cmake", cmake_args, "", "CMake", verbose);
bool success = cforge::execute_tool("cmake", cmake_args, "", "CMake", verbose);
if (success) {
logger::generated("CodeBlocks project files");
logger::print_status("Open " + build_dir.string() +
cforge::logger::generated("CodeBlocks project files");
cforge::logger::print_status("Open " + build_dir.string() +
"/*.cbp to start working with the project");
} else {
logger::print_error("Failed to generate CodeBlocks project files");
cforge::logger::print_error("Failed to generate CodeBlocks project files");
}
return success;
@@ -119,14 +118,14 @@ generate_codeblocks_project(const std::filesystem::path &project_dir,
static bool generate_xcode_project(const std::filesystem::path &project_dir,
const std::filesystem::path &build_dir,
bool verbose) {
logger::print_action("Generating", "Xcode project files");
cforge::logger::print_action("Generating", "Xcode project files");
// Create build directory if it doesn't exist
if (!std::filesystem::exists(build_dir)) {
try {
std::filesystem::create_directories(build_dir);
} catch (const std::exception &ex) {
logger::print_error("Failed to create build directory: " +
cforge::logger::print_error("Failed to create build directory: " +
std::string(ex.what()));
return false;
}
@@ -136,14 +135,14 @@ static bool generate_xcode_project(const std::filesystem::path &project_dir,
std::vector<std::string> cmake_args = {
"-B", build_dir.string(), "-S", project_dir.string(), "-G", "Xcode"};
bool success = execute_tool("cmake", cmake_args, "", "CMake", verbose);
bool success = cforge::execute_tool("cmake", cmake_args, "", "CMake", verbose);
if (success) {
logger::generated("Xcode project files");
logger::print_status("Open " + build_dir.string() +
cforge::logger::generated("Xcode project files");
cforge::logger::print_status("Open " + build_dir.string() +
"/*.xcodeproj to start working with the project");
} else {
logger::print_error("Failed to generate Xcode project files");
cforge::logger::print_error("Failed to generate Xcode project files");
}
return success;
@@ -160,14 +159,14 @@ static bool generate_xcode_project(const std::filesystem::path &project_dir,
static bool generate_clion_project(const std::filesystem::path &project_dir,
const std::filesystem::path &build_dir,
bool verbose) {
logger::print_action("Setting up", "project for CLion");
cforge::logger::print_action("Setting up", "project for CLion");
// Create build directory if it doesn't exist
if (!std::filesystem::exists(build_dir)) {
try {
std::filesystem::create_directories(build_dir);
} catch (const std::exception &ex) {
logger::print_error("Failed to create build directory: " +
cforge::logger::print_error("Failed to create build directory: " +
std::string(ex.what()));
return false;
}
@@ -177,13 +176,13 @@ static bool generate_clion_project(const std::filesystem::path &project_dir,
std::vector<std::string> cmake_args = {"-B", build_dir.string(), "-S",
project_dir.string()};
bool success = execute_tool("cmake", cmake_args, "", "CMake", verbose);
bool success = cforge::execute_tool("cmake", cmake_args, "", "CMake", verbose);
if (success) {
logger::print_action("Set up", "project for CLion");
logger::print_status("Open the project root directory in CLion");
cforge::logger::print_action("Set up", "project for CLion");
cforge::logger::print_status("Open the project root directory in CLion");
} else {
logger::print_error("Failed to set up project for CLion");
cforge::logger::print_error("Failed to set up project for CLion");
}
return success;
@@ -206,7 +205,7 @@ static std::string generate_uuid() {
// Write a minimal .vcxproj for a project from its TOML configuration
static bool write_vcxproj(const std::filesystem::path &proj_dir,
const toml_reader &cfg,
const cforge::toml_reader &cfg,
const std::filesystem::path &out_dir,
const std::string &proj_guid, bool /*verbose*/) {
std::string name =
@@ -217,7 +216,7 @@ static bool write_vcxproj(const std::filesystem::path &proj_dir,
}
std::ofstream f(rel_out);
if (!f) {
logger::print_error("Failed to create vcxproj: " + rel_out.string());
cforge::logger::print_error("Failed to create vcxproj: " + rel_out.string());
return false;
}
// Detect current platform for platform-specific settings
@@ -504,7 +503,7 @@ static bool write_vcxproj(const std::filesystem::path &proj_dir,
std::filesystem::path filters_path = out_dir / (name + ".vcxproj.filters");
std::ofstream fl(filters_path);
if (!fl) {
logger::print_error("Failed to create filters: " + filters_path.string());
cforge::logger::print_error("Failed to create filters: " + filters_path.string());
return false;
}
fl << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
@@ -594,7 +593,7 @@ static bool write_sln(const std::filesystem::path &workspace_dir,
std::filesystem::path sln_path = out_dir / (ws.get_name() + ".sln");
std::ofstream sln(sln_path);
if (!sln) {
logger::print_error("Failed to create solution: " + sln_path.string());
cforge::logger::print_error("Failed to create solution: " + sln_path.string());
return false;
}
sln << "Microsoft Visual Studio Solution File, Format Version 12.00\n"
@@ -628,15 +627,15 @@ static bool write_sln(const std::filesystem::path &workspace_dir,
static bool
generate_vs_workspace_solution(const std::filesystem::path &workspace_dir,
bool verbose) {
toml_reader ws_cfg;
cforge::toml_reader ws_cfg;
auto ws_file = workspace_dir / WORKSPACE_FILE;
if (!ws_cfg.load(ws_file.string())) {
logger::print_error("Failed to load " + ws_file.string());
cforge::logger::print_error("Failed to load " + ws_file.string());
return false;
}
cforge::workspace ws;
if (!ws.load(workspace_dir)) {
logger::print_error("Failed to parse workspace at " +
cforge::logger::print_error("Failed to parse workspace at " +
workspace_dir.string());
return false;
}
@@ -645,10 +644,10 @@ generate_vs_workspace_solution(const std::filesystem::path &workspace_dir,
for (auto &proj : ws.get_projects()) {
std::string guid = generate_uuid();
project_guids[proj.name] = guid;
toml_reader proj_cfg;
cforge::toml_reader proj_cfg;
auto proj_toml = workspace_dir / proj.path / CFORGE_FILE;
if (!proj_cfg.load(proj_toml.string())) {
logger::print_error("Failed to load " + proj_toml.string());
cforge::logger::print_error("Failed to load " + proj_toml.string());
return false;
}
// Write each project into its own directory
@@ -679,8 +678,8 @@ generate_vs_workspace_solution(const std::filesystem::path &workspace_dir,
lines.push_back(line);
in.close();
// Find insertion point
int insert_idx = -1;
for (int i = 0; i < (int)lines.size(); ++i) {
cforge_int_t insert_idx = -1;
for (cforge_int_t i = 0; i < (cforge_int_t)lines.size(); ++i) {
if (lines[i].find(
"<Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\"") !=
std::string::npos) {
@@ -722,8 +721,8 @@ generate_vs_workspace_solution(const std::filesystem::path &workspace_dir,
for (auto &l : lines)
out << l << '\n';
}
logger::generated("Visual Studio solution and project files");
logger::print_status("Open " +
cforge::logger::generated("Visual Studio solution and project files");
cforge::logger::print_status("Open " +
(workspace_dir / (ws.get_name() + ".sln")).string() +
" to start working");
return true;
@@ -736,7 +735,7 @@ static bool write_sln_single(const std::filesystem::path &out_dir,
std::filesystem::path sln_path = out_dir / (name + ".sln");
std::ofstream sln(sln_path);
if (!sln) {
logger::print_error("Failed to create solution: " + sln_path.string());
cforge::logger::print_error("Failed to create solution: " + sln_path.string());
return false;
}
sln << "Microsoft Visual Studio Solution File, Format Version 12.00\n"
@@ -752,7 +751,7 @@ static bool write_sln_single(const std::filesystem::path &out_dir,
// Generate VS project and solution for a single project without CMake
static bool generate_vs_project_direct(const std::filesystem::path &project_dir,
const toml_reader &cfg, bool verbose) {
const cforge::toml_reader &cfg, bool verbose) {
std::filesystem::path out_dir = project_dir;
std::string name =
cfg.get_string("project.name", project_dir.filename().string());
@@ -763,8 +762,8 @@ static bool generate_vs_project_direct(const std::filesystem::path &project_dir,
if (!write_sln_single(out_dir, name, guid, verbose)) {
return false;
}
logger::generated("Visual Studio solution and project files");
logger::print_status("Open " + (out_dir / (name + ".sln")).string() +
cforge::logger::generated("Visual Studio solution and project files");
cforge::logger::print_status("Open " + (out_dir / (name + ".sln")).string() +
" to start working");
return true;
}
@@ -780,7 +779,7 @@ cforge_int_t cforge_cmd_ide(const cforge_context_t *ctx) {
std::filesystem::path project_dir = ctx->working_dir;
// Determine verbosity
bool verbose = logger::get_verbosity() == log_verbosity::VERBOSITY_VERBOSE;
bool verbose = cforge::logger::get_verbosity() == cforge::log_verbosity::VERBOSITY_VERBOSE;
// Get IDE type from arguments
std::string ide_type;
if (ctx->args.args && ctx->args.args[0]) {
@@ -799,7 +798,7 @@ cforge_int_t cforge_cmd_ide(const cforge_context_t *ctx) {
// Workspace mode: bypass CMake and generate VS solution
if (ctx->is_workspace) {
if (ide_type != "" && ide_type != "vs" && ide_type != "visual-studio") {
logger::print_error("Workspace IDE only supports Visual Studio (vs)");
cforge::logger::print_error("Workspace IDE only supports Visual Studio (vs)");
return 1;
}
return generate_vs_workspace_solution(project_dir, verbose) ? 0 : 1;
@@ -809,15 +808,15 @@ cforge_int_t cforge_cmd_ide(const cforge_context_t *ctx) {
// Check if cforge.toml exists
std::filesystem::path config_path = project_dir / CFORGE_FILE;
if (!std::filesystem::exists(config_path)) {
logger::print_error("Not a valid cforge project (missing " +
cforge::logger::print_error("Not a valid cforge project (missing " +
std::string(CFORGE_FILE) + ")");
return 1;
}
// Load project configuration
toml_reader project_config;
cforge::toml_reader project_config;
if (!project_config.load(config_path.string())) {
logger::print_error("Failed to parse " + std::string(CFORGE_FILE));
cforge::logger::print_error("Failed to parse " + std::string(CFORGE_FILE));
return 1;
}
@@ -838,8 +837,8 @@ cforge_int_t cforge_cmd_ide(const cforge_context_t *ctx) {
} else if (ide_type == "clion") {
success = generate_clion_project(project_dir, build_dir, verbose);
} else {
logger::print_error("Unknown IDE type: " + ide_type);
logger::print_status("Available IDE types: vs (Visual Studio), cb "
cforge::logger::print_error("Unknown IDE type: " + ide_type);
cforge::logger::print_status("Available IDE types: vs (Visual Studio), cb "
"(CodeBlocks), xcode, clion");
return 1;
}
@@ -6,12 +6,11 @@
#include "cforge/log.hpp"
#include "core/commands.hpp"
#include "core/registry.hpp"
#include "core/types.h"
#include <algorithm>
#include <iomanip>
#include <iostream>
using namespace cforge;
/**
* @brief Handle the 'info' command
*
@@ -25,7 +24,7 @@ cforge_int_t cforge_cmd_info(const cforge_context_t *ctx) {
bool show_features = true;
bool update_index = false;
for (int i = 0; i < ctx->args.arg_count; ++i) {
for (cforge_int_t i = 0; i < ctx->args.arg_count; ++i) {
std::string arg = ctx->args.args[i];
if (arg == "--versions" || arg == "-v") {
show_versions = true;
@@ -39,26 +38,26 @@ cforge_int_t cforge_cmd_info(const cforge_context_t *ctx) {
}
if (package_name.empty()) {
logger::print_error("No package name provided");
logger::print_action("Usage", "cforge info <package> [--versions] [--update]");
cforge::logger::print_error("No package name provided");
cforge::logger::print_action("Usage", "cforge info <package> [--versions] [--update]");
return 1;
}
// Initialize registry
registry reg;
cforge::registry reg;
// Update index if needed or requested
if (update_index || reg.needs_update()) {
if (!reg.update(update_index)) {
logger::print_warning("Failed to update package index, using cached version");
cforge::logger::print_warning("Failed to update package index, using cached version");
}
}
// Get package info
auto pkg = reg.get_package(package_name);
if (!pkg) {
logger::print_error("Package '" + package_name + "' not found");
logger::print_action("Hint", "Run 'cforge search " + package_name + "' to search for similar packages");
cforge::logger::print_error("Package '" + package_name + "' not found");
cforge::logger::print_action("Hint", "Run 'cforge search " + package_name + "' to search for similar packages");
return 1;
}
@@ -100,7 +99,7 @@ cforge_int_t cforge_cmd_info(const cforge_context_t *ctx) {
// Keywords
if (!pkg->keywords.empty()) {
std::string keywords_str;
for (size_t i = 0; i < pkg->keywords.size(); ++i) {
for (cforge_size_t i = 0; i < pkg->keywords.size(); ++i) {
if (i > 0) keywords_str += ", ";
keywords_str += pkg->keywords[i];
}
@@ -110,7 +109,7 @@ cforge_int_t cforge_cmd_info(const cforge_context_t *ctx) {
// Categories
if (!pkg->categories.empty()) {
std::string categories_str;
for (size_t i = 0; i < pkg->categories.size(); ++i) {
for (cforge_size_t i = 0; i < pkg->categories.size(); ++i) {
if (i > 0) categories_str += ", ";
categories_str += pkg->categories[i];
}
@@ -126,7 +125,7 @@ cforge_int_t cforge_cmd_info(const cforge_context_t *ctx) {
// Default features
if (!pkg->default_features.empty()) {
std::cout << " \033[90mDefault:\033[0m ";
for (size_t i = 0; i < pkg->default_features.size(); ++i) {
for (cforge_size_t i = 0; i < pkg->default_features.size(); ++i) {
if (i > 0) std::cout << ", ";
std::cout << "\033[32m" << pkg->default_features[i] << "\033[0m";
}
@@ -134,11 +133,11 @@ cforge_int_t cforge_cmd_info(const cforge_context_t *ctx) {
}
// All features
size_t max_name_len = 0;
cforge_size_t max_name_len = 0;
for (const auto &[name, feat] : pkg->features) {
max_name_len = std::max(max_name_len, name.length());
}
max_name_len = std::min(max_name_len, size_t(20));
max_name_len = std::min(max_name_len, cforge_size_t(20));
for (const auto &[name, feat] : pkg->features) {
std::cout << " \033[36m" << std::left << std::setw(max_name_len + 2) << name << "\033[0m";
@@ -158,7 +157,7 @@ cforge_int_t cforge_cmd_info(const cforge_context_t *ctx) {
// Show required dependencies
if (!feat.required_deps.empty()) {
std::cout << " \033[90m(requires: ";
for (size_t i = 0; i < feat.required_deps.size(); ++i) {
for (cforge_size_t i = 0; i < feat.required_deps.size(); ++i) {
if (i > 0) std::cout << ", ";
std::cout << feat.required_deps[i];
}
@@ -175,8 +174,8 @@ cforge_int_t cforge_cmd_info(const cforge_context_t *ctx) {
if (show_versions && !pkg->versions.empty()) {
std::cout << "\033[1mVersions:\033[0m" << std::endl;
size_t count = std::min(size_t(10), pkg->versions.size());
for (size_t i = 0; i < count; ++i) {
cforge_size_t count = std::min(cforge_size_t(10), pkg->versions.size());
for (cforge_size_t i = 0; i < count; ++i) {
const auto &ver = pkg->versions[i];
std::cout << " \033[36m" << std::setw(12) << std::left << ver.version << "\033[0m";
std::cout << " tag: " << ver.tag;
@@ -208,7 +207,7 @@ cforge_int_t cforge_cmd_info(const cforge_context_t *ctx) {
std::cout << "\033[1mMaintainers:\033[0m" << std::endl;
if (!pkg->maintainer_owners.empty()) {
std::cout << " Owners: ";
for (size_t i = 0; i < pkg->maintainer_owners.size(); ++i) {
for (cforge_size_t i = 0; i < pkg->maintainer_owners.size(); ++i) {
if (i > 0) std::cout << ", ";
std::cout << pkg->maintainer_owners[i];
}
@@ -216,7 +215,7 @@ cforge_int_t cforge_cmd_info(const cforge_context_t *ctx) {
}
if (!pkg->maintainer_authors.empty()) {
std::cout << " Authors: ";
for (size_t i = 0; i < pkg->maintainer_authors.size(); ++i) {
for (cforge_size_t i = 0; i < pkg->maintainer_authors.size(); ++i) {
if (i > 0) std::cout << ", ";
std::cout << pkg->maintainer_authors[i];
}
@@ -9,6 +9,7 @@
#include "core/file_system.h"
#include "core/process_utils.hpp"
#include "core/toml_reader.hpp"
#include "core/types.h"
#include "core/workspace.hpp"
#include <algorithm>
@@ -19,8 +20,6 @@
#include <string>
#include <vector>
using namespace cforge;
// Global init template name (executable, static-lib, shared-library,
// header-only)
static std::string g_template_name = "executable";
@@ -95,16 +94,16 @@ parse_project_list(const std::string &project_list) {
std::filesystem::path gitignore_path = project_path / ".gitignore";
if (std::filesystem::exists(gitignore_path) && !g_force_overwrite) {
logger::print_warning(".gitignore already exists, skipping");
cforge::logger::print_warning(".gitignore already exists, skipping");
return true;
} else if (std::filesystem::exists(gitignore_path) && g_force_overwrite) {
logger::print_action("Overwriting", ".gitignore");
cforge::logger::print_action("Overwriting", ".gitignore");
}
std::ofstream gitignore(gitignore_path);
if (!gitignore.is_open()) {
logger::print_error("Failed to create .gitignore file");
cforge::logger::print_error("Failed to create .gitignore file");
return false;
}
@@ -161,7 +160,7 @@ parse_project_list(const std::string &project_list) {
gitignore.close();
logger::created(".gitignore");
cforge::logger::created(".gitignore");
return true;
}
@@ -177,16 +176,16 @@ static bool create_readme(const std::filesystem::path &project_path,
std::filesystem::path readme_path = project_path / "README.md";
if (std::filesystem::exists(readme_path) && !g_force_overwrite) {
logger::print_warning("README.md already exists, skipping");
cforge::logger::print_warning("README.md already exists, skipping");
return true;
} else if (std::filesystem::exists(readme_path) && g_force_overwrite) {
logger::print_action("Overwriting", "README.md");
cforge::logger::print_action("Overwriting", "README.md");
}
std::ofstream readme(readme_path);
if (!readme.is_open()) {
logger::print_error("Failed to create README.md file");
cforge::logger::print_error("Failed to create README.md file");
return false;
}
@@ -213,7 +212,7 @@ static bool create_readme(const std::filesystem::path &project_path,
readme.close();
logger::created("README.md");
cforge::logger::created("README.md");
return true;
}
@@ -234,15 +233,15 @@ static bool create_cmakelists(const std::filesystem::path &project_path,
std::filesystem::path cmakelists_path = project_path / "CMakeLists.txt";
if (std::filesystem::exists(cmakelists_path) && !g_force_overwrite) {
logger::print_warning("CMakeLists.txt already exists, skipping");
cforge::logger::print_warning("CMakeLists.txt already exists, skipping");
return true;
} else if (std::filesystem::exists(cmakelists_path) && g_force_overwrite) {
logger::print_action("Overwriting", "CMakeLists.txt");
cforge::logger::print_action("Overwriting", "CMakeLists.txt");
}
std::ofstream cmakelists(cmakelists_path);
if (!cmakelists) {
logger::print_error("Failed to create CMakeLists.txt");
cforge::logger::print_error("Failed to create CMakeLists.txt");
return false;
}
@@ -410,7 +409,7 @@ static bool create_cmakelists(const std::filesystem::path &project_path,
cmakelists << "endif()\n";
cmakelists.close();
logger::created("CMakeLists.txt");
cforge::logger::created("CMakeLists.txt");
return true;
}
@@ -429,15 +428,15 @@ static bool create_cforge_toml(const std::filesystem::path &project_path,
std::filesystem::path config_path = project_path / CFORGE_FILE;
if (std::filesystem::exists(config_path) && !g_force_overwrite) {
logger::print_warning("cforge.toml already exists, skipping");
cforge::logger::print_warning("cforge.toml already exists, skipping");
return true;
} else if (std::filesystem::exists(config_path) && g_force_overwrite) {
logger::print_action("Overwriting", "cforge.toml");
cforge::logger::print_action("Overwriting", "cforge.toml");
}
std::ofstream config(config_path);
if (!config) {
logger::print_error("Failed to create cforge.toml file");
cforge::logger::print_error("Failed to create cforge.toml file");
return false;
}
@@ -480,31 +479,37 @@ static bool create_cforge_toml(const std::filesystem::path &project_path,
config << "directory = \"build\"\n";
config << "source_dirs = [\"src\"]\n";
config << "include_dirs = [\"include\"]\n";
config << "export_compile_commands = true # Generate compile_commands.json for IDEs\n";
config << "# position_independent_code = true # For shared libraries\n";
config << "# Uncomment to specify custom source patterns\n";
config << "# source_patterns = [\"src/*.cpp\", \"src/**/*.cpp\"]\n";
config << "# Uncomment to specify individual source files\n";
config << "# source_files = [\"src/main.cpp\", \"src/example.cpp\"]\n\n";
// Add build configuration for different build types
// Using portable options that work across MSVC, GCC, and Clang
config << "[build.config.debug]\n";
config << "defines = [\"DEBUG=1\", \"ENABLE_LOGGING=1\"]\n";
config << "flags = [\"-g\", \"-O0\"]\n";
config << "cmake_args = [\"-DENABLE_TESTS=ON\"]\n\n";
config << "optimize = \"debug\" # Portable: maps to /Od (MSVC) or -Og (GCC/Clang)\n";
config << "debug_info = true # Portable: maps to /Zi (MSVC) or -g (GCC/Clang)\n";
config << "warnings = \"all\" # Portable: maps to /W4 (MSVC) or -Wall -Wextra (GCC/Clang)\n";
config << "defines = [\"DEBUG=1\"]\n";
config << "# sanitizers = [\"address\"] # Enable AddressSanitizer for debug builds\n\n";
config << "[build.config.release]\n";
config << "defines = [\"NDEBUG=1\"]\n";
config << "flags = [\"-O3\"]\n";
config << "cmake_args = [\"-DENABLE_TESTS=OFF\"]\n\n";
config << "optimize = \"speed\" # Portable: maps to /O2 (MSVC) or -O2 (GCC/Clang)\n";
config << "warnings = \"all\" # Portable: maps to /W4 (MSVC) or -Wall -Wextra (GCC/Clang)\n";
config << "lto = true # Link-time optimization: /GL+/LTCG (MSVC) or -flto (GCC/Clang)\n";
config << "defines = [\"NDEBUG\"]\n\n";
config << "[build.config.relwithdebinfo]\n";
config << "defines = [\"NDEBUG=1\"]\n";
config << "flags = [\"-O2\", \"-g\"]\n";
config << "cmake_args = []\n\n";
config << "optimize = \"speed\"\n";
config << "debug_info = true\n";
config << "warnings = \"all\"\n";
config << "defines = [\"NDEBUG\"]\n\n";
config << "[build.config.minsizerel]\n";
config << "defines = [\"NDEBUG=1\"]\n";
config << "flags = [\"-Os\"]\n";
config << "cmake_args = []\n\n";
config << "optimize = \"size\" # Portable: maps to /O1 /Os (MSVC) or -Os (GCC/Clang)\n";
config << "defines = [\"NDEBUG\"]\n\n";
config << "[test]\n";
config << "enabled = " << (with_tests ? "true" : "false") << "\n";
@@ -563,35 +568,38 @@ static bool create_cforge_toml(const std::filesystem::path &project_path,
// Platform-specific configuration
config << "# Platform-specific configuration\n";
config << "# Portable options: optimize, warnings, debug_info, sanitizers, lto,\n";
config << "# exceptions, rtti, hardening, visibility\n";
config << "# [platform.windows]\n";
config << "# hardening = \"full\" # Portable: /GS /sdl /GUARD:CF (MSVC)\n";
config << "# defines = [\"WIN32\", \"_WINDOWS\"]\n";
config << "# links = [\"kernel32\", \"user32\"]\n";
config << "# flags = [\"/W4\"]\n";
config << "#\n";
config << "# [platform.linux]\n";
config << "# hardening = \"basic\" # Portable: -fstack-protector-strong -D_FORTIFY_SOURCE=2\n";
config << "# defines = [\"LINUX\"]\n";
config << "# links = [\"pthread\", \"dl\"]\n";
config << "# flags = [\"-Wall\"]\n";
config << "#\n";
config << "# [platform.macos]\n";
config << "# stdlib = \"libc++\" # Portable: use libc++ on macOS\n";
config << "# defines = [\"MACOS\"]\n";
config << "# frameworks = [\"Cocoa\", \"IOKit\"] # macOS frameworks\n";
config << "# flags = [\"-Wall\"]\n\n";
config << "# frameworks = [\"Cocoa\", \"IOKit\"]\n\n";
// Compiler-specific configuration
config << "# Compiler-specific configuration\n";
config << "# Portable options work here too - they translate to the right flags\n";
config << "# [compiler.msvc]\n";
config << "# flags = [\"/W4\", \"/WX\"]\n";
config << "# warnings = \"strict\" # Portable: /W4 /WX\n";
config << "# defines = [\"_CRT_SECURE_NO_WARNINGS\"]\n";
config << "#\n";
config << "# [compiler.gcc]\n";
config << "# flags = [\"-Wall\", \"-Wextra\", \"-Wpedantic\"]\n";
config << "# warnings = \"pedantic\" # Portable: -Wall -Wextra -Wpedantic -Werror\n";
config << "#\n";
config << "# [compiler.clang]\n";
config << "# flags = [\"-Wall\", \"-Wextra\", \"-Wpedantic\"]\n";
config << "# warnings = \"pedantic\" # Portable: -Wall -Wextra -Wpedantic -Werror\n";
config << "#\n";
config << "# [compiler.mingw]\n";
config << "# flags = [\"-Wall\", \"-Wextra\"]\n";
config << "# warnings = \"all\" # Portable: -Wall -Wextra\n";
config << "# defines = [\"MINGW\"]\n\n";
// Platform + Compiler combination
@@ -642,7 +650,7 @@ static bool create_cforge_toml(const std::filesystem::path &project_path,
config << "# module_paths = [\"cmake/modules\"] # Custom module paths\n";
config.close();
logger::created("cforge.toml");
cforge::logger::created("cforge.toml");
return true;
}
@@ -666,16 +674,16 @@ static bool create_main_cpp(const std::filesystem::path &project_path,
std::filesystem::path main_cpp_path = src_dir / "main.cpp";
if (std::filesystem::exists(main_cpp_path) && !g_force_overwrite) {
logger::print_warning("main.cpp already exists, skipping");
cforge::logger::print_warning("main.cpp already exists, skipping");
return true;
} else if (std::filesystem::exists(main_cpp_path) && g_force_overwrite) {
logger::print_action("Overwriting", "main.cpp");
cforge::logger::print_action("Overwriting", "main.cpp");
}
std::ofstream main_cpp(main_cpp_path);
if (!main_cpp.is_open()) {
logger::print_error("Failed to create main.cpp file");
cforge::logger::print_error("Failed to create main.cpp file");
return false;
}
@@ -700,7 +708,7 @@ static bool create_main_cpp(const std::filesystem::path &project_path,
main_cpp << "}\n";
main_cpp.close();
logger::created("src/main.cpp");
cforge::logger::created("src/main.cpp");
return true;
}
@@ -734,11 +742,11 @@ static bool create_include_files(const std::filesystem::path &project_path,
project_include_dir / "example.hpp";
if (std::filesystem::exists(example_header_path) && !g_force_overwrite) {
logger::print_warning("example.hpp already exists, skipping");
cforge::logger::print_warning("example.hpp already exists, skipping");
return true;
} else if (std::filesystem::exists(example_header_path) &&
g_force_overwrite) {
logger::print_action("Overwriting", "example.hpp");
cforge::logger::print_action("Overwriting", "example.hpp");
}
std::ofstream example_header(example_header_path);
@@ -784,10 +792,10 @@ create_example_implementation(const std::filesystem::path &project_path,
std::filesystem::path example_cpp_path = src_dir / "example.cpp";
if (std::filesystem::exists(example_cpp_path) && !g_force_overwrite) {
logger::print_warning("example.cpp already exists, skipping");
cforge::logger::print_warning("example.cpp already exists, skipping");
return true;
} else if (std::filesystem::exists(example_cpp_path) && g_force_overwrite) {
logger::print_action("Overwriting", "example.cpp");
cforge::logger::print_action("Overwriting", "example.cpp");
}
std::ofstream example_cpp(example_cpp_path);
@@ -831,10 +839,10 @@ static bool create_test_files(const std::filesystem::path &project_path,
std::filesystem::path tests_cmake_path = tests_dir / "CMakeLists.txt";
if (std::filesystem::exists(tests_cmake_path) && !g_force_overwrite) {
logger::print_warning("tests/CMakeLists.txt already exists, skipping");
cforge::logger::print_warning("tests/CMakeLists.txt already exists, skipping");
} else {
if (std::filesystem::exists(tests_cmake_path) && g_force_overwrite) {
logger::print_action("Overwriting", "tests/CMakeLists.txt");
cforge::logger::print_action("Overwriting", "tests/CMakeLists.txt");
}
std::ofstream tests_cmake(tests_cmake_path);
if (!tests_cmake.is_open()) {
@@ -884,10 +892,10 @@ static bool create_test_files(const std::filesystem::path &project_path,
std::filesystem::path test_main_path = tests_dir / "test_main.cpp";
if (std::filesystem::exists(test_main_path) && !g_force_overwrite) {
logger::print_warning("test_main.cpp already exists, skipping");
cforge::logger::print_warning("test_main.cpp already exists, skipping");
} else {
if (std::filesystem::exists(test_main_path) && g_force_overwrite) {
logger::print_action("Overwriting", "test_main.cpp");
cforge::logger::print_action("Overwriting", "test_main.cpp");
}
std::ofstream test_main(test_main_path);
if (!test_main.is_open()) {
@@ -911,10 +919,10 @@ static bool create_test_files(const std::filesystem::path &project_path,
std::filesystem::path test_example_path = tests_dir / "test_example.cpp";
if (std::filesystem::exists(test_example_path) && !g_force_overwrite) {
logger::print_warning("test_example.cpp already exists, skipping");
cforge::logger::print_warning("test_example.cpp already exists, skipping");
} else {
if (std::filesystem::exists(test_example_path) && g_force_overwrite) {
logger::print_action("Overwriting", "test_example.cpp");
cforge::logger::print_action("Overwriting", "test_example.cpp");
}
std::ofstream test_example(test_example_path);
if (!test_example.is_open()) {
@@ -954,16 +962,16 @@ static bool create_license_file(const std::filesystem::path &project_path,
std::filesystem::path license_path = project_path / "LICENSE";
if (std::filesystem::exists(license_path) && !g_force_overwrite) {
logger::print_warning("LICENSE already exists, skipping");
cforge::logger::print_warning("LICENSE already exists, skipping");
return true;
} else if (std::filesystem::exists(license_path) && g_force_overwrite) {
logger::print_action("Overwriting", "LICENSE");
cforge::logger::print_action("Overwriting", "LICENSE");
}
std::ofstream license(license_path);
if (!license.is_open()) {
logger::print_error("Failed to create LICENSE file");
cforge::logger::print_error("Failed to create LICENSE file");
return false;
}
@@ -971,7 +979,7 @@ static bool create_license_file(const std::filesystem::path &project_path,
auto now = std::chrono::system_clock::now();
std::time_t current_time = std::chrono::system_clock::to_time_t(now);
std::tm *time_info = std::localtime(&current_time);
int current_year = time_info->tm_year + 1900;
cforge_int_t current_year = time_info->tm_year + 1900;
license << "MIT License\n\n";
license << "Copyright (c) " << current_year << " " << project_name << "\n\n";
@@ -1005,7 +1013,7 @@ static bool create_license_file(const std::filesystem::path &project_path,
license.close();
logger::created("LICENSE (MIT)");
cforge::logger::created("LICENSE (MIT)");
return true;
}
@@ -1014,7 +1022,7 @@ static bool create_license_file(const std::filesystem::path &project_path,
*
* @return bool True if git is available
*/
static bool is_git_available() { return is_command_available("git"); }
static bool is_git_available() { return cforge::is_command_available("git"); }
/**
* @brief Init a new git repository if requested and git is available
@@ -1028,35 +1036,35 @@ static bool is_git_available() { return is_command_available("git"); }
bool verbose) {
// If Git initialization wasn't explicitly requested, just return success
if (!verbose) {
logger::print_action("Skipped",
cforge::logger::print_action("Skipped",
"git initialization (use --git flag to enable)");
return true;
}
// First check if git is available
if (!is_git_available()) {
logger::print_warning("Git not found in PATH, skipping git initialization");
cforge::logger::print_warning("Git not found in PATH, skipping git initialization");
return true; // Not critical for project creation
}
logger::print_action("Initializing", "git repository");
cforge::logger::print_action("Initializing", "git repository");
std::vector<std::string> git_args = {"init"};
// Use a shorter timeout for git init
bool result =
execute_tool("git", git_args, project_path.string(), "Git", verbose, 20);
cforge::execute_tool("git", git_args, project_path.string(), "Git", verbose, 20);
if (result) {
logger::created("git repository");
cforge::logger::created("git repository");
// Create an initial commit (optional)
if (verbose) {
logger::print_action("Creating", "initial commit");
cforge::logger::print_action("Creating", "initial commit");
// Add all files
std::vector<std::string> git_add_args = {"add", "."};
bool add_result = execute_tool("git", git_add_args, project_path.string(),
bool add_result = cforge::execute_tool("git", git_add_args, project_path.string(),
"Git add", verbose, 10);
if (add_result) {
@@ -1064,22 +1072,22 @@ static bool is_git_available() { return is_command_available("git"); }
std::vector<std::string> git_commit_args = {"commit", "-m",
"Initial commit"};
bool commit_result =
execute_tool("git", git_commit_args, project_path.string(),
cforge::execute_tool("git", git_commit_args, project_path.string(),
"Git commit", verbose, 10);
if (commit_result) {
logger::created("initial commit");
cforge::logger::created("initial commit");
} else {
logger::print_warning(
cforge::logger::print_warning(
"Failed to create initial commit. This is not critical");
}
} else {
logger::print_warning(
cforge::logger::print_warning(
"Failed to add files to git. This is not critical");
}
}
} else {
logger::print_warning("Failed to initialize git repository. This is not "
cforge::logger::print_warning("Failed to initialize git repository. This is not "
"critical for project creation");
}
@@ -1132,16 +1140,16 @@ generate_workspace_cmakelists(const std::filesystem::path &workspace_dir,
std::filesystem::path cmakelists_path = workspace_dir / "CMakeLists.txt";
if (std::filesystem::exists(cmakelists_path) && !g_force_overwrite) {
logger::print_warning(
cforge::logger::print_warning(
"Workspace-level CMakeLists.txt already exists, skipping");
return true;
} else if (std::filesystem::exists(cmakelists_path) && g_force_overwrite) {
logger::print_action("Overwriting", "workspace-level CMakeLists.txt");
cforge::logger::print_action("Overwriting", "workspace-level CMakeLists.txt");
}
std::ofstream cmakelists(cmakelists_path);
if (!cmakelists.is_open()) {
logger::print_error("Failed to create workspace CMakeLists.txt at: " +
cforge::logger::print_error("Failed to create workspace CMakeLists.txt at: " +
cmakelists_path.string());
return false;
}
@@ -1181,7 +1189,7 @@ generate_workspace_cmakelists(const std::filesystem::path &workspace_dir,
cmakelists << "message(STATUS \" - C++ Standard: ${CMAKE_CXX_STANDARD}\")\n";
cmakelists << "message(STATUS \" - Build Type: ${CMAKE_BUILD_TYPE}\")\n";
cmakelists << "message(STATUS \" - Projects: ";
for (size_t i = 0; i < project_names.size(); ++i) {
for (cforge_size_t i = 0; i < project_names.size(); ++i) {
if (i > 0)
cmakelists << ", ";
cmakelists << project_names[i];
@@ -1189,7 +1197,7 @@ generate_workspace_cmakelists(const std::filesystem::path &workspace_dir,
cmakelists << "\")\n";
cmakelists.close();
logger::created("workspace CMakeLists.txt");
cforge::logger::created("workspace CMakeLists.txt");
return true;
}
@@ -1226,59 +1234,59 @@ static bool create_project(const std::filesystem::path &project_path,
// Create README file
if (!create_readme(project_path, project_name)) {
logger::print_error("Failed to create README.md");
cforge::logger::print_error("Failed to create README.md");
return false;
}
// Create CMakeLists.txt
if (!create_cmakelists(project_path, project_name, cpp_version,
with_tests)) {
logger::print_error("Failed to create CMakeLists.txt");
cforge::logger::print_error("Failed to create CMakeLists.txt");
return false;
}
// Create cforge.toml configuration
if (!create_cforge_toml(project_path, project_name, cpp_version,
with_tests)) {
logger::print_error("Failed to create cforge.toml");
cforge::logger::print_error("Failed to create cforge.toml");
return false;
}
// Create src/main.cpp
if (!create_main_cpp(project_path, project_name)) {
logger::print_error("Failed to create main.cpp");
cforge::logger::print_error("Failed to create main.cpp");
return false;
}
// Create include files
if (!create_include_files(project_path, normalized_name)) {
logger::print_error("Failed to create include files");
cforge::logger::print_error("Failed to create include files");
return false;
}
// Create example implementation file
if (!create_example_implementation(project_path, normalized_name)) {
logger::print_error("Failed to create implementation files");
cforge::logger::print_error("Failed to create implementation files");
return false;
}
// Create test files if requested
if (with_tests) {
if (!create_test_files(project_path, normalized_name)) {
logger::print_error("Failed to create test files");
cforge::logger::print_error("Failed to create test files");
return false;
}
}
// Create license file
if (!create_license_file(project_path, project_name)) {
logger::print_error("Failed to create LICENSE file");
cforge::logger::print_error("Failed to create LICENSE file");
return false;
}
return true;
} catch (const std::exception &ex) {
logger::print_error("Failed to create project: " + std::string(ex.what()));
cforge::logger::print_error("Failed to create project: " + std::string(ex.what()));
return false;
}
}
@@ -1322,7 +1330,7 @@ cforge_int_t cforge_cmd_init(const cforge_context_t *ctx) {
}
// Parse flag arguments
for (int i = 0; i < ctx->args.arg_count; ++i) {
for (cforge_int_t i = 0; i < ctx->args.arg_count; ++i) {
std::string arg = ctx->args.args[i];
// Handle overwrite flag
@@ -1370,7 +1378,7 @@ cforge_int_t cforge_cmd_init(const cforge_context_t *ctx) {
// If the next argument is a flag or end of args, use empty project
// list
if (i + 1 >= ctx->args.arg_count || ctx->args.args[i + 1][0] == '-') {
logger::print_warning(
cforge::logger::print_warning(
"--projects flag provided but no projects specified");
continue;
}
@@ -1384,7 +1392,7 @@ cforge_int_t cforge_cmd_init(const cforge_context_t *ctx) {
i++; // Skip the list in next iteration
} else {
// Collect all arguments until next flag
int j = i + 1;
cforge_int_t j = i + 1;
while (j < ctx->args.arg_count && ctx->args.args[j][0] != '-') {
project_names.push_back(ctx->args.args[j]);
j++;
@@ -1446,12 +1454,12 @@ cforge_int_t cforge_cmd_init(const cforge_context_t *ctx) {
// Handle loading from existing workspace file if requested
if (from_file && workspace_file_exists) {
logger::print_action("Loading", workspace_file_path.string());
cforge::logger::print_action("Loading", workspace_file_path.string());
// Load the workspace configuration
workspace_config config;
cforge::workspace_config config;
if (!config.load(workspace_file_path.string())) {
logger::print_error("Failed to load workspace configuration from " +
cforge::logger::print_error("Failed to load workspace configuration from " +
workspace_file_path.string());
return 1;
}
@@ -1460,11 +1468,11 @@ cforge_int_t cforge_cmd_init(const cforge_context_t *ctx) {
std::string ws_name = config.get_name();
// Get the projects from the workspace
std::vector<workspace_project> projects = config.get_projects();
std::vector<cforge::workspace_project> projects = config.get_projects();
// Create each project
for (const auto &project : projects) {
logger::creating(project.name);
cforge::logger::creating(project.name);
// Create the project directory
std::filesystem::path project_dir =
@@ -1473,7 +1481,7 @@ cforge_int_t cforge_cmd_init(const cforge_context_t *ctx) {
try {
std::filesystem::create_directories(project_dir);
} catch (const std::exception &ex) {
logger::print_error("Failed to create project directory: " +
cforge::logger::print_error("Failed to create project directory: " +
project_dir.string() + " Error: " + ex.what());
continue;
}
@@ -1482,20 +1490,20 @@ cforge_int_t cforge_cmd_init(const cforge_context_t *ctx) {
// Create the project files
if (!create_project(project_dir, project.name, cpp_standard, with_git,
with_tests)) {
logger::print_error("Failed to create project '" + project.name +
cforge::logger::print_error("Failed to create project '" + project.name +
"'");
continue;
}
logger::created(project.name);
cforge::logger::created(project.name);
}
logger::finished(ws_name);
cforge::logger::finished(ws_name);
return 0;
}
// Handle workspace creation
else if (is_workspace) {
logger::creating(workspace_name);
cforge::logger::creating(workspace_name);
// Create workspace directory
std::filesystem::path workspace_dir = ctx->working_dir;
@@ -1509,12 +1517,12 @@ cforge_int_t cforge_cmd_init(const cforge_context_t *ctx) {
try {
bool created = std::filesystem::create_directories(workspace_dir);
if (!created) {
logger::print_error(
cforge::logger::print_error(
"Failed to create workspace directory (returned false)");
return 1;
}
} catch (const std::exception &ex) {
logger::print_error("Exception creating workspace directory: " +
cforge::logger::print_error("Exception creating workspace directory: " +
std::string(ex.what()));
return 1;
}
@@ -1526,16 +1534,16 @@ cforge_int_t cforge_cmd_init(const cforge_context_t *ctx) {
std::string test_path = (workspace_dir / "cforge_test_file").string();
std::ofstream test_file(test_path);
if (!test_file.is_open()) {
logger::print_error("Workspace directory is not writable: " +
cforge::logger::print_error("Workspace directory is not writable: " +
workspace_dir.string());
logger::print_error(
cforge::logger::print_error(
"Please check permissions or try a different location");
return 1;
}
test_file.close();
std::filesystem::remove(test_path);
} catch (const std::exception &ex) {
logger::print_error("Failed to write to workspace directory: " +
cforge::logger::print_error("Failed to write to workspace directory: " +
std::string(ex.what()));
return 1;
}
@@ -1544,16 +1552,16 @@ cforge_int_t cforge_cmd_init(const cforge_context_t *ctx) {
// Create workspace configuration file directly
if (std::filesystem::exists(config_path) && !g_force_overwrite) {
logger::print_warning("Workspace configuration file '" +
cforge::logger::print_warning("Workspace configuration file '" +
config_path.string() +
"' already exists. Skipping creation");
} else {
if (std::filesystem::exists(config_path) && g_force_overwrite) {
logger::print_action("Overwriting", "workspace configuration");
cforge::logger::print_action("Overwriting", "workspace configuration");
}
std::ofstream config_file(config_path);
if (!config_file.is_open()) {
logger::print_error(
cforge::logger::print_error(
"Failed to create workspace configuration file: " +
config_path.string());
return 1;
@@ -1565,7 +1573,7 @@ cforge_int_t cforge_cmd_init(const cforge_context_t *ctx) {
<< "description = \"A C++ workspace created with cforge\"\n\n";
// Write projects as array-of-tables
for (size_t i = 0; i < project_names.size(); ++i) {
for (cforge_size_t i = 0; i < project_names.size(); ++i) {
const auto &proj_name = project_names[i];
bool is_startup = (i == 0); // first project marked startup
config_file << "[[workspace.project]]\n";
@@ -1580,13 +1588,13 @@ cforge_int_t cforge_cmd_init(const cforge_context_t *ctx) {
}
config_file.close();
logger::created("workspace configuration");
cforge::logger::created("workspace configuration");
}
// Generate workspace-level CMakeLists.txt
if (!generate_workspace_cmakelists(workspace_dir, workspace_name,
project_names, cpp_standard)) {
logger::print_warning(
cforge::logger::print_warning(
"Failed to generate workspace-level CMakeLists.txt");
// Continue anyway, not critical
}
@@ -1596,24 +1604,24 @@ cforge_int_t cforge_cmd_init(const cforge_context_t *ctx) {
for (const auto &proj_name : project_names) {
std::filesystem::path project_dir = workspace_dir / proj_name;
logger::creating(proj_name);
cforge::logger::creating(proj_name);
// Create the project with detailed logging
if (!create_project(project_dir, proj_name, cpp_standard, with_git,
with_tests)) {
logger::print_error("Failed to create project '" + proj_name + "'");
cforge::logger::print_error("Failed to create project '" + proj_name + "'");
all_projects_success = false;
// Continue with other projects instead of stopping
continue;
}
logger::created(proj_name);
cforge::logger::created(proj_name);
}
if (all_projects_success) {
logger::finished(workspace_name);
cforge::logger::finished(workspace_name);
} else {
logger::print_warning("Workspace '" + workspace_name +
cforge::logger::print_warning("Workspace '" + workspace_name +
"' created with some errors");
}
@@ -1624,7 +1632,7 @@ cforge_int_t cforge_cmd_init(const cforge_context_t *ctx) {
bool all_projects_success = true;
for (const auto &proj_name : project_names) {
logger::creating(proj_name);
cforge::logger::creating(proj_name);
// Create the project in a new directory named after the project
std::filesystem::path project_dir =
@@ -1635,13 +1643,13 @@ cforge_int_t cforge_cmd_init(const cforge_context_t *ctx) {
try {
bool created = std::filesystem::create_directories(project_dir);
if (!created) {
logger::print_error("Failed to create project directory for '" +
cforge::logger::print_error("Failed to create project directory for '" +
proj_name + "' (returned false)");
all_projects_success = false;
continue;
}
} catch (const std::exception &ex) {
logger::print_error("Exception creating project directory for '" +
cforge::logger::print_error("Exception creating project directory for '" +
proj_name + "': " + std::string(ex.what()));
all_projects_success = false;
continue;
@@ -1651,23 +1659,23 @@ cforge_int_t cforge_cmd_init(const cforge_context_t *ctx) {
// Create the project files
if (!create_project(project_dir, proj_name, cpp_standard, with_git,
with_tests, "", "Debug")) {
logger::print_error("Failed to create project '" + proj_name + "'");
cforge::logger::print_error("Failed to create project '" + proj_name + "'");
all_projects_success = false;
continue;
}
logger::created(proj_name);
cforge::logger::created(proj_name);
}
if (!all_projects_success) {
logger::print_warning("Some projects could not be created");
cforge::logger::print_warning("Some projects could not be created");
}
return all_projects_success ? 0 : 1;
}
// Handle single project creation
else {
logger::creating(project_name);
cforge::logger::creating(project_name);
// Decide whether to create in current directory or subdirectory
std::filesystem::path project_dir;
@@ -1683,12 +1691,12 @@ cforge_int_t cforge_cmd_init(const cforge_context_t *ctx) {
try {
bool created = std::filesystem::create_directories(project_dir);
if (!created) {
logger::print_error(
cforge::logger::print_error(
"Failed to create project directory (returned false)");
return 1;
}
} catch (const std::exception &ex) {
logger::print_error("Exception creating project directory: " +
cforge::logger::print_error("Exception creating project directory: " +
std::string(ex.what()));
return 1;
}
@@ -1704,16 +1712,16 @@ cforge_int_t cforge_cmd_init(const cforge_context_t *ctx) {
std::string test_path = (project_dir / "cforge_test_file").string();
std::ofstream test_file(test_path);
if (!test_file.is_open()) {
logger::print_error("Directory is not writable: " +
cforge::logger::print_error("Directory is not writable: " +
project_dir.string());
logger::print_error(
cforge::logger::print_error(
"Please check permissions or try a different location");
return 1;
}
test_file.close();
std::filesystem::remove(test_path);
} catch (const std::exception &ex) {
logger::print_error("Failed to write to directory: " +
cforge::logger::print_error("Failed to write to directory: " +
std::string(ex.what()));
return 1;
}
@@ -1725,16 +1733,16 @@ cforge_int_t cforge_cmd_init(const cforge_context_t *ctx) {
// Create the project with detailed logging
if (!create_project(project_dir, project_name, cpp_standard, with_git,
with_tests, cmake_preset, build_type)) {
logger::print_error("Failed to create project '" + project_name + "'");
cforge::logger::print_error("Failed to create project '" + project_name + "'");
return 1;
}
logger::finished(project_name);
cforge::logger::finished(project_name);
}
return 0;
} catch (const std::exception &ex) {
logger::print_error("Failed to initialize project: " +
cforge::logger::print_error("Failed to initialize project: " +
std::string(ex.what()));
return 1;
}
@@ -8,6 +8,7 @@
#include "core/constants.h"
#include "core/installer.hpp"
#include "core/process_utils.hpp"
#include "core/types.h"
#include "core/workspace.hpp"
#include "core/workspace_utils.hpp"
@@ -15,8 +16,6 @@
#include <filesystem>
#include <string>
using namespace cforge;
/**
* @brief Handle the 'install' command: install the current project or specified
* source
@@ -27,7 +26,7 @@ using namespace cforge;
cforge_int_t cforge_cmd_install(const cforge_context_t *ctx) {
// Check if help was requested
if (ctx->args.args) {
for (int i = 0; i < ctx->args.arg_count; i++) {
for (cforge_int_t i = 0; i < ctx->args.arg_count; i++) {
if (strcmp(ctx->args.args[i], "--help") == 0 ||
strcmp(ctx->args.args[i], "-h") == 0) {
// Show install usage
@@ -41,7 +40,7 @@ cforge_int_t cforge_cmd_install(const cforge_context_t *ctx) {
}
// Parse install flags
installer installer_instance;
cforge::installer installer_instance;
std::string project_source;
std::string install_path;
@@ -53,37 +52,37 @@ cforge_int_t cforge_cmd_install(const cforge_context_t *ctx) {
std::string env_var;
if (ctx->args.args) {
for (int i = 0; i < ctx->args.arg_count; ++i) {
for (cforge_int_t i = 0; i < ctx->args.arg_count; ++i) {
std::string arg = ctx->args.args[i];
// Build configuration for install
if ((arg == "--config" || arg == "-c") && i + 1 < ctx->args.arg_count) {
build_config = ctx->args.args[++i];
logger::print_action("Config", build_config);
cforge::logger::print_action("Config", build_config);
continue;
} else if (arg.rfind("--config=", 0) == 0) {
build_config = arg.substr(9);
logger::print_action("Config", build_config);
cforge::logger::print_action("Config", build_config);
continue;
}
if (arg == "--add-to-path") {
add_to_path = true;
logger::print_action("Option", "will add to PATH environment variable");
cforge::logger::print_action("Option", "will add to PATH environment variable");
} else if (arg == "--from" && i + 1 < ctx->args.arg_count) {
project_source = ctx->args.args[++i];
have_from = true;
logger::print_action("Source", project_source);
cforge::logger::print_action("Source", project_source);
} else if (arg == "--to" && i + 1 < ctx->args.arg_count) {
install_path = ctx->args.args[++i];
have_to = true;
logger::print_action("Target", install_path);
cforge::logger::print_action("Target", install_path);
} else if ((arg == "--name" || arg == "-n") &&
i + 1 < ctx->args.arg_count) {
project_name_override = ctx->args.args[++i];
logger::print_action("Name", project_name_override);
cforge::logger::print_action("Name", project_name_override);
} else if (arg == "--env" && i + 1 < ctx->args.arg_count) {
// Set environment variable name for installation
env_var = ctx->args.args[++i];
logger::print_action("Env", env_var);
cforge::logger::print_action("Env", env_var);
} else if (!have_from && project_source.empty() &&
arg.rfind("-", 0) != 0) {
// positional first => source
@@ -98,10 +97,10 @@ cforge_int_t cforge_cmd_install(const cforge_context_t *ctx) {
if (std::filesystem::exists(cwd / CFORGE_FILE) ||
std::filesystem::exists(cwd / WORKSPACE_FILE)) {
project_source = cwd.string();
logger::print_verbose("Detected project in current directory: " +
cforge::logger::print_verbose("Detected project in current directory: " +
project_source);
} else {
logger::print_error("No cforge project or workspace found. Provide "
cforge::logger::print_error("No cforge project or workspace found. Provide "
"source with '--from'.");
return 1;
}
@@ -122,7 +121,7 @@ cforge_int_t cforge_cmd_install(const cforge_context_t *ctx) {
};
if (is_workspace) {
logger::print_action("Building", "workspace before installation");
cforge::logger::print_action("Building", "workspace before installation");
// Build the workspace
cforge_context_t build_ctx;
memset(&build_ctx, 0, sizeof(build_ctx));
@@ -133,46 +132,46 @@ cforge_int_t cforge_cmd_install(const cforge_context_t *ctx) {
if (!build_config.empty()) {
build_ctx.args.config = strdup(build_config.c_str());
}
if (logger::get_verbosity() == log_verbosity::VERBOSITY_VERBOSE) {
if (cforge::logger::get_verbosity() == cforge::log_verbosity::VERBOSITY_VERBOSE) {
build_ctx.args.verbosity = strdup("verbose");
}
int build_res = cforge_cmd_build(&build_ctx);
cforge_int_t build_res = cforge_cmd_build(&build_ctx);
free((void *)build_ctx.args.command);
if (build_ctx.args.config)
free((void *)build_ctx.args.config);
if (build_ctx.args.verbosity)
free((void *)build_ctx.args.verbosity);
if (build_res != 0) {
logger::print_error("Workspace build failed");
cforge::logger::print_error("Workspace build failed");
return build_res;
}
logger::finished("workspace build");
cforge::logger::finished("workspace build");
// Now install projects from build artifacts
logger::installing("workspace projects from " + project_source);
cforge::logger::installing("workspace projects from " + project_source);
// Load workspace.toml and determine main startup project
toml_reader ws_cfg(
cforge::toml_reader ws_cfg(
toml::parse_file((source_path / WORKSPACE_FILE).string()));
std::string main_project = ws_cfg.get_string("workspace.main_project", "");
// Get sorted project list
auto names = get_workspace_projects(source_path);
auto sorted = topo_sort_projects(source_path, names);
auto names = cforge::get_workspace_projects(source_path);
auto sorted = cforge::topo_sort_projects(source_path, names);
// Install libraries and the main executable
for (const auto &name : sorted) {
std::filesystem::path proj_path = source_path / name;
std::filesystem::path cfg = proj_path / CFORGE_FILE;
if (!std::filesystem::exists(cfg)) {
logger::print_warning("Skipping non-project directory: " + name);
cforge::logger::print_warning("Skipping non-project directory: " + name);
continue;
}
toml_reader proj_cfg(toml::parse_file(cfg.string()));
cforge::toml_reader proj_cfg(toml::parse_file(cfg.string()));
std::string proj_type = proj_cfg.get_string("project.type", "executable");
// Skip executables that are not the main startup
if (proj_type == "executable" && name != main_project) {
logger::print_verbose("Skipping non-startup executable project: " +
cforge::logger::print_verbose("Skipping non-startup executable project: " +
name);
continue;
}
logger::installing(name);
cforge::logger::installing(name);
install_proj(proj_path.string());
}
} else {
@@ -189,17 +188,17 @@ cforge_int_t cforge_cmd_install(const cforge_context_t *ctx) {
if (std::filesystem::exists(temp_dir))
std::filesystem::remove_all(temp_dir);
std::filesystem::create_directories(temp_dir);
logger::print_action("Cloning", source);
if (!execute_tool("git", {"clone", source, temp_dir.string()}, "",
cforge::logger::print_action("Cloning", source);
if (!cforge::execute_tool("git", {"clone", source, temp_dir.string()}, "",
"Git Clone", add_to_path)) {
logger::print_error("Git clone failed: " + source);
cforge::logger::print_error("Git clone failed: " + source);
return 1;
}
source = temp_dir.string();
needs_cleanup = true;
}
// Verbose install source logging
logger::print_verbose("Installing project from: " + source);
cforge::logger::print_verbose("Installing project from: " + source);
bool success = installer_instance.install_project(
source, install_path, add_to_path, project_name_override,
build_config, env_var);
@@ -210,12 +209,12 @@ cforge_int_t cforge_cmd_install(const cforge_context_t *ctx) {
}
}
if (!success) {
logger::print_error("Project installation failed");
cforge::logger::print_error("Project installation failed");
return 1;
}
}
logger::finished("project installation");
cforge::logger::finished("project installation");
}
logger::finished("install");
cforge::logger::finished("install");
return 0;
}
@@ -17,8 +17,6 @@
#include <string>
#include <vector>
using namespace cforge;
/**
* @brief Lists available build configurations
*/
@@ -136,12 +134,12 @@ cforge_int_t cforge_cmd_list(const cforge_context_t *ctx) {
list_project_settings();
} else if (category == "projects") {
if (!ctx->is_workspace) {
logger::print_error("Not in a workspace");
cforge::logger::print_error("Not in a workspace");
return 1;
}
workspace ws;
cforge::workspace ws;
if (!ws.load(ctx->working_dir)) {
logger::print_error("Failed to load workspace configuration");
cforge::logger::print_error("Failed to load workspace configuration");
return 1;
}
std::cout << "Workspace projects:\n";
@@ -154,12 +152,12 @@ cforge_int_t cforge_cmd_list(const cforge_context_t *ctx) {
std::cout << "\n";
} else if (category == "order" || category == "build-order") {
if (!ctx->is_workspace) {
logger::print_error("Not in a workspace");
cforge::logger::print_error("Not in a workspace");
return 1;
}
workspace ws;
cforge::workspace ws;
if (!ws.load(ctx->working_dir)) {
logger::print_error("Failed to load workspace configuration");
cforge::logger::print_error("Failed to load workspace configuration");
return 1;
}
std::cout << "Workspace build order:\n";
@@ -170,9 +168,9 @@ cforge_int_t cforge_cmd_list(const cforge_context_t *ctx) {
} else if (category == "dependencies" || category == "deps") {
if (ctx->is_workspace) {
// Workspace project dependencies
workspace ws;
cforge::workspace ws;
if (!ws.load(ctx->working_dir)) {
logger::print_error("Failed to load workspace configuration");
cforge::logger::print_error("Failed to load workspace configuration");
return 1;
}
std::cout << "Workspace project dependencies:\n";
@@ -190,9 +188,9 @@ cforge_int_t cforge_cmd_list(const cforge_context_t *ctx) {
// Project-level dependencies from cforge.toml
std::filesystem::path toml_path =
std::filesystem::path(ctx->working_dir) / CFORGE_FILE;
toml_reader cfg;
cforge::toml_reader cfg;
if (!cfg.load(toml_path.string())) {
logger::print_error("Failed to load cforge.toml");
cforge::logger::print_error("Failed to load cforge.toml");
return 1;
}
if (cfg.has_key("dependencies.vcpkg")) {
@@ -228,12 +226,12 @@ cforge_int_t cforge_cmd_list(const cforge_context_t *ctx) {
}
} else if (category == "graph" || category == "dep-graph") {
if (!ctx->is_workspace) {
logger::print_error("Not in a workspace");
cforge::logger::print_error("Not in a workspace");
return 1;
}
workspace ws;
cforge::workspace ws;
if (!ws.load(ctx->working_dir)) {
logger::print_error("Failed to load workspace configuration");
cforge::logger::print_error("Failed to load workspace configuration");
return 1;
}
// Output Mermaid dependency graph
@@ -259,9 +257,9 @@ cforge_int_t cforge_cmd_list(const cforge_context_t *ctx) {
std::filesystem::path toml_path =
std::filesystem::path(ctx->working_dir) /
(ctx->is_workspace ? WORKSPACE_FILE : CFORGE_FILE);
toml_reader cfg;
cforge::toml_reader cfg;
if (!cfg.load(toml_path.string())) {
logger::print_error("Failed to load configuration: " +
cforge::logger::print_error("Failed to load configuration: " +
toml_path.string());
return 1;
}
@@ -282,7 +280,7 @@ cforge_int_t cforge_cmd_list(const cforge_context_t *ctx) {
}
std::cout << "\n";
} else {
logger::print_error("Unknown list category: " + category);
cforge::logger::print_error("Unknown list category: " + category);
std::cout
<< "Available categories: configs, generators, targets, commands, "
"settings, projects, order, dependencies, graph, scripts\n";
@@ -296,7 +294,7 @@ cforge_int_t cforge_cmd_list(const cforge_context_t *ctx) {
list_build_targets();
list_project_settings();
if (ctx->is_workspace) {
workspace ws;
cforge::workspace ws;
if (ws.load(ctx->working_dir)) {
std::cout << "Workspace projects:\n";
for (const auto &proj : ws.get_projects()) {
@@ -14,6 +14,7 @@
#include "core/lockfile.hpp"
#include "core/registry.hpp"
#include "core/toml_reader.hpp"
#include "core/types.h"
#include "core/workspace.hpp"
#include <cstring>
@@ -21,8 +22,6 @@
#include <fstream>
#include <string>
using namespace cforge;
/**
* @brief Handle the 'lock' command
*/
@@ -35,7 +34,7 @@ cforge_int_t cforge_cmd_lock(const cforge_context_t *ctx) {
bool clean_lock = false;
bool force_update = false;
for (int i = 0; i < ctx->args.arg_count; ++i) {
for (cforge_int_t i = 0; i < ctx->args.arg_count; ++i) {
if (ctx->args.args[i] == nullptr)
continue;
std::string arg = ctx->args.args[i];
@@ -47,22 +46,22 @@ cforge_int_t cforge_cmd_lock(const cforge_context_t *ctx) {
} else if (arg == "--force" || arg == "-f") {
force_update = true;
} else if (arg == "--help" || arg == "-h") {
logger::print_plain("Usage: cforge lock [options]");
logger::print_plain("");
logger::print_plain(
cforge::logger::print_plain("Usage: cforge lock [options]");
cforge::logger::print_plain("");
cforge::logger::print_plain(
"Generate or verify dependency lock file (cforge.lock)");
logger::print_plain("");
logger::print_plain("Options:");
logger::print_plain(
cforge::logger::print_plain("");
cforge::logger::print_plain("Options:");
cforge::logger::print_plain(
" --verify, -v Verify dependencies match lock file");
logger::print_plain(" --clean, -c Remove the lock file");
logger::print_plain(
cforge::logger::print_plain(" --clean, -c Remove the lock file");
cforge::logger::print_plain(
" --force, -f Force regeneration even if lock exists");
logger::print_plain(" --help, -h Show this help message");
logger::print_plain("");
logger::print_plain(
cforge::logger::print_plain(" --help, -h Show this help message");
cforge::logger::print_plain("");
cforge::logger::print_plain(
"The lock file ensures reproducible builds by tracking");
logger::print_plain(
cforge::logger::print_plain(
"exact versions (commit hashes) of all dependencies.");
return 0;
}
@@ -79,33 +78,33 @@ cforge_int_t cforge_cmd_lock(const cforge_context_t *ctx) {
}
if (!std::filesystem::exists(config_path)) {
logger::print_error("No cforge project found in current directory");
logger::print_error("Run 'cforge init' to create a new project");
cforge::logger::print_error("No cforge project found in current directory");
cforge::logger::print_error("Run 'cforge init' to create a new project");
return 1;
}
// Handle --clean option
if (clean_lock) {
std::filesystem::path lock_path = current_dir / LOCK_FILE;
std::filesystem::path lock_path = current_dir / cforge::LOCK_FILE;
if (std::filesystem::exists(lock_path)) {
try {
std::filesystem::remove(lock_path);
logger::removing(std::string(LOCK_FILE));
cforge::logger::removing(std::string(cforge::LOCK_FILE));
} catch (const std::exception &e) {
logger::print_error("Failed to remove lock file: " +
cforge::logger::print_error("Failed to remove lock file: " +
std::string(e.what()));
return 1;
}
} else {
logger::print_action("Skipping", "no lock file to remove");
cforge::logger::print_action("Skipping", "no lock file to remove");
}
return 0;
}
// Load project configuration
toml_reader config;
cforge::toml_reader config;
if (!config.load(config_path.string())) {
logger::print_error("Failed to load configuration: " +
cforge::logger::print_error("Failed to load configuration: " +
config_path.string());
return 1;
}
@@ -117,55 +116,55 @@ cforge_int_t cforge_cmd_lock(const cforge_context_t *ctx) {
// Handle --verify option
if (verify_only) {
logger::print_action("Verifying", "dependencies against lock file");
cforge::logger::print_action("Verifying", "dependencies against lock file");
if (!lockfile::exists(current_dir)) {
logger::print_warning(
if (!cforge::lockfile::exists(current_dir)) {
cforge::logger::print_warning(
"No lock file found. Run 'cforge lock' to create one");
return 1;
}
if (verify_lockfile(current_dir, deps_dir, verbose)) {
logger::print_action("Verified", "all dependencies match lock file");
if (cforge::verify_lockfile(current_dir, deps_dir, verbose)) {
cforge::logger::print_action("Verified", "all dependencies match lock file");
return 0;
} else {
logger::print_error("Dependencies do not match lock file");
logger::print_action(
cforge::logger::print_error("Dependencies do not match lock file");
cforge::logger::print_action(
"Help", "run 'cforge lock' to update, or 'cforge deps' to restore");
return 1;
}
}
// Check if lock file already exists
if (lockfile::exists(current_dir) && !force_update) {
logger::print_action("Checking",
if (cforge::lockfile::exists(current_dir) && !force_update) {
cforge::logger::print_action("Checking",
"lock file already exists. Use --force to regenerate");
// Still verify it
if (verify_lockfile(current_dir, deps_dir, verbose)) {
logger::print_action("Verified", "dependencies match lock file");
if (cforge::verify_lockfile(current_dir, deps_dir, verbose)) {
cforge::logger::print_action("Verified", "dependencies match lock file");
return 0;
} else {
logger::print_warning(
cforge::logger::print_warning(
"Dependencies have changed. Use --force to update lock file");
return 1;
}
}
// Generate/update lock file
logger::print_action("Generating", "lock file");
cforge::logger::print_action("Generating", "lock file");
// Check if using FetchContent mode
bool use_fetch_content = config.get_bool("dependencies.fetch_content", true);
if (use_fetch_content) {
// FetchContent mode: generate lock file from cforge.toml + registry
registry reg;
std::filesystem::path lock_path = current_dir / LOCK_FILE;
cforge::registry reg;
std::filesystem::path lock_path = current_dir / cforge::LOCK_FILE;
std::ofstream lock_file(lock_path);
if (!lock_file.is_open()) {
logger::print_error("Failed to create lock file: " + lock_path.string());
cforge::logger::print_error("Failed to create lock file: " + lock_path.string());
return 1;
}
@@ -229,7 +228,7 @@ cforge_int_t cforge_cmd_lock(const cforge_context_t *ctx) {
has_deps = true;
if (verbose) {
logger::print_verbose("Locked " + key + " @ " + resolved_version);
cforge::logger::print_verbose("Locked " + key + " @ " + resolved_version);
}
}
@@ -253,33 +252,33 @@ cforge_int_t cforge_cmd_lock(const cforge_context_t *ctx) {
lock_file.close();
if (!has_deps) {
logger::print_warning("No dependencies found to lock");
cforge::logger::print_warning("No dependencies found to lock");
std::filesystem::remove(lock_path);
return 0;
}
logger::generated(std::string(LOCK_FILE));
logger::print_action(
cforge::logger::generated(std::string(cforge::LOCK_FILE));
cforge::logger::print_action(
"Note", "commit this file to version control for reproducible builds");
return 0;
}
// Non-FetchContent mode: scan deps directory
if (!std::filesystem::exists(deps_dir)) {
logger::print_warning("Dependencies directory not found: " +
cforge::logger::print_warning("Dependencies directory not found: " +
deps_dir.string());
logger::print_action("Help",
cforge::logger::print_action("Help",
"run 'cforge build' first to fetch dependencies");
return 1;
}
if (update_lockfile(current_dir, deps_dir, verbose)) {
logger::generated(std::string(LOCK_FILE));
logger::print_action(
if (cforge::update_lockfile(current_dir, deps_dir, verbose)) {
cforge::logger::generated(std::string(cforge::LOCK_FILE));
cforge::logger::print_action(
"Note", "commit this file to version control for reproducible builds");
return 0;
} else {
logger::print_error("Failed to create lock file");
cforge::logger::print_error("Failed to create lock file");
return 1;
}
}
@@ -6,6 +6,7 @@
#include "cforge/log.hpp"
#include "core/commands.hpp"
#include "core/toml_reader.hpp"
#include "core/types.h"
#include <algorithm>
#include <filesystem>
@@ -41,7 +42,7 @@ std::string to_pascal_case(const std::string &input) {
*/
std::string to_snake_case(const std::string &input) {
std::string result;
for (size_t i = 0; i < input.length(); i++) {
for (cforge_size_t i = 0; i < input.length(); i++) {
char c = input[i];
if (std::isupper(c)) {
if (i > 0 && !std::isupper(input[i - 1])) {
@@ -336,8 +337,6 @@ bool generate_interface(const fs::path &path, const std::string &interface_name,
* @brief Handle the 'new' command for creating files from templates
*/
cforge_int_t cforge_cmd_new(const cforge_context_t *ctx) {
using namespace cforge;
fs::path project_dir = ctx->working_dir;
// Parse arguments
@@ -347,7 +346,7 @@ cforge_int_t cforge_cmd_new(const cforge_context_t *ctx) {
std::string output_dir;
bool force = false;
for (int i = 0; i < ctx->args.arg_count; i++) {
for (cforge_int_t i = 0; i < ctx->args.arg_count; i++) {
std::string arg = ctx->args.args[i];
if (arg == "-n" || arg == "--namespace") {
if (i + 1 < ctx->args.arg_count) {
@@ -367,34 +366,34 @@ cforge_int_t cforge_cmd_new(const cforge_context_t *ctx) {
}
if (template_type.empty()) {
logger::print_plain("cforge new - Create files from templates");
logger::print_plain("");
logger::print_plain("Usage: cforge new <template> <name> [options]");
logger::print_plain("");
logger::print_plain("Templates:");
logger::print_plain(
cforge::logger::print_plain("cforge new - Create files from templates");
cforge::logger::print_plain("");
cforge::logger::print_plain("Usage: cforge new <template> <name> [options]");
cforge::logger::print_plain("");
cforge::logger::print_plain("Templates:");
cforge::logger::print_plain(
" class Create a class with header and source files");
logger::print_plain(" header Create a header-only file");
logger::print_plain(" struct Create a struct header file");
logger::print_plain(" interface Create an interface (abstract class)");
logger::print_plain(" test Create a test file");
logger::print_plain(" main Create a main.cpp file");
logger::print_plain("");
logger::print_plain("Options:");
logger::print_plain(" -n, --namespace <name> Wrap in namespace");
logger::print_plain(" -o, --output <dir> Output directory");
logger::print_plain(" -f, --force Overwrite existing files");
logger::print_plain("");
logger::print_plain("Examples:");
logger::print_plain(" cforge new class MyClass");
logger::print_plain(" cforge new class MyClass -n myproject");
logger::print_plain(" cforge new header utils -o include/myproject");
logger::print_plain(" cforge new test MyClass --framework catch2");
cforge::logger::print_plain(" header Create a header-only file");
cforge::logger::print_plain(" struct Create a struct header file");
cforge::logger::print_plain(" interface Create an interface (abstract class)");
cforge::logger::print_plain(" test Create a test file");
cforge::logger::print_plain(" main Create a main.cpp file");
cforge::logger::print_plain("");
cforge::logger::print_plain("Options:");
cforge::logger::print_plain(" -n, --namespace <name> Wrap in namespace");
cforge::logger::print_plain(" -o, --output <dir> Output directory");
cforge::logger::print_plain(" -f, --force Overwrite existing files");
cforge::logger::print_plain("");
cforge::logger::print_plain("Examples:");
cforge::logger::print_plain(" cforge new class MyClass");
cforge::logger::print_plain(" cforge new class MyClass -n myproject");
cforge::logger::print_plain(" cforge new header utils -o include/myproject");
cforge::logger::print_plain(" cforge new test MyClass --framework catch2");
return 0;
}
if (name.empty() && template_type != "main") {
logger::print_error("Please specify a name for the " + template_type);
cforge::logger::print_error("Please specify a name for the " + template_type);
return 1;
}
@@ -402,7 +401,7 @@ cforge_int_t cforge_cmd_new(const cforge_context_t *ctx) {
if (namespace_name.empty()) {
fs::path config_file = project_dir / "cforge.toml";
if (fs::exists(config_file)) {
toml_reader config;
cforge::toml_reader config;
config.load(config_file.string());
namespace_name = config.get_string("project.namespace", "");
}
@@ -432,75 +431,75 @@ cforge_int_t cforge_cmd_new(const cforge_context_t *ctx) {
// Check if files exist
if (!force && (fs::exists(header_path) || fs::exists(source_path))) {
logger::print_error("File(s) already exist. Use --force to overwrite.");
cforge::logger::print_error("File(s) already exist. Use --force to overwrite.");
return 1;
}
if (!generate_class_header(header_path, name, namespace_name)) {
logger::print_error("Failed to create " + header_path.string());
cforge::logger::print_error("Failed to create " + header_path.string());
return 1;
}
logger::print_action("Created", header_path.string());
cforge::logger::print_action("Created", header_path.string());
std::string header_include = snake_name + ".hpp";
if (!generate_class_source(source_path, name, header_include,
namespace_name)) {
logger::print_error("Failed to create " + source_path.string());
cforge::logger::print_error("Failed to create " + source_path.string());
return 1;
}
logger::print_action("Created", source_path.string());
cforge::logger::print_action("Created", source_path.string());
} else if (template_type == "header") {
fs::path header_path = include_dir / (snake_name + ".hpp");
fs::create_directories(header_path.parent_path());
if (!force && fs::exists(header_path)) {
logger::print_error("File already exists. Use --force to overwrite.");
cforge::logger::print_error("File already exists. Use --force to overwrite.");
return 1;
}
if (!generate_header(header_path, name, namespace_name)) {
logger::print_error("Failed to create " + header_path.string());
cforge::logger::print_error("Failed to create " + header_path.string());
return 1;
}
logger::print_action("Created", header_path.string());
cforge::logger::print_action("Created", header_path.string());
} else if (template_type == "struct") {
fs::path header_path = include_dir / (snake_name + ".hpp");
fs::create_directories(header_path.parent_path());
if (!force && fs::exists(header_path)) {
logger::print_error("File already exists. Use --force to overwrite.");
cforge::logger::print_error("File already exists. Use --force to overwrite.");
return 1;
}
if (!generate_struct(header_path, name, namespace_name)) {
logger::print_error("Failed to create " + header_path.string());
cforge::logger::print_error("Failed to create " + header_path.string());
return 1;
}
logger::print_action("Created", header_path.string());
cforge::logger::print_action("Created", header_path.string());
} else if (template_type == "interface") {
fs::path header_path = include_dir / (snake_name + ".hpp");
fs::create_directories(header_path.parent_path());
if (!force && fs::exists(header_path)) {
logger::print_error("File already exists. Use --force to overwrite.");
cforge::logger::print_error("File already exists. Use --force to overwrite.");
return 1;
}
if (!generate_interface(header_path, name, namespace_name)) {
logger::print_error("Failed to create " + header_path.string());
cforge::logger::print_error("Failed to create " + header_path.string());
return 1;
}
logger::print_action("Created", header_path.string());
cforge::logger::print_action("Created", header_path.string());
} else if (template_type == "test") {
fs::path test_path = test_dir / ("test_" + snake_name + ".cpp");
fs::create_directories(test_path.parent_path());
if (!force && fs::exists(test_path)) {
logger::print_error("File already exists. Use --force to overwrite.");
cforge::logger::print_error("File already exists. Use --force to overwrite.");
return 1;
}
@@ -508,35 +507,35 @@ cforge_int_t cforge_cmd_new(const cforge_context_t *ctx) {
std::string test_framework = "";
fs::path config_file = project_dir / "cforge.toml";
if (fs::exists(config_file)) {
toml_reader config;
cforge::toml_reader config;
config.load(config_file.string());
test_framework = config.get_string("test.framework", "");
}
if (!generate_test(test_path, name, test_framework)) {
logger::print_error("Failed to create " + test_path.string());
cforge::logger::print_error("Failed to create " + test_path.string());
return 1;
}
logger::print_action("Created", test_path.string());
cforge::logger::print_action("Created", test_path.string());
} else if (template_type == "main") {
fs::path main_path = src_dir / "main.cpp";
fs::create_directories(main_path.parent_path());
if (!force && fs::exists(main_path)) {
logger::print_error("File already exists. Use --force to overwrite.");
cforge::logger::print_error("File already exists. Use --force to overwrite.");
return 1;
}
if (!generate_main(main_path)) {
logger::print_error("Failed to create " + main_path.string());
cforge::logger::print_error("Failed to create " + main_path.string());
return 1;
}
logger::print_action("Created", main_path.string());
cforge::logger::print_action("Created", main_path.string());
} else {
logger::print_error("Unknown template: " + template_type);
logger::print_plain(
cforge::logger::print_error("Unknown template: " + template_type);
cforge::logger::print_plain(
"Available templates: class, header, struct, interface, test, main");
return 1;
}
File diff suppressed because it is too large Load Diff
@@ -10,6 +10,7 @@
#include "core/file_system.h"
#include "core/process_utils.hpp"
#include "core/toml_reader.hpp"
#include "core/types.h"
#include "core/workspace_utils.hpp"
#include <filesystem>
#include <fstream>
@@ -18,8 +19,6 @@
#include <string>
#include <vector>
using namespace cforge;
/**
* @brief Remove a dependency from the project configuration
*
@@ -35,7 +34,7 @@ remove_dependency_from_config(const std::filesystem::path &config_file,
std::string content;
std::ifstream file(config_file);
if (!file) {
logger::print_error("Failed to read configuration file: " +
cforge::logger::print_error("Failed to read configuration file: " +
config_file.string());
return false;
}
@@ -62,7 +61,7 @@ remove_dependency_from_config(const std::filesystem::path &config_file,
// Check if the content was changed
if (result == content) {
logger::print_warning("Dependency '" + package_name +
cforge::logger::print_warning("Dependency '" + package_name +
"' not found in configuration file");
return false;
}
@@ -106,7 +105,7 @@ remove_dependency_from_config(const std::filesystem::path &config_file,
// Write back to file
std::ofstream outfile(config_file);
if (!outfile) {
logger::print_error("Failed to open configuration file for writing: " +
cforge::logger::print_error("Failed to open configuration file for writing: " +
config_file.string());
return false;
}
@@ -115,7 +114,7 @@ remove_dependency_from_config(const std::filesystem::path &config_file,
outfile.close();
if (verbose) {
logger::print_action("Removed", package_name);
cforge::logger::print_action("Removed", package_name);
}
return true;
@@ -159,10 +158,10 @@ static bool remove_package_with_vcpkg(const std::filesystem::path &project_dir,
if (!global_dir.empty() && std::filesystem::exists(global_exe)) {
vcpkg_exe = global_exe;
} else {
logger::print_error(
cforge::logger::print_error(
"vcpkg not found. Checked: " + project_vcpkg_exe.string() + " and " +
global_exe.string());
logger::print_status(
cforge::logger::print_status(
"Run 'cforge vcpkg setup' to set up vcpkg integration");
return false;
}
@@ -173,20 +172,20 @@ static bool remove_package_with_vcpkg(const std::filesystem::path &project_dir,
std::vector<std::string> args = {"remove", package_name};
// Run the command
logger::removing(package_name);
cforge::logger::removing(package_name);
auto result = execute_process(
auto result = cforge::execute_process(
command, args,
"", // working directory
[verbose](const std::string &line) {
if (verbose) {
logger::print_verbose(line);
cforge::logger::print_verbose(line);
}
},
[](const std::string &line) { logger::print_error(line); });
[](const std::string &line) { cforge::logger::print_error(line); });
if (!result.success) {
logger::print_error("Failed to remove package with vcpkg. Exit code: " +
cforge::logger::print_error("Failed to remove package with vcpkg. Exit code: " +
std::to_string(result.exit_code));
return false;
}
@@ -198,9 +197,9 @@ static bool remove_package_with_vcpkg(const std::filesystem::path &project_dir,
static bool remove_git_repo(const std::filesystem::path &project_dir,
const std::string &package_name, bool verbose) {
// Get the configured dependency directory from cforge.toml
toml_reader project_config;
cforge::toml_reader project_config;
if (!project_config.load((project_dir / "cforge.toml").string())) {
logger::print_error("Failed to read project configuration");
cforge::logger::print_error("Failed to read project configuration");
return false;
}
@@ -212,18 +211,18 @@ static bool remove_git_repo(const std::filesystem::path &project_dir,
try {
std::filesystem::remove_all(repo_path);
if (verbose) {
logger::print_action("Removed",
cforge::logger::print_action("Removed",
"cloned git dependency: " + package_name);
}
return true;
} catch (const std::exception &e) {
logger::print_error("Failed to remove cloned git dependency: " +
cforge::logger::print_error("Failed to remove cloned git dependency: " +
std::string(e.what()));
return false;
}
}
if (verbose) {
logger::print_warning("Cloned git dependency not found: " + package_name);
cforge::logger::print_warning("Cloned git dependency not found: " + package_name);
}
return true;
}
@@ -236,7 +235,7 @@ remove_dependency_from_section(const std::filesystem::path &config_file,
// Read existing config file
std::ifstream file(config_file);
if (!file) {
logger::print_error("Failed to read configuration file: " +
cforge::logger::print_error("Failed to read configuration file: " +
config_file.string());
return false;
}
@@ -245,7 +244,7 @@ remove_dependency_from_section(const std::filesystem::path &config_file,
std::string line;
bool in_section = false;
bool found_dependency = false;
int line_to_remove = -1;
cforge_int_t line_to_remove = -1;
// Read all lines and find the dependency
while (std::getline(file, line)) {
@@ -272,7 +271,7 @@ remove_dependency_from_section(const std::filesystem::path &config_file,
if (line_copy == package_name) {
found_dependency = true;
line_to_remove = lines.size();
line_to_remove = static_cast<cforge_int_t>(lines.size());
}
}
@@ -281,7 +280,7 @@ remove_dependency_from_section(const std::filesystem::path &config_file,
file.close();
if (!found_dependency) {
logger::print_warning("Dependency '" + package_name +
cforge::logger::print_warning("Dependency '" + package_name +
"' not found in section [" + section + "]");
return false;
}
@@ -291,7 +290,7 @@ remove_dependency_from_section(const std::filesystem::path &config_file,
lines.erase(lines.begin() + line_to_remove);
// Clean up empty lines around the removed dependency
if (static_cast<size_t>(line_to_remove) < lines.size() && lines[line_to_remove].empty() &&
if (static_cast<cforge_size_t>(line_to_remove) < lines.size() && lines[line_to_remove].empty() &&
(line_to_remove == 0 || lines[line_to_remove - 1].empty())) {
lines.erase(lines.begin() + line_to_remove);
}
@@ -300,13 +299,13 @@ remove_dependency_from_section(const std::filesystem::path &config_file,
// Write back to file
std::ofstream outfile(config_file);
if (!outfile) {
logger::print_error("Failed to write configuration file: " +
cforge::logger::print_error("Failed to write configuration file: " +
config_file.string());
return false;
}
bool last_was_empty = false;
for (size_t i = 0; i < lines.size(); ++i) {
for (cforge_size_t i = 0; i < lines.size(); ++i) {
const auto &l = lines[i];
// Avoid multiple consecutive empty lines
if (l.empty()) {
@@ -322,7 +321,7 @@ remove_dependency_from_section(const std::filesystem::path &config_file,
outfile.close();
if (verbose) {
logger::print_verbose("Removed dependency '" + package_name +
cforge::logger::print_verbose("Removed dependency '" + package_name +
"' from section [" + section + "]");
}
@@ -354,20 +353,20 @@ remove_git_dependency_from_config(const std::filesystem::path &config_file,
cforge_int_t cforge_cmd_remove(const cforge_context_t *ctx) {
// Check if package name was provided
if (ctx->args.arg_count < 1) {
logger::print_error("Package name not specified");
logger::print_status("Usage: cforge remove <package>");
cforge::logger::print_error("Package name not specified");
cforge::logger::print_status("Usage: cforge remove <package>");
return 1;
}
std::string package_name = ctx->args.args[0];
bool verbose = logger::get_verbosity() == log_verbosity::VERBOSITY_VERBOSE;
bool verbose = cforge::logger::get_verbosity() == cforge::log_verbosity::VERBOSITY_VERBOSE;
// Get project directory and config file
std::filesystem::path project_dir = ctx->working_dir;
std::filesystem::path config_file = project_dir / CFORGE_FILE;
if (!std::filesystem::exists(config_file)) {
logger::print_error(
cforge::logger::print_error(
"Not a cforge project directory (cforge.toml not found)");
return 1;
}
@@ -384,7 +383,7 @@ cforge_int_t cforge_cmd_remove(const cforge_context_t *ctx) {
// If it was in git dependencies, also remove the cloned repository
if (git_removed) {
if (!remove_git_repo(project_dir, package_name, verbose)) {
logger::print_warning("Failed to remove git repository for: " +
cforge::logger::print_warning("Failed to remove git repository for: " +
package_name);
}
}
@@ -392,7 +391,7 @@ cforge_int_t cforge_cmd_remove(const cforge_context_t *ctx) {
// If it was in vcpkg dependencies, try to remove the package
if (vcpkg_removed) {
if (!remove_package_with_vcpkg(project_dir, package_name, verbose)) {
logger::print_warning("Failed to remove vcpkg package: " + package_name);
cforge::logger::print_warning("Failed to remove vcpkg package: " + package_name);
}
}
@@ -400,10 +399,10 @@ cforge_int_t cforge_cmd_remove(const cforge_context_t *ctx) {
// fetched during build via FetchContent and stored in build/_deps
if (!git_removed && !vcpkg_removed && !index_removed) {
logger::print_error("Failed to remove dependency: " + package_name);
cforge::logger::print_error("Failed to remove dependency: " + package_name);
return 1;
}
logger::print_action("Removed", package_name);
cforge::logger::print_action("Removed", package_name);
return 0;
}
@@ -10,9 +10,11 @@
#include "core/constants.h"
#include "core/error_format.hpp"
#include "core/file_system.h"
#include "core/platform.hpp"
#include "core/process_utils.hpp"
#include "core/script_runner.hpp"
#include "core/toml_reader.hpp"
#include "core/types.h"
#include "core/workspace.hpp"
#include <algorithm>
@@ -26,8 +28,6 @@
#include <string>
#include <vector>
using namespace cforge;
// Note: get_cmake_generator() and get_build_dir_for_config() are now in
// build_utils.hpp
@@ -44,11 +44,11 @@ static std::filesystem::path
find_project_executable(const std::filesystem::path &project_path,
const std::string &build_dir, const std::string &config,
const std::string &project_name) {
logger::print_verbose("Searching for executable for project: " +
cforge::logger::print_verbose("Searching for executable for project: " +
project_name);
logger::print_verbose("Project path: " + project_path.string());
logger::print_verbose("Build directory: " + build_dir);
logger::print_verbose("Configuration: " + config);
cforge::logger::print_verbose("Project path: " + project_path.string());
cforge::logger::print_verbose("Build directory: " + build_dir);
cforge::logger::print_verbose("Configuration: " + config);
// Convert config to lowercase for directory matching
std::string config_lower = config;
@@ -102,7 +102,7 @@ find_project_executable(const std::filesystem::path &project_path,
std::filesystem::perms::none;
#endif
} catch (const std::exception &ex) {
logger::print_verbose("Error checking executable permissions: " +
cforge::logger::print_verbose("Error checking executable permissions: " +
std::string(ex.what()));
return false;
}
@@ -138,12 +138,12 @@ find_project_executable(const std::filesystem::path &project_path,
continue;
}
logger::print_verbose("Searching in: " + search_path.string());
cforge::logger::print_verbose("Searching in: " + search_path.string());
for (const auto &pattern : executable_patterns) {
std::filesystem::path exe_path = search_path / pattern;
if (std::filesystem::exists(exe_path) && is_valid_executable(exe_path)) {
logger::print_verbose("Found executable: " + exe_path.string());
cforge::logger::print_verbose("Found executable: " + exe_path.string());
return exe_path;
}
}
@@ -164,19 +164,19 @@ find_project_executable(const std::filesystem::path &project_path,
}
if (is_likely_project_executable(entry.path())) {
logger::print_verbose("Found executable: " + entry.path().string());
cforge::logger::print_verbose("Found executable: " + entry.path().string());
return entry.path();
}
}
} catch (const std::exception &ex) {
logger::print_verbose(
cforge::logger::print_verbose(
"Error scanning directory: " + search_path.string() + " - " +
std::string(ex.what()));
}
}
// Final attempt: recursive search in build directory
logger::print_action("Searching", (project_path / build_dir).string());
cforge::logger::print_action("Searching", (project_path / build_dir).string());
try {
for (const auto &entry : std::filesystem::recursive_directory_iterator(
project_path / build_dir)) {
@@ -185,21 +185,21 @@ find_project_executable(const std::filesystem::path &project_path,
}
if (is_likely_project_executable(entry.path())) {
logger::print_verbose("Found executable in recursive search: " +
cforge::logger::print_verbose("Found executable in recursive search: " +
entry.path().string());
return entry.path();
}
}
} catch (const std::exception &ex) {
logger::print_verbose("Error in recursive search: " +
cforge::logger::print_verbose("Error in recursive search: " +
std::string(ex.what()));
}
// List all valid executables found for debugging
logger::print_error("no matching executable found for project: " +
cforge::logger::print_error("no matching executable found for project: " +
project_name);
logger::print_action("Listing", "all executables found");
int found_count = 0;
cforge::logger::print_action("Listing", "all executables found");
cforge_int_t found_count = 0;
for (const auto &search_path : search_paths) {
if (!std::filesystem::exists(search_path)) {
@@ -210,7 +210,7 @@ find_project_executable(const std::filesystem::path &project_path,
for (const auto &entry :
std::filesystem::directory_iterator(search_path)) {
if (is_valid_executable(entry.path())) {
logger::print_action("", " - " + entry.path().string());
cforge::logger::print_action("", " - " + entry.path().string());
found_count++;
}
}
@@ -219,7 +219,7 @@ find_project_executable(const std::filesystem::path &project_path,
}
if (found_count == 0) {
logger::print_action(
cforge::logger::print_action(
"Info",
"no executables found, project might not have been built correctly");
}
@@ -228,144 +228,94 @@ find_project_executable(const std::filesystem::path &project_path,
}
/**
* @brief Get build configuration from various sources
*/
[[maybe_unused]] static std::string get_build_config(const cforge_context_t *ctx,
const toml_reader *project_config) {
// Priority 1: Direct configuration argument
if (ctx->args.config != nullptr && strlen(ctx->args.config) > 0) {
logger::print_verbose("Using build configuration from direct argument: " +
std::string(ctx->args.config));
return std::string(ctx->args.config);
}
// Priority 2: Command line argument
if (ctx->args.args) {
for (int i = 0; i < ctx->args.arg_count; ++i) {
if (strcmp(ctx->args.args[i], "--config") == 0 ||
strcmp(ctx->args.args[i], "-c") == 0) {
if (i + 1 < ctx->args.arg_count) {
logger::print_verbose(
"Using build configuration from command line: " +
std::string(ctx->args.args[i + 1]));
return std::string(ctx->args.args[i + 1]);
}
}
}
}
// Priority 3: Configuration from cforge.toml
if (project_config) {
std::string config = project_config->get_string("build.build_type", "");
if (!config.empty()) {
logger::print_verbose("Using build configuration from cforge.toml: " +
config);
return config;
}
}
// Priority 4: Default to Release
logger::print_verbose(
"No build configuration specified, defaulting to Release");
return "Release";
}
/**
* @brief Build a project before running it
* @brief Build a project before running it (with smart rebuild detection)
*
* Uses the smart rebuild pipeline:
* 1. Check if CMakeLists.txt needs regeneration (cforge.toml changed)
* 2. Check if CMake needs reconfiguration (CMakeCache.txt stale)
* 3. Only regenerate/reconfigure when necessary
* 4. Always run build (CMake handles incremental builds)
*/
static bool build_project_for_run(const std::filesystem::path &project_dir,
const std::string &config, bool verbose) {
std::string build_cmd = "cmake";
// Source directory for CMake
std::filesystem::path source_dir = project_dir;
// If no CMakeLists.txt in project root, generate from cforge.toml
std::filesystem::path project_toml = project_dir / CFORGE_FILE;
if (!std::filesystem::exists(project_dir / "CMakeLists.txt") &&
std::filesystem::exists(project_toml)) {
toml::table tbl;
try {
tbl = toml::parse_file(project_toml.string());
toml_reader proj_cfg(tbl);
if (!generate_cmakelists_from_toml(project_dir, proj_cfg, verbose)) {
logger::print_error(
"failed to generate CMakeLists.txt from cforge.toml");
return false;
}
} catch (...) {
logger::print_error(
"error parsing cforge.toml for automatic CMakeLists generation");
return false;
}
}
std::filesystem::path build_dir = project_dir / "build";
// If top-level CMakeLists.txt missing, try build directory
if (!std::filesystem::exists(project_dir / "CMakeLists.txt")) {
std::filesystem::path build_cmake = build_dir / "CMakeLists.txt";
if (std::filesystem::exists(build_cmake)) {
source_dir = build_dir;
logger::print_verbose("Using CMakeLists.txt from build directory");
} else {
logger::print_error(
"CMakeLists.txt not found in project or build directory");
return false;
}
}
// Create build directory if it doesn't exist
if (!std::filesystem::exists(build_dir)) {
try {
std::filesystem::create_directories(build_dir);
} catch (const std::exception &ex) {
logger::print_error("failed to create build directory: " +
std::string(ex.what()));
return false;
}
}
// Determine build directory
std::filesystem::path build_dir = cforge::get_build_dir_for_config(
(project_dir / "build").string(), config);
// Configure the project
logger::print_action("Configuring", "project");
std::vector<std::string> config_args = {"-S", source_dir.string(), "-B",
build_dir.string(),
"-DCMAKE_BUILD_TYPE=" + config};
// Use smart rebuild detection to prepare the project
cforge::build_preparation_result prep_result = cforge::prepare_project_for_build(
project_dir, build_dir, config, verbose);
bool config_success =
execute_tool(build_cmd, config_args, "", "CMake Configure", verbose);
if (!config_success) {
logger::print_error("failed to configure project");
if (!prep_result.success) {
cforge::logger::print_error(prep_result.error_message);
return false;
}
// Build the project
logger::building(project_dir.filename().string());
std::vector<std::string> build_args = {"--build", build_dir.string(),
"--config", config};
// Log what happened during preparation
if (prep_result.cmakelists_regenerated) {
cforge::logger::print_verbose("CMakeLists.txt was regenerated");
}
if (prep_result.cmake_reconfigured) {
cforge::logger::print_verbose("CMake was reconfigured");
}
bool build_success =
execute_tool(build_cmd, build_args, "", "CMake Build", verbose);
if (!build_success) {
// Always run build (CMake handles incremental builds efficiently)
cforge::logger::building(project_dir.filename().string());
if (!cforge::run_cmake_build(build_dir, config, "", 0, verbose)) {
return false;
}
logger::finished(config);
cforge::logger::finished(config);
return true;
}
// Spawn a command in a new terminal window across platforms
static bool spawn_in_terminal(const std::string &cmd) {
#if defined(_WIN32)
// Use start to open a new Command Prompt
std::string winCmd = "start \"CForge Run\" cmd /k \"" + cmd + "\"";
return std::system(winCmd.c_str()) == 0;
#elif defined(__APPLE__)
// Use AppleScript to open a new Terminal window
std::string osa =
"osascript -e 'tell application \"Terminal\" to do script \"" + cmd +
"\"'";
return std::system(osa.c_str()) == 0;
#else
// Use x-terminal-emulator for Linux (fallback to GNOME Terminal)
std::string linuxCmd = "x-terminal-emulator -e '" + cmd + "' &";
return std::system(linuxCmd.c_str()) == 0;
#endif
if constexpr (cforge::platform::is_windows) {
// Use start to open a new Command Prompt
std::string winCmd = "start \"CForge Run\" cmd /k \"" + cmd + "\"";
return std::system(winCmd.c_str()) == 0;
} else if constexpr (cforge::platform::is_macos) {
// Use AppleScript to open a new Terminal window
std::string osa =
"osascript -e 'tell application \"Terminal\" to do script \"" + cmd +
"\"'";
return std::system(osa.c_str()) == 0;
} else {
// Linux: Try multiple terminal emulators in order of preference
auto terminals = cforge::platform::get_linux_terminals();
for (const auto &terminal : terminals) {
if (cforge::is_command_available(terminal, 3)) {
cforge::logger::print_verbose("Using terminal emulator: " + terminal);
std::string termCmd;
// Different terminals have different argument formats
if (terminal == "gnome-terminal" || terminal == "mate-terminal") {
termCmd = terminal + " -- " + cmd + " &";
} else if (terminal == "konsole") {
termCmd = terminal + " -e " + cmd + " &";
} else if (terminal == "alacritty" || terminal == "kitty") {
termCmd = terminal + " -e " + cmd + " &";
} else {
// Default format works for most terminals
termCmd = terminal + " -e '" + cmd + "' &";
}
cforge_int_t result = std::system(termCmd.c_str());
if (result == 0) {
return true;
}
// If this terminal failed, try the next one
cforge::logger::print_verbose("Terminal " + terminal + " failed, trying next");
}
}
// All terminals failed
cforge::logger::print_warning("No suitable terminal emulator found");
return false;
}
}
cforge_int_t cforge_cmd_run(const cforge_context_t *ctx) {
@@ -383,7 +333,7 @@ cforge_int_t cforge_cmd_run(const cforge_context_t *ctx) {
// Check for --config or -c flag
if (ctx->args.args) {
for (int i = 0; i < ctx->args.arg_count; ++i) {
for (cforge_int_t i = 0; i < ctx->args.arg_count; ++i) {
std::string arg = ctx->args.args[i];
if ((arg == "--config" || arg == "-c") && i + 1 < ctx->args.arg_count) {
config = ctx->args.args[i + 1];
@@ -393,12 +343,12 @@ cforge_int_t cforge_cmd_run(const cforge_context_t *ctx) {
}
// Check verbosity
bool verbose = logger::get_verbosity() == log_verbosity::VERBOSITY_VERBOSE;
bool verbose = cforge::logger::get_verbosity() == cforge::log_verbosity::VERBOSITY_VERBOSE;
// Check if build should be skipped
bool skip_build = false;
if (ctx->args.args) {
for (int i = 0; i < ctx->args.arg_count; ++i) {
for (cforge_int_t i = 0; i < ctx->args.arg_count; ++i) {
if (strcmp(ctx->args.args[i], "--no-build") == 0) {
skip_build = true;
break;
@@ -411,7 +361,7 @@ cforge_int_t cforge_cmd_run(const cforge_context_t *ctx) {
if (ctx->args.project) {
specific_project = ctx->args.project;
} else if (ctx->args.args) {
for (int i = 0; i < ctx->args.arg_count; ++i) {
for (cforge_int_t i = 0; i < ctx->args.arg_count; ++i) {
if ((strcmp(ctx->args.args[i], "--project") == 0 ||
strcmp(ctx->args.args[i], "-p") == 0) &&
i + 1 < ctx->args.arg_count) {
@@ -426,7 +376,7 @@ cforge_int_t cforge_cmd_run(const cforge_context_t *ctx) {
bool found_dash_dash = false;
if (ctx->args.args) {
for (int i = 0; i < ctx->args.arg_count; ++i) {
for (cforge_int_t i = 0; i < ctx->args.arg_count; ++i) {
// Check for delimiter between command args and app args
if (strcmp(ctx->args.args[i], "--") == 0) {
found_dash_dash = true;
@@ -440,22 +390,22 @@ cforge_int_t cforge_cmd_run(const cforge_context_t *ctx) {
}
// Check if we're in a workspace (may be in a subdirectory)
auto [is_workspace, workspace_root] = is_in_workspace(project_dir);
auto [is_workspace, workspace_root] = cforge::is_in_workspace(project_dir);
// Handle workspace-run only when at the workspace root; subprojects fall
// through to single-run
if (is_workspace && project_dir == workspace_root) {
logger::print_action("Running",
cforge::logger::print_action("Running",
"in workspace context: " + project_dir.string());
// Ensure workspace CMakeLists.txt exists (generate if needed)
std::filesystem::path ws_cmake = project_dir / "CMakeLists.txt";
if (!std::filesystem::exists(ws_cmake)) {
logger::print_verbose("Generating workspace CMakeLists.txt for run");
toml_reader ws_cfg(
cforge::logger::print_verbose("Generating workspace CMakeLists.txt for run");
cforge::toml_reader ws_cfg(
toml::parse_file((project_dir / WORKSPACE_FILE).string()));
if (!generate_workspace_cmakelists(project_dir, ws_cfg, verbose)) {
logger::print_error("failed to generate workspace CMakeLists.txt");
if (!cforge::generate_workspace_cmakelists(project_dir, ws_cfg, verbose)) {
cforge::logger::print_error("failed to generate workspace CMakeLists.txt");
return 1;
}
}
@@ -463,8 +413,8 @@ cforge_int_t cforge_cmd_run(const cforge_context_t *ctx) {
// Determine workspace-level build directory
std::filesystem::path ws_build_base = project_dir / DEFAULT_BUILD_DIR;
std::filesystem::path ws_build_dir =
get_build_dir_for_config(ws_build_base.string(), config);
logger::print_verbose("Using workspace build directory: " +
cforge::get_build_dir_for_config(ws_build_base.string(), config);
cforge::logger::print_verbose("Using workspace build directory: " +
ws_build_dir.string());
// Build workspace if needed
@@ -473,7 +423,7 @@ cforge_int_t cforge_cmd_run(const cforge_context_t *ctx) {
if (skip_build &&
!std::filesystem::exists(ws_build_dir / "CMakeCache.txt")) {
need_build = true;
logger::print_action("Info",
cforge::logger::print_action("Info",
"workspace build not found for config '" + config +
"', configuring and building workspace");
}
@@ -488,28 +438,28 @@ cforge_int_t cforge_cmd_run(const cforge_context_t *ctx) {
if (verbose) {
build_ctx.args.verbosity = strdup("verbose");
}
int build_res = cforge_cmd_build(&build_ctx);
cforge_int_t build_res = cforge_cmd_build(&build_ctx);
free((void *)build_ctx.args.command);
free((void *)build_ctx.args.config);
if (build_ctx.args.verbosity)
free((void *)build_ctx.args.verbosity);
if (build_res != 0) {
logger::print_error("workspace build failed");
cforge::logger::print_error("workspace build failed");
return build_res;
}
} else {
logger::print_action("Skipping", "workspace build as requested");
cforge::logger::print_action("Skipping", "workspace build as requested");
}
std::filesystem::path workspace_file = project_dir / WORKSPACE_FILE;
toml_reader workspace_config(toml::parse_file(workspace_file.string()));
cforge::toml_reader workspace_config(toml::parse_file(workspace_file.string()));
if (config.empty()) {
config = workspace_config.get_string("workspace.build_type", "Debug");
}
// Determine startup projects using workspace API
workspace ws;
cforge::workspace ws;
ws.load(workspace_root);
// Collect all projects flagged as startup
std::vector<workspace_project> projects = ws.get_projects();
std::vector<cforge::workspace_project> projects = ws.get_projects();
std::vector<std::string> to_run;
for (const auto &proj : projects) {
if (proj.is_startup) {
@@ -518,13 +468,13 @@ cforge_int_t cforge_cmd_run(const cforge_context_t *ctx) {
}
// Fallback to main_project if none marked
if (to_run.empty()) {
workspace_project main_proj = ws.get_startup_project();
cforge::workspace_project main_proj = ws.get_startup_project();
if (!main_proj.name.empty()) {
to_run.push_back(main_proj.name);
}
}
if (to_run.empty()) {
logger::print_error("no startup project set in workspace");
cforge::logger::print_error("no startup project set in workspace");
return 1;
}
// Run each startup project
@@ -533,18 +483,18 @@ cforge_int_t cforge_cmd_run(const cforge_context_t *ctx) {
// Determine project directory
std::filesystem::path proj_path = project_dir / proj_name;
if (!std::filesystem::exists(proj_path / CFORGE_FILE)) {
logger::print_warning("skipping missing project: " + proj_name);
cforge::logger::print_warning("skipping missing project: " + proj_name);
overall_success = false;
continue;
}
// Load project config to get real name
toml_reader pconf(toml::parse_file((proj_path / CFORGE_FILE).string()));
cforge::toml_reader pconf(toml::parse_file((proj_path / CFORGE_FILE).string()));
std::string real_name = pconf.get_string("project.name", proj_name);
// Find executable
auto exe = find_project_executable(proj_path, ws_build_dir.string(),
config, real_name);
if (exe.empty()) {
logger::print_error("executable not found: " + proj_name);
cforge::logger::print_error("executable not found: " + proj_name);
overall_success = false;
continue;
}
@@ -557,19 +507,19 @@ cforge_int_t cforge_cmd_run(const cforge_context_t *ctx) {
}
// Spawn in new terminal
if (!spawn_in_terminal(oss.str())) {
logger::print_error("failed to spawn terminal for: " + proj_name);
cforge::logger::print_error("failed to spawn terminal for: " + proj_name);
overall_success = false;
}
}
return overall_success ? 0 : 1;
} else {
// Handle single project run
logger::print_action("Running", "in single project context");
cforge::logger::print_action("Running", "in single project context");
// Check if this is a valid cforge project
std::filesystem::path config_path = project_dir / CFORGE_FILE;
if (!std::filesystem::exists(config_path)) {
logger::print_error("not a valid cforge project (missing " +
cforge::logger::print_error("not a valid cforge project (missing " +
std::string(CFORGE_FILE) + ")");
return 1;
}
@@ -579,12 +529,12 @@ cforge_int_t cforge_cmd_run(const cforge_context_t *ctx) {
try {
config_table = toml::parse_file(config_path.string());
} catch (const toml::parse_error &e) {
logger::print_error("failed to parse " + std::string(CFORGE_FILE) +
cforge::logger::print_error("failed to parse " + std::string(CFORGE_FILE) +
": " + std::string(e.what()));
return 1;
}
toml_reader project_config(config_table);
cforge::toml_reader project_config(config_table);
// Get project name
std::string project_name = project_config.get_string("project.name", "");
@@ -593,14 +543,14 @@ cforge_int_t cforge_cmd_run(const cforge_context_t *ctx) {
}
// Log project info
logger::print_action("Project", project_name);
logger::print_action("Configuration", config);
cforge::logger::print_action("Project", project_name);
cforge::logger::print_action("Configuration", config);
// Check binary type
std::string binary_type =
project_config.get_string("project.binary_type", "executable");
if (binary_type != "executable") {
logger::print_error("project is not an executable (binary_type is '" +
cforge::logger::print_error("project is not an executable (binary_type is '" +
binary_type + "')");
return 1;
}
@@ -612,11 +562,11 @@ cforge_int_t cforge_cmd_run(const cforge_context_t *ctx) {
// Build the project if needed
if (!skip_build) {
if (!build_project_for_run(project_dir, config, verbose)) {
logger::print_error("failed to build project");
cforge::logger::print_error("failed to build project");
return 1;
}
} else {
logger::print_action("Skipping", "build step as requested");
cforge::logger::print_action("Skipping", "build step as requested");
}
// Find the executable
@@ -624,15 +574,15 @@ cforge_int_t cforge_cmd_run(const cforge_context_t *ctx) {
project_dir, build_dir_name, config, project_name);
if (executable.empty()) {
logger::print_error("executable not found for project: " +
cforge::logger::print_error("executable not found for project: " +
project_name);
return 1;
}
logger::running(executable.string());
cforge::logger::running(executable.string());
// Display program output header
logger::print_action("", "Program Output\n");
cforge::logger::print_action("", "Program Output\n");
// Capture stderr for runtime error formatting
std::string captured_stderr;
@@ -648,8 +598,8 @@ cforge_int_t cforge_cmd_run(const cforge_context_t *ctx) {
};
// Execute the program with output handling
process_result result =
execute_process(executable.string(), extra_args, project_dir.string(),
cforge::process_result result =
cforge::execute_process(executable.string(), extra_args, project_dir.string(),
stdout_callback, stderr_callback,
0 // No timeout
);
@@ -658,7 +608,7 @@ cforge_int_t cforge_cmd_run(const cforge_context_t *ctx) {
std::cout << std::endl;
if (result.success) {
logger::finished(config);
cforge::logger::finished(config);
return 0;
} else {
// Combine stdout and stderr for error analysis
@@ -667,22 +617,22 @@ cforge_int_t cforge_cmd_run(const cforge_context_t *ctx) {
captured_stderr;
// Try to format any runtime errors
std::string formatted = format_build_errors(combined_output);
std::string formatted = cforge::format_build_errors(combined_output);
if (!formatted.empty()) {
std::cout << "\n";
std::cout << formatted;
}
logger::print_error("program exited with code: " +
cforge::logger::print_error("program exited with code: " +
std::to_string(result.exit_code));
return result.exit_code;
}
}
} catch (const std::exception &ex) {
logger::print_error("exception: " + std::string(ex.what()));
cforge::logger::print_error("exception: " + std::string(ex.what()));
return 1;
} catch (...) {
logger::print_error("unknown exception occurred");
cforge::logger::print_error("unknown exception occurred");
return 1;
}
@@ -6,13 +6,12 @@
#include "cforge/log.hpp"
#include "core/commands.hpp"
#include "core/registry.hpp"
#include "core/types.h"
#include <algorithm>
#include <iomanip>
#include <iostream>
#include <sstream>
using namespace cforge;
/**
* @brief Handle the 'search' command
*
@@ -22,17 +21,17 @@ using namespace cforge;
cforge_int_t cforge_cmd_search(const cforge_context_t *ctx) {
// Parse arguments
std::string query;
size_t limit = 20;
cforge_size_t limit = 20;
bool update_index = false;
for (int i = 0; i < ctx->args.arg_count; ++i) {
for (cforge_int_t i = 0; i < ctx->args.arg_count; ++i) {
std::string arg = ctx->args.args[i];
if (arg == "--limit" || arg == "-l") {
if (i + 1 < ctx->args.arg_count) {
try {
limit = std::stoul(ctx->args.args[++i]);
} catch (...) {
logger::print_error("Invalid limit value");
cforge::logger::print_error("Invalid limit value");
return 1;
}
}
@@ -48,27 +47,27 @@ cforge_int_t cforge_cmd_search(const cforge_context_t *ctx) {
}
if (query.empty()) {
logger::print_error("No search query provided");
logger::print_action("Usage", "cforge search <query> [--limit N] [--update]");
cforge::logger::print_error("No search query provided");
cforge::logger::print_action("Usage", "cforge search <query> [--limit N] [--update]");
return 1;
}
// Initialize registry
registry reg;
cforge::registry reg;
// Update index if needed or requested
if (update_index || reg.needs_update()) {
if (!reg.update(update_index)) {
logger::print_warning("Failed to update package index, using cached version");
cforge::logger::print_warning("Failed to update package index, using cached version");
}
}
// Search for packages
logger::print_action("Searching", "for '" + query + "'");
cforge::logger::print_action("Searching", "for '" + query + "'");
auto results = reg.search(query, limit);
if (results.empty()) {
logger::print_warning("No packages found matching '" + query + "'");
cforge::logger::print_warning("No packages found matching '" + query + "'");
return 0;
}
@@ -76,11 +75,11 @@ cforge_int_t cforge_cmd_search(const cforge_context_t *ctx) {
std::cout << std::endl;
// Find maximum name length for alignment
size_t max_name_len = 0;
cforge_size_t max_name_len = 0;
for (const auto &name : results) {
max_name_len = std::max(max_name_len, name.length());
}
max_name_len = std::min(max_name_len, size_t(30));
max_name_len = std::min(max_name_len, cforge_size_t(30));
// Print each result
for (const auto &name : results) {
@@ -100,7 +99,7 @@ cforge_int_t cforge_cmd_search(const cforge_context_t *ctx) {
// Format description (truncate if needed)
std::string desc = pkg->description;
size_t max_desc_len = 50;
cforge_size_t max_desc_len = 50;
if (desc.length() > max_desc_len) {
desc = desc.substr(0, max_desc_len - 3) + "...";
}
@@ -131,7 +130,7 @@ cforge_int_t cforge_cmd_search(const cforge_context_t *ctx) {
}
std::cout << std::endl;
logger::finished(std::to_string(results.size()) + " package(s) found");
cforge::logger::finished(std::to_string(results.size()) + " package(s) found");
return 0;
}
@@ -14,6 +14,7 @@
#include "core/test_output_formatter.hpp"
#include "core/test_runner.hpp"
#include "core/toml_reader.hpp"
#include "core/types.h"
#include "core/workspace.hpp"
#include <algorithm>
@@ -25,8 +26,6 @@
#include <string>
#include <vector>
using namespace cforge;
namespace {
/**
@@ -40,7 +39,7 @@ void ensure_test_framework_header(const std::filesystem::path &tests_dir) {
return;
}
logger::print_action("Generating", "test framework header: " + header_path.string());
cforge::logger::print_action("Generating", "test framework header: " + header_path.string());
std::ofstream file(header_path);
file << R"(#ifndef TEST_FRAMEWORK_H
@@ -100,8 +99,8 @@ struct TestOptions {
bool no_build = false;
bool list_only = false;
bool verbose = false;
int jobs = 0;
int timeout = 0;
cforge_int_t jobs = 0;
cforge_int_t timeout = 0;
};
TestOptions parse_test_options(const cforge_context_t *ctx) {
@@ -113,7 +112,7 @@ TestOptions parse_test_options(const cforge_context_t *ctx) {
}
// Parse additional arguments
for (int i = 0; i < ctx->args.arg_count; ++i) {
for (cforge_int_t i = 0; i < ctx->args.arg_count; ++i) {
std::string arg = ctx->args.args[i];
if (arg == "--native") {
@@ -140,7 +139,7 @@ TestOptions parse_test_options(const cforge_context_t *ctx) {
}
// Check verbosity from context
if (logger::get_verbosity() == log_verbosity::VERBOSITY_VERBOSE) {
if (cforge::logger::get_verbosity() == cforge::log_verbosity::VERBOSITY_VERBOSE) {
opts.verbose = true;
}
@@ -156,23 +155,23 @@ TestOptions parse_test_options(const cforge_context_t *ctx) {
* @param results_out Output parameter for test results
* @return int Exit code (0 for success)
*/
int run_tests_for_project(const std::filesystem::path &project_dir,
cforge_int_t run_tests_for_project(const std::filesystem::path &project_dir,
const TestOptions &opts,
TestSummary &summary_out,
std::vector<TestResult> &results_out) {
cforge::TestSummary &summary_out,
std::vector<cforge::TestResult> &results_out) {
namespace fs = std::filesystem;
// Load project configuration
toml_reader cfg;
cforge::toml_reader cfg;
if (!cfg.load((project_dir / CFORGE_FILE).string())) {
logger::print_error("Failed to load " CFORGE_FILE " in " + project_dir.string());
cforge::logger::print_error("Failed to load " CFORGE_FILE " in " + project_dir.string());
return 1;
}
// Get project name
std::string project_name = cfg.get_string("project.name", "");
if (project_name.empty()) {
logger::print_error("project.name must be set in " CFORGE_FILE);
cforge::logger::print_error("project.name must be set in " CFORGE_FILE);
return 1;
}
@@ -182,7 +181,7 @@ int run_tests_for_project(const std::filesystem::path &project_dir,
// Create test directory if it doesn't exist
if (!fs::exists(tests_dir)) {
logger::print_action("Creating", "test directory: " + tests_dir.string());
cforge::logger::print_action("Creating", "test directory: " + tests_dir.string());
fs::create_directories(tests_dir);
}
@@ -190,29 +189,29 @@ int run_tests_for_project(const std::filesystem::path &project_dir,
ensure_test_framework_header(tests_dir);
// Create test runner
TestRunner runner(project_dir, cfg);
cforge::TestRunner runner(project_dir, cfg);
if (!runner.load_config()) {
logger::print_error("Failed to load test configuration for " + project_name);
cforge::logger::print_error("Failed to load test configuration for " + project_name);
return 1;
}
// Discover test targets
auto targets = runner.discover_targets();
if (targets.empty()) {
logger::print_verbose("No test targets found in " + project_name);
cforge::logger::print_verbose("No test targets found in " + project_name);
return 0; // Not an error - project just has no tests
}
// List mode
if (opts.list_only) {
auto tests = runner.list_tests();
TestOutputFormatter formatter(TestOutputFormatter::Style::Cargo);
cforge::TestOutputFormatter formatter(cforge::TestOutputFormatter::Style::Cargo);
formatter.print_test_list(tests);
return 0;
}
// Run tests
TestRunOptions run_opts;
cforge::TestRunOptions run_opts;
run_opts.build_config = opts.build_config;
run_opts.filter = opts.filter;
run_opts.native_output = opts.native_output;
@@ -265,37 +264,37 @@ cforge_int_t cforge_cmd_test(const cforge_context_t *ctx) {
TestOptions opts = parse_test_options(ctx);
// Check if we're in a workspace
auto [is_ws, workspace_dir] = is_in_workspace(current_dir);
auto [is_ws, workspace_dir] = cforge::is_in_workspace(current_dir);
// If we're at the workspace root, run tests for all projects
if (is_ws && current_dir == workspace_dir) {
workspace ws;
cforge::workspace ws;
if (!ws.load(workspace_dir)) {
logger::print_error("Failed to load workspace configuration");
cforge::logger::print_error("Failed to load workspace configuration");
return 1;
}
logger::print_header("Running tests for workspace: " + ws.get_name());
cforge::logger::print_header("Running tests for workspace: " + ws.get_name());
// Get projects in build order (respects dependencies)
auto build_order = ws.get_build_order();
auto projects = ws.get_projects();
// Aggregate results across all projects
TestSummary total_summary{};
std::vector<TestResult> all_results;
int projects_tested = 0;
int projects_failed = 0;
cforge::TestSummary total_summary{};
std::vector<cforge::TestResult> all_results;
cforge_int_t projects_tested = 0;
cforge_int_t projects_failed = 0;
// Create formatter for output
TestOutputFormatter formatter(
opts.native_output ? TestOutputFormatter::Style::Native
: TestOutputFormatter::Style::Cargo);
cforge::TestOutputFormatter formatter(
opts.native_output ? cforge::TestOutputFormatter::Style::Native
: cforge::TestOutputFormatter::Style::Cargo);
for (const auto &project_name : build_order) {
// Find the project
auto it = std::find_if(projects.begin(), projects.end(),
[&project_name](const workspace_project &p) {
[&project_name](const cforge::workspace_project &p) {
return p.name == project_name;
});
@@ -307,26 +306,26 @@ cforge_int_t cforge_cmd_test(const cforge_context_t *ctx) {
// Check if project has cforge.toml
if (!fs::exists(project.path / CFORGE_FILE)) {
logger::print_verbose("Skipping " + project.name + " (no cforge.toml)");
cforge::logger::print_verbose("Skipping " + project.name + " (no cforge.toml)");
continue;
}
// Check if project has tests directory
toml_reader proj_cfg;
cforge::toml_reader proj_cfg;
if (proj_cfg.load((project.path / CFORGE_FILE).string())) {
std::string test_dir = proj_cfg.get_string("test.directory", "tests");
if (!fs::exists(project.path / test_dir)) {
logger::print_verbose("Skipping " + project.name + " (no tests directory)");
cforge::logger::print_verbose("Skipping " + project.name + " (no tests directory)");
continue;
}
}
logger::print_action("Testing", project.name);
cforge::logger::print_action("Testing", project.name);
TestSummary project_summary{};
std::vector<TestResult> project_results;
cforge::TestSummary project_summary{};
std::vector<cforge::TestResult> project_results;
int result = run_tests_for_project(project.path, opts, project_summary, project_results);
cforge_int_t result = run_tests_for_project(project.path, opts, project_summary, project_results);
// Aggregate results
total_summary.passed += project_summary.passed;
@@ -350,10 +349,10 @@ cforge_int_t cforge_cmd_test(const cforge_context_t *ctx) {
// Print aggregated results
if (!opts.native_output && !opts.list_only) {
fmt::print("\n");
logger::print_header("Workspace Test Summary");
cforge::logger::print_header("Workspace Test Summary");
// Print header with actual test count
formatter.print_run_start(static_cast<int>(all_results.size()));
formatter.print_run_start(static_cast<cforge_int_t>(all_results.size()));
for (const auto &result : all_results) {
formatter.print_test_result(result);
@@ -379,16 +378,16 @@ cforge_int_t cforge_cmd_test(const cforge_context_t *ctx) {
// Single project mode - original behavior
fs::path project_dir = current_dir;
toml_reader cfg;
cforge::toml_reader cfg;
if (!cfg.load((project_dir / CFORGE_FILE).string())) {
logger::print_error("Failed to load " CFORGE_FILE);
cforge::logger::print_error("Failed to load " CFORGE_FILE);
return 1;
}
// Get project name
std::string project_name = cfg.get_string("project.name", "");
if (project_name.empty()) {
logger::print_error("project.name must be set in " CFORGE_FILE);
cforge::logger::print_error("project.name must be set in " CFORGE_FILE);
return 1;
}
@@ -398,7 +397,7 @@ cforge_int_t cforge_cmd_test(const cforge_context_t *ctx) {
// Create test directory if it doesn't exist
if (!fs::exists(tests_dir)) {
logger::print_action("Creating", "test directory: " + tests_dir.string());
cforge::logger::print_action("Creating", "test directory: " + tests_dir.string());
fs::create_directories(tests_dir);
}
@@ -406,23 +405,23 @@ cforge_int_t cforge_cmd_test(const cforge_context_t *ctx) {
ensure_test_framework_header(tests_dir);
// Create test runner
TestRunner runner(project_dir, cfg);
cforge::TestRunner runner(project_dir, cfg);
if (!runner.load_config()) {
logger::print_error("Failed to load test configuration");
cforge::logger::print_error("Failed to load test configuration");
return 1;
}
// Create output formatter
TestOutputFormatter formatter(
opts.native_output ? TestOutputFormatter::Style::Native
: TestOutputFormatter::Style::Cargo);
cforge::TestOutputFormatter formatter(
opts.native_output ? cforge::TestOutputFormatter::Style::Native
: cforge::TestOutputFormatter::Style::Cargo);
// Discover test targets
auto targets = runner.discover_targets();
if (targets.empty()) {
logger::print_warning("No test targets found");
logger::print_status("Create test files in '" + test_dir + "/' directory");
logger::print_status("Or add [[test.targets]] to cforge.toml");
cforge::logger::print_warning("No test targets found");
cforge::logger::print_status("Create test files in '" + test_dir + "/' directory");
cforge::logger::print_status("Or add [[test.targets]] to cforge.toml");
return 0;
}
@@ -434,7 +433,7 @@ cforge_int_t cforge_cmd_test(const cforge_context_t *ctx) {
}
// Run tests
TestRunOptions run_opts;
cforge::TestRunOptions run_opts;
run_opts.build_config = opts.build_config;
run_opts.filter = opts.filter;
run_opts.native_output = opts.native_output;
@@ -445,13 +444,13 @@ cforge_int_t cforge_cmd_test(const cforge_context_t *ctx) {
run_opts.timeout_override = opts.timeout;
// Execute tests
TestSummary summary = runner.run_tests(run_opts);
cforge::TestSummary summary = runner.run_tests(run_opts);
const auto &results = runner.get_results();
// Print results (unless native output, which prints as it runs)
if (!opts.native_output) {
// Print header with actual test count
formatter.print_run_start(static_cast<int>(results.size()));
formatter.print_run_start(static_cast<cforge_int_t>(results.size()));
for (const auto &result : results) {
formatter.print_test_result(result);
@@ -7,6 +7,7 @@
#include "core/commands.hpp"
#include "core/process_utils.hpp"
#include "core/toml_reader.hpp"
#include "core/types.h"
#include <algorithm>
#include <filesystem>
@@ -390,8 +391,6 @@ complete -c cforge -n '__fish_seen_subcommand_from lint' -l checks -d 'Checks to
* @brief Handle the 'fmt' command for code formatting
*/
cforge_int_t cforge_cmd_fmt(const cforge_context_t *ctx) {
using namespace cforge;
fs::path project_dir = ctx->working_dir;
// Parse arguments
@@ -399,7 +398,7 @@ cforge_int_t cforge_cmd_fmt(const cforge_context_t *ctx) {
bool dry_run = false;
std::string style = "file"; // Default: use .clang-format file
for (int i = 0; i < ctx->args.arg_count; i++) {
for (cforge_int_t i = 0; i < ctx->args.arg_count; i++) {
std::string arg = ctx->args.args[i];
if (arg == "--check") {
check_only = true;
@@ -413,12 +412,12 @@ cforge_int_t cforge_cmd_fmt(const cforge_context_t *ctx) {
// Find clang-format
std::string clang_format = find_clang_format();
if (clang_format.empty()) {
logger::print_error("clang-format not found in PATH");
logger::print_status("Install clang-format or add it to your PATH");
cforge::logger::print_error("clang-format not found in PATH");
cforge::logger::print_status("Install clang-format or add it to your PATH");
return 1;
}
logger::print_action("Formatting", "source files with " + clang_format);
cforge::logger::print_action("Formatting", "source files with " + clang_format);
// Find source files
std::vector<fs::path> files;
@@ -431,14 +430,14 @@ cforge_int_t cforge_cmd_fmt(const cforge_context_t *ctx) {
}
if (files.empty()) {
logger::print_warning("No source files found to format");
cforge::logger::print_warning("No source files found to format");
return 0;
}
logger::print_status("Found " + std::to_string(files.size()) + " files");
cforge::logger::print_status("Found " + std::to_string(files.size()) + " files");
int formatted_count = 0;
int failed_count = 0;
cforge_int_t formatted_count = 0;
cforge_int_t failed_count = 0;
for (const auto &file : files) {
std::vector<std::string> args;
@@ -455,37 +454,37 @@ cforge_int_t cforge_cmd_fmt(const cforge_context_t *ctx) {
args.push_back(file.string());
auto result = execute_process(clang_format, args, project_dir.string());
auto result = cforge::execute_process(clang_format, args, project_dir.string());
if (result.exit_code == 0) {
formatted_count++;
if (!check_only && !dry_run) {
logger::print_verbose("Formatted: " + file.filename().string());
cforge::logger::print_verbose("Formatted: " + file.filename().string());
}
} else {
failed_count++;
if (check_only) {
logger::print_warning("Needs formatting: " + file.string());
cforge::logger::print_warning("Needs formatting: " + file.string());
} else {
logger::print_error("Failed to format: " + file.string());
cforge::logger::print_error("Failed to format: " + file.string());
}
}
}
if (check_only) {
if (failed_count > 0) {
logger::print_error(std::to_string(failed_count) +
cforge::logger::print_error(std::to_string(failed_count) +
" file(s) need formatting");
logger::print_status("Run 'cforge fmt' to format them");
cforge::logger::print_status("Run 'cforge fmt' to format them");
return 1;
} else {
logger::print_success("All files are properly formatted");
cforge::logger::print_success("All files are properly formatted");
}
} else if (dry_run) {
logger::print_status("Would format " + std::to_string(formatted_count) +
cforge::logger::print_status("Would format " + std::to_string(formatted_count) +
" file(s)");
} else {
logger::finished(
cforge::logger::finished(
"formatted " + std::to_string(formatted_count) + " file(s)", "");
}
@@ -496,8 +495,6 @@ cforge_int_t cforge_cmd_fmt(const cforge_context_t *ctx) {
* @brief Handle the 'lint' command for static analysis
*/
cforge_int_t cforge_cmd_lint(const cforge_context_t *ctx) {
using namespace cforge;
fs::path project_dir = ctx->working_dir;
fs::path build_dir = project_dir / "build";
@@ -505,7 +502,7 @@ cforge_int_t cforge_cmd_lint(const cforge_context_t *ctx) {
bool fix = false;
std::string checks = "";
for (int i = 0; i < ctx->args.arg_count; i++) {
for (cforge_int_t i = 0; i < ctx->args.arg_count; i++) {
std::string arg = ctx->args.args[i];
if (arg == "--fix") {
fix = true;
@@ -517,18 +514,18 @@ cforge_int_t cforge_cmd_lint(const cforge_context_t *ctx) {
// Find clang-tidy
std::string clang_tidy = find_clang_tidy();
if (clang_tidy.empty()) {
logger::print_error("clang-tidy not found in PATH");
logger::print_status("Install clang-tidy or add it to your PATH");
cforge::logger::print_error("clang-tidy not found in PATH");
cforge::logger::print_status("Install clang-tidy or add it to your PATH");
return 1;
}
logger::print_action("Analyzing", "source files with " + clang_tidy);
cforge::logger::print_action("Analyzing", "source files with " + clang_tidy);
// Check for compile_commands.json
fs::path compile_commands = build_dir / "compile_commands.json";
if (!fs::exists(compile_commands)) {
logger::print_warning("compile_commands.json not found");
logger::print_status(
cforge::logger::print_warning("compile_commands.json not found");
cforge::logger::print_status(
"Building project first to generate compilation database...");
// Try to generate compile_commands.json
@@ -536,10 +533,10 @@ cforge_int_t cforge_cmd_lint(const cforge_context_t *ctx) {
"-B", build_dir.string(), "-S", project_dir.string(),
"-DCMAKE_EXPORT_COMPILE_COMMANDS=ON"};
auto result = execute_process("cmake", cmake_args, project_dir.string());
auto result = cforge::execute_process("cmake", cmake_args, project_dir.string());
if (result.exit_code != 0 || !fs::exists(compile_commands)) {
logger::print_error("Could not generate compile_commands.json");
logger::print_status(
cforge::logger::print_error("Could not generate compile_commands.json");
cforge::logger::print_status(
"Run 'cforge build' first, or create compile_commands.json manually");
return 1;
}
@@ -554,15 +551,15 @@ cforge_int_t cforge_cmd_lint(const cforge_context_t *ctx) {
}
if (files.empty()) {
logger::print_warning("No source files found to analyze");
cforge::logger::print_warning("No source files found to analyze");
return 0;
}
logger::print_status("Analyzing " + std::to_string(files.size()) +
cforge::logger::print_status("Analyzing " + std::to_string(files.size()) +
" file(s)...");
int warnings = 0;
int errors = 0;
cforge_int_t warnings = 0;
cforge_int_t errors = 0;
for (const auto &file : files) {
std::vector<std::string> args;
@@ -579,9 +576,9 @@ cforge_int_t cforge_cmd_lint(const cforge_context_t *ctx) {
args.push_back(file.string());
logger::print_verbose("Checking: " + file.filename().string());
cforge::logger::print_verbose("Checking: " + file.filename().string());
auto result = execute_process(
auto result = cforge::execute_process(
clang_tidy, args, project_dir.string(), [&](const std::string &line) {
// Parse clang-tidy output
if (line.find("warning:") != std::string::npos) {
@@ -599,22 +596,22 @@ cforge_int_t cforge_cmd_lint(const cforge_context_t *ctx) {
// Summary
fmt::print("\n");
if (errors > 0 || warnings > 0) {
logger::print_status("Analysis complete:");
cforge::logger::print_status("Analysis complete:");
if (errors > 0) {
logger::print_error(std::to_string(errors) + " error(s)");
cforge::logger::print_error(std::to_string(errors) + " error(s)");
}
if (warnings > 0) {
logger::print_warning(std::to_string(warnings) + " warning(s)");
cforge::logger::print_warning(std::to_string(warnings) + " warning(s)");
}
if (fix) {
logger::print_status("Some issues may have been automatically fixed");
cforge::logger::print_status("Some issues may have been automatically fixed");
} else {
logger::print_status(
cforge::logger::print_status(
"Run 'cforge lint --fix' to automatically fix some issues");
}
return errors > 0 ? 1 : 0;
} else {
logger::print_success("No issues found");
cforge::logger::print_success("No issues found");
return 0;
}
}
@@ -623,12 +620,10 @@ cforge_int_t cforge_cmd_lint(const cforge_context_t *ctx) {
* @brief Handle the 'completions' command
*/
cforge_int_t cforge_cmd_completions(const cforge_context_t *ctx) {
using namespace cforge;
std::string shell = "bash"; // Default
// Parse shell argument
for (int i = 0; i < ctx->args.arg_count; i++) {
for (cforge_int_t i = 0; i < ctx->args.arg_count; i++) {
std::string arg = ctx->args.args[i];
if (arg == "bash" || arg == "zsh" || arg == "powershell" || arg == "fish" ||
arg == "ps" || arg == "ps1") {
@@ -655,8 +650,8 @@ cforge_int_t cforge_cmd_completions(const cforge_context_t *ctx) {
script = generate_fish_completions();
install_hint = "Save to ~/.config/fish/completions/cforge.fish";
} else {
logger::print_error("Unknown shell: " + shell);
logger::print_status("Supported shells: bash, zsh, powershell, fish");
cforge::logger::print_error("Unknown shell: " + shell);
cforge::logger::print_status("Supported shells: bash, zsh, powershell, fish");
return 1;
}
@@ -6,6 +6,7 @@
#include "cforge/log.hpp"
#include "core/commands.hpp"
#include "core/toml_reader.hpp"
#include "core/types.h"
#include "core/workspace.hpp"
#include <filesystem>
@@ -31,10 +32,8 @@ struct dependency_info {
void print_tree_branch(const std::string &name, const dependency_info &info,
const std::map<std::string, dependency_info> &all_deps,
const std::string &prefix, bool is_last,
std::set<std::string> &visited, int max_depth,
int current_depth) {
using namespace cforge;
std::set<std::string> &visited, cforge_int_t max_depth,
cforge_int_t current_depth) {
if (current_depth > max_depth)
return;
if (visited.count(name)) {
@@ -71,7 +70,7 @@ void print_tree_branch(const std::string &name, const dependency_info &info,
// Print children
std::string child_prefix = prefix + (is_last ? " " : "| ");
for (size_t i = 0; i < info.children.size(); i++) {
for (cforge_size_t i = 0; i < info.children.size(); i++) {
const auto &child = info.children[i];
bool child_is_last = (i == info.children.size() - 1);
@@ -122,7 +121,7 @@ void print_tree_branch(const std::string &name, const dependency_info &info,
for (const auto &dep : vcpkg_deps) {
dependency_info info;
// Parse name:version format
size_t colon = dep.find(':');
cforge_size_t colon = dep.find(':');
if (colon != std::string::npos) {
info.name = dep.substr(0, colon);
info.version = dep.substr(colon + 1);
@@ -163,16 +162,14 @@ void print_tree_branch(const std::string &name, const dependency_info &info,
* @brief Handle the 'tree' command for visualizing dependencies
*/
cforge_int_t cforge_cmd_tree(const cforge_context_t *ctx) {
using namespace cforge;
fs::path current_dir = ctx->working_dir;
// Parse arguments
[[maybe_unused]] bool show_all = false;
int max_depth = 10;
cforge_int_t max_depth = 10;
[[maybe_unused]] bool inverted = false;
for (int i = 0; i < ctx->args.arg_count; i++) {
for (cforge_int_t i = 0; i < ctx->args.arg_count; i++) {
std::string arg = ctx->args.args[i];
if (arg == "-a" || arg == "--all") {
show_all = true;
@@ -186,16 +183,16 @@ cforge_int_t cforge_cmd_tree(const cforge_context_t *ctx) {
}
// Check for workspace or project
auto [is_workspace, workspace_dir] = is_in_workspace(current_dir);
auto [is_workspace, workspace_dir] = cforge::is_in_workspace(current_dir);
std::map<std::string, dependency_info> all_deps;
std::vector<std::pair<std::string, dependency_info>> roots;
if (is_workspace) {
// Load workspace
workspace ws;
cforge::workspace ws;
if (!ws.load(workspace_dir)) {
logger::print_error("Failed to load workspace");
cforge::logger::print_error("Failed to load workspace");
return 1;
}
@@ -206,7 +203,7 @@ cforge_int_t cforge_cmd_tree(const cforge_context_t *ctx) {
for (const auto &proj : ws.get_projects()) {
fs::path proj_toml = proj.path / "cforge.toml";
if (fs::exists(proj_toml)) {
toml_reader config;
cforge::toml_reader config;
config.load(proj_toml.string());
dependency_info proj_info;
@@ -230,11 +227,11 @@ cforge_int_t cforge_cmd_tree(const cforge_context_t *ctx) {
// Single project
fs::path config_file = current_dir / "cforge.toml";
if (!fs::exists(config_file)) {
logger::print_error("No cforge.toml found in current directory");
cforge::logger::print_error("No cforge.toml found in current directory");
return 1;
}
toml_reader config;
cforge::toml_reader config;
config.load(config_file.string());
std::string project_name =
@@ -263,7 +260,7 @@ cforge_int_t cforge_cmd_tree(const cforge_context_t *ctx) {
// Print tree
std::set<std::string> visited;
for (size_t i = 0; i < roots.size(); i++) {
for (cforge_size_t i = 0; i < roots.size(); i++) {
bool is_last = (i == roots.size() - 1);
print_tree_branch(roots[i].first, roots[i].second, all_deps, "", is_last,
visited, max_depth, 0);
@@ -271,7 +268,7 @@ cforge_int_t cforge_cmd_tree(const cforge_context_t *ctx) {
// Print summary
fmt::print("\n");
int git_count = 0, vcpkg_count = 0, sys_count = 0, proj_count = 0;
cforge_int_t git_count = 0, vcpkg_count = 0, sys_count = 0, proj_count = 0;
for (const auto &[name, info] : all_deps) {
if (info.type == "git")
git_count++;
@@ -295,7 +292,7 @@ cforge_int_t cforge_cmd_tree(const cforge_context_t *ctx) {
if (!summary_parts.empty()) {
fmt::print("Dependencies: ");
for (size_t i = 0; i < summary_parts.size(); i++) {
for (cforge_size_t i = 0; i < summary_parts.size(); i++) {
if (i > 0)
fmt::print(", ");
fmt::print("{}", summary_parts[i]);
@@ -10,13 +10,12 @@
#include "core/process_utils.hpp"
#include "core/registry.hpp"
#include "core/toml_reader.hpp"
#include "core/types.h"
#include <filesystem>
#include <map>
#include <string>
#include <vector>
using namespace cforge;
/**
* @brief Handle the 'update' command
*
@@ -25,7 +24,7 @@ using namespace cforge;
*/
cforge_int_t cforge_cmd_update(const cforge_context_t *ctx) {
std::filesystem::path cwd = ctx->working_dir;
installer installer_instance;
cforge::installer installer_instance;
// Parse flags
std::string install_path;
@@ -33,7 +32,7 @@ cforge_int_t cforge_cmd_update(const cforge_context_t *ctx) {
bool update_self = false;
bool update_packages = false;
for (int i = 0; i < ctx->args.arg_count; ++i) {
for (cforge_int_t i = 0; i < ctx->args.arg_count; ++i) {
std::string arg = ctx->args.args[i];
if (arg == "--self" || arg == "-s") {
update_self = true;
@@ -50,23 +49,23 @@ cforge_int_t cforge_cmd_update(const cforge_context_t *ctx) {
// Require explicit flag
if (!update_self && !update_packages) {
logger::print_error("Please specify what to update:");
logger::print_action("--self, -s", "Update cforge itself");
logger::print_action("--packages, -p", "Update the package registry index");
logger::print_action("Usage", "cforge update --self");
logger::print_action("Usage", "cforge update --packages");
cforge::logger::print_error("Please specify what to update:");
cforge::logger::print_action("--self, -s", "Update cforge itself");
cforge::logger::print_action("--packages, -p", "Update the package registry index");
cforge::logger::print_action("Usage", "cforge update --self");
cforge::logger::print_action("Usage", "cforge update --packages");
return 1;
}
// Cannot use both flags at once
if (update_self && update_packages) {
logger::print_error("Cannot use both --self and --packages at the same time");
cforge::logger::print_error("Cannot use both --self and --packages at the same time");
return 1;
}
if (update_self) {
// Self-update: clone, build, and install from GitHub
logger::print_header("Updating cforge itself");
cforge::logger::print_header("Updating cforge itself");
// Determine install location
if (install_path.empty()) {
@@ -78,7 +77,7 @@ cforge_int_t cforge_cmd_update(const cforge_context_t *ctx) {
install_path = installer_instance.get_default_install_path();
}
}
logger::print_action("Install path", install_path);
cforge::logger::print_action("Install path", install_path);
// Prepare temporary clone directory
std::filesystem::path temp_dir =
@@ -89,45 +88,45 @@ cforge_int_t cforge_cmd_update(const cforge_context_t *ctx) {
// Clone repository
const std::string repo_url = "https://github.com/ChaseSunstrom/cforge.git";
logger::print_action("Cloning", "cforge from GitHub: " + repo_url);
bool verbose = logger::get_verbosity() == log_verbosity::VERBOSITY_VERBOSE;
if (!execute_tool("git",
cforge::logger::print_action("Cloning", "cforge from GitHub: " + repo_url);
bool verbose = cforge::logger::get_verbosity() == cforge::log_verbosity::VERBOSITY_VERBOSE;
if (!cforge::execute_tool("git",
{"clone", "--branch", "master", repo_url, temp_dir.string()},
"", "Git Clone", verbose)) {
logger::print_error("Failed to clone cforge repository");
cforge::logger::print_error("Failed to clone cforge repository");
return 1;
}
// Fetch dependencies
logger::print_action("Fetching", "dependencies");
cforge::logger::print_action("Fetching", "dependencies");
std::filesystem::path vendor_dir = temp_dir / "vendor";
std::filesystem::create_directories(vendor_dir);
if (!execute_tool("git",
if (!cforge::execute_tool("git",
{"clone", "https://github.com/fmtlib/fmt.git",
(vendor_dir / "fmt").string()},
"", "Clone fmt", verbose)) {
logger::print_error("Failed to clone fmt dependency");
cforge::logger::print_error("Failed to clone fmt dependency");
return 1;
}
// Checkout fmt version
execute_tool("git", {"checkout", "11.1.4"}, (vendor_dir / "fmt").string(),
cforge::execute_tool("git", {"checkout", "11.1.4"}, (vendor_dir / "fmt").string(),
"Checkout fmt", verbose);
if (!execute_tool("git",
if (!cforge::execute_tool("git",
{"clone", "https://github.com/marzer/tomlplusplus.git",
(vendor_dir / "tomlplusplus").string()},
"", "Clone tomlplusplus", verbose)) {
logger::print_error("Failed to clone tomlplusplus dependency");
cforge::logger::print_error("Failed to clone tomlplusplus dependency");
return 1;
}
// Checkout tomlplusplus version
execute_tool("git", {"checkout", "v3.4.0"},
cforge::execute_tool("git", {"checkout", "v3.4.0"},
(vendor_dir / "tomlplusplus").string(), "Checkout tomlplusplus",
verbose);
// Configure with CMake
logger::print_action("Configuring", "build with CMake");
cforge::logger::print_action("Configuring", "build with CMake");
std::filesystem::path build_dir = temp_dir / "build";
std::filesystem::create_directories(build_dir);
@@ -138,25 +137,25 @@ cforge_int_t cforge_cmd_update(const cforge_context_t *ctx) {
#ifdef _WIN32
// Use Ninja if available on Windows
if (is_command_available("ninja")) {
if (cforge::is_command_available("ninja")) {
cmake_args.push_back("-G");
cmake_args.push_back("Ninja");
}
#endif
if (!execute_tool("cmake", cmake_args, "", "CMake Configure", verbose, 180)) {
logger::print_error("CMake configuration failed");
if (!cforge::execute_tool("cmake", cmake_args, "", "CMake Configure", verbose, 180)) {
cforge::logger::print_error("CMake configuration failed");
std::filesystem::remove_all(temp_dir);
return 1;
}
// Build
logger::print_action("Building", "cforge");
cforge::logger::print_action("Building", "cforge");
std::vector<std::string> build_args = {"--build", build_dir.string(),
"--config", "Release"};
if (!execute_tool("cmake", build_args, "", "CMake Build", verbose, 600)) {
logger::print_error("Build failed");
if (!cforge::execute_tool("cmake", build_args, "", "CMake Build", verbose, 600)) {
cforge::logger::print_error("Build failed");
std::filesystem::remove_all(temp_dir);
return 1;
}
@@ -181,12 +180,12 @@ cforge_int_t cforge_cmd_update(const cforge_context_t *ctx) {
}
if (built_exe.empty()) {
logger::print_error("Could not find built cforge executable");
cforge::logger::print_error("Could not find built cforge executable");
std::filesystem::remove_all(temp_dir);
return 1;
}
logger::print_verbose("Found built executable: " + built_exe.string());
cforge::logger::print_verbose("Found built executable: " + built_exe.string());
// Install the binary to the same location as `cforge install`
// This is: <platform_path>/installed/cforge/bin/
@@ -228,7 +227,7 @@ cforge_int_t cforge_cmd_update(const cforge_context_t *ctx) {
std::filesystem::copy_file(
built_exe, target_exe,
std::filesystem::copy_options::overwrite_existing);
logger::print_action("Installed", target_exe.string());
cforge::logger::print_action("Installed", target_exe.string());
install_success = true;
// Try to remove backup (ignore errors - Windows may have it locked)
@@ -240,13 +239,13 @@ cforge_int_t cforge_cmd_update(const cforge_context_t *ctx) {
}
}
} catch (const std::exception &e) {
logger::print_error("Failed to install binary: " + std::string(e.what()));
cforge::logger::print_error("Failed to install binary: " + std::string(e.what()));
}
// Update PATH if requested (only if install succeeded)
if (install_success && add_to_path) {
installer_instance.update_path_env(install_bin_dir);
logger::print_action("Updated", "PATH environment variable");
cforge::logger::print_action("Updated", "PATH environment variable");
}
// Clean up temporary directory (handle read-only git files on Windows)
@@ -268,20 +267,20 @@ cforge_int_t cforge_cmd_update(const cforge_context_t *ctx) {
// Ignore cleanup errors - temp files will be cleaned up by OS eventually
}
logger::finished("cforge updated successfully!");
logger::print_action("Location", target_exe.string());
cforge::logger::finished("cforge updated successfully!");
cforge::logger::print_action("Location", target_exe.string());
return 0;
}
// Update package registry index (update_packages == true)
logger::print_header("Updating package registry index");
cforge::logger::print_header("Updating package registry index");
registry reg;
cforge::registry reg;
if (reg.update(true)) { // force=true to always update
logger::finished("Package registry updated successfully");
cforge::logger::finished("Package registry updated successfully");
return 0;
} else {
logger::print_error("Failed to update package registry");
cforge::logger::print_error("Failed to update package registry");
return 1;
}
}
@@ -6,6 +6,7 @@
#include "cforge/log.hpp"
#include "core/commands.hpp"
#include "core/constants.h"
#include "core/types.h"
#include "core/file_system.h"
#include "core/process_utils.hpp"
#include "core/toml_reader.hpp"
@@ -16,8 +17,6 @@
#include <string>
#include <vector>
using namespace cforge;
// Default vcpkg directory location relative to user home
#ifdef _WIN32
const char *DEFAULT_VCPKG_DIR = "%USERPROFILE%\\vcpkg";
@@ -31,7 +30,7 @@ const char *DEFAULT_VCPKG_DIR = "~/vcpkg";
* @param project_config Optional project configuration
* @return std::filesystem::path Path to vcpkg
*/
static std::filesystem::path get_vcpkg_path(const toml_reader *project_config) {
static std::filesystem::path get_vcpkg_path(const cforge::toml_reader *project_config) {
std::filesystem::path vcpkg_path;
// First check project config
@@ -113,7 +112,7 @@ static std::filesystem::path get_vcpkg_path(const toml_reader *project_config) {
std::filesystem::path vcpkg_dir = project_dir / "vcpkg";
if (std::filesystem::exists(vcpkg_dir)) {
logger::print_action("Done", "vcpkg is already installed");
cforge::logger::print_action("Done", "vcpkg is already installed");
return true;
}
@@ -121,7 +120,7 @@ static std::filesystem::path get_vcpkg_path(const toml_reader *project_config) {
try {
std::filesystem::create_directory(vcpkg_dir);
} catch (const std::exception &e) {
logger::print_error("Failed to create vcpkg directory: " +
cforge::logger::print_error("Failed to create vcpkg directory: " +
std::string(e.what()));
return false;
}
@@ -131,20 +130,20 @@ static std::filesystem::path get_vcpkg_path(const toml_reader *project_config) {
std::vector<std::string> git_args = {
"clone", "https://github.com/microsoft/vcpkg.git", vcpkg_dir.string()};
logger::fetching("vcpkg repository");
cforge::logger::fetching("vcpkg repository");
auto result = execute_process(
auto result = cforge::execute_process(
git_cmd, git_args,
"", // working directory
[verbose](const std::string &line) {
if (verbose) {
logger::print_verbose(line);
cforge::logger::print_verbose(line);
}
},
[](const std::string &line) { logger::print_error(line); });
[](const std::string &line) { cforge::logger::print_error(line); });
if (!result.success) {
logger::print_error("Failed to clone vcpkg repository. Exit code: " +
cforge::logger::print_error("Failed to clone vcpkg repository. Exit code: " +
std::to_string(result.exit_code));
return false;
}
@@ -160,19 +159,19 @@ static std::filesystem::path get_vcpkg_path(const toml_reader *project_config) {
bootstrap_args = {"-disableMetrics"};
#endif
logger::installing("vcpkg");
cforge::logger::installing("vcpkg");
auto bootstrap_result = execute_process(
auto bootstrap_result = cforge::execute_process(
bootstrap_cmd, bootstrap_args, vcpkg_dir.string(),
[verbose](const std::string &line) {
if (verbose) {
logger::print_verbose(line);
cforge::logger::print_verbose(line);
}
},
[](const std::string &line) { logger::print_error(line); });
[](const std::string &line) { cforge::logger::print_error(line); });
if (!bootstrap_result.success) {
logger::print_error("Failed to bootstrap vcpkg. Exit code: " +
cforge::logger::print_error("Failed to bootstrap vcpkg. Exit code: " +
std::to_string(bootstrap_result.exit_code));
return false;
}
@@ -199,7 +198,7 @@ static std::filesystem::path get_vcpkg_path(const toml_reader *project_config) {
#endif
if (!std::filesystem::exists(vcpkg_exe)) {
logger::print_error("vcpkg not found at: " + vcpkg_exe.string());
cforge::logger::print_error("vcpkg not found at: " + vcpkg_exe.string());
return false;
}
@@ -207,22 +206,22 @@ static std::filesystem::path get_vcpkg_path(const toml_reader *project_config) {
std::string command = vcpkg_exe.string();
std::vector<std::string> args = {"integrate", "install"};
logger::print_action("Integrating", "vcpkg");
cforge::logger::print_action("Integrating", "vcpkg");
auto result = execute_process(
auto result = cforge::execute_process(
command, args,
"", // working directory
[verbose](const std::string &line) {
if (verbose) {
logger::print_verbose(line);
cforge::logger::print_verbose(line);
} else {
logger::print_status(line);
cforge::logger::print_status(line);
}
},
[](const std::string &line) { logger::print_error(line); });
[](const std::string &line) { cforge::logger::print_error(line); });
if (!result.success) {
logger::print_error("Failed to set up vcpkg integration. Exit code: " +
cforge::logger::print_error("Failed to set up vcpkg integration. Exit code: " +
std::to_string(result.exit_code));
return false;
}
@@ -245,7 +244,7 @@ static std::filesystem::path get_vcpkg_path(const toml_reader *project_config) {
file << "}\n";
file.close();
} else {
logger::print_warning("Failed to create vcpkg-configuration.json");
cforge::logger::print_warning("Failed to create vcpkg-configuration.json");
}
}
@@ -272,7 +271,7 @@ static std::filesystem::path get_vcpkg_path(const toml_reader *project_config) {
#endif
if (!std::filesystem::exists(vcpkg_exe)) {
logger::print_error("vcpkg not found at: " + vcpkg_exe.string());
cforge::logger::print_error("vcpkg not found at: " + vcpkg_exe.string());
return false;
}
@@ -282,7 +281,7 @@ static std::filesystem::path get_vcpkg_path(const toml_reader *project_config) {
// Forward all arguments to vcpkg
if (args.args) {
for (int i = 0; args.args[i]; ++i) {
for (cforge_int_t i = 0; args.args[i]; ++i) {
vcpkg_args.push_back(args.args[i]);
}
}
@@ -297,16 +296,16 @@ static std::filesystem::path get_vcpkg_path(const toml_reader *project_config) {
}
}
logger::print_action("Running", "vcpkg command: " + command_str);
cforge::logger::print_action("Running", "vcpkg command: " + command_str);
auto result = execute_process(
auto result = cforge::execute_process(
command, vcpkg_args,
"", // working directory
[](const std::string &line) { logger::print_status(line); },
[](const std::string &line) { logger::print_error(line); });
[](const std::string &line) { cforge::logger::print_status(line); },
[](const std::string &line) { cforge::logger::print_error(line); });
if (!result.success) {
logger::print_error("vcpkg command failed. Exit code: " +
cforge::logger::print_error("vcpkg command failed. Exit code: " +
std::to_string(result.exit_code));
return false;
}
@@ -323,17 +322,17 @@ static std::filesystem::path get_vcpkg_path(const toml_reader *project_config) {
cforge_int_t cforge_cmd_vcpkg(const cforge_context_t *ctx) {
// Parse arguments
std::vector<std::string> args;
for (int i = 0; i < ctx->args.arg_count; ++i) {
for (cforge_int_t i = 0; i < ctx->args.arg_count; ++i) {
args.push_back(ctx->args.args[i]);
}
if (args.empty()) {
logger::print_error("No command specified");
logger::print_status("Usage: cforge vcpkg <command>");
logger::print_status("Commands:");
logger::print_status(" setup - Set up vcpkg integration");
logger::print_status(" update - Update vcpkg and installed packages");
logger::print_status(" list - List installed packages");
cforge::logger::print_error("No command specified");
cforge::logger::print_status("Usage: cforge vcpkg <command>");
cforge::logger::print_status("Commands:");
cforge::logger::print_status(" setup - Set up vcpkg integration");
cforge::logger::print_status(" update - Update vcpkg and installed packages");
cforge::logger::print_status(" list - List installed packages");
return 1;
}
@@ -342,7 +341,7 @@ cforge_int_t cforge_cmd_vcpkg(const cforge_context_t *ctx) {
// Get vcpkg path
std::filesystem::path vcpkg_path = get_vcpkg_path(nullptr);
if (vcpkg_path.empty()) {
logger::print_error("Could not determine vcpkg path");
cforge::logger::print_error("Could not determine vcpkg path");
return 1;
}
@@ -355,41 +354,41 @@ cforge_int_t cforge_cmd_vcpkg(const cforge_context_t *ctx) {
#endif
if (std::filesystem::exists(vcpkg_exe)) {
logger::print_action("Done", "vcpkg is already installed");
cforge::logger::print_action("Done", "vcpkg is already installed");
} else {
// Clone vcpkg
logger::fetching("vcpkg");
cforge::logger::fetching("vcpkg");
std::string git_cmd = "git";
std::vector<std::string> git_args = {
"clone", "https://github.com/Microsoft/vcpkg.git",
vcpkg_path.string()};
auto result = execute_process(
auto result = cforge::execute_process(
git_cmd, git_args,
"", // working directory
[](const std::string &line) { logger::print_verbose(line); },
[](const std::string &line) { logger::print_error(line); });
[](const std::string &line) { cforge::logger::print_verbose(line); },
[](const std::string &line) { cforge::logger::print_error(line); });
if (!result.success) {
logger::print_error("Failed to clone vcpkg");
cforge::logger::print_error("Failed to clone vcpkg");
return 1;
}
// Bootstrap vcpkg
logger::installing("vcpkg");
cforge::logger::installing("vcpkg");
#ifdef _WIN32
std::string bootstrap_cmd = (vcpkg_path / "bootstrap-vcpkg.bat").string();
#else
std::string bootstrap_cmd = (vcpkg_path / "bootstrap-vcpkg.sh").string();
#endif
auto bootstrap_result = execute_process(
auto bootstrap_result = cforge::execute_process(
bootstrap_cmd, {}, vcpkg_path.string(),
[](const std::string &line) { logger::print_verbose(line); },
[](const std::string &line) { logger::print_error(line); });
[](const std::string &line) { cforge::logger::print_verbose(line); },
[](const std::string &line) { cforge::logger::print_error(line); });
if (!bootstrap_result.success) {
logger::print_error("Failed to bootstrap vcpkg");
cforge::logger::print_error("Failed to bootstrap vcpkg");
return 1;
}
}
@@ -403,65 +402,65 @@ cforge_int_t cforge_cmd_vcpkg(const cforge_context_t *ctx) {
"echo 'export VCPKG_ROOT=\"" + vcpkg_root + "\"' >> ~/.bashrc";
#endif
auto env_result = execute_process(
auto env_result = cforge::execute_process(
cmd, {},
"", // working directory
[](const std::string &line) { logger::print_verbose(line); },
[](const std::string &line) { logger::print_error(line); });
[](const std::string &line) { cforge::logger::print_verbose(line); },
[](const std::string &line) { cforge::logger::print_error(line); });
if (!env_result.success) {
logger::print_warning("Failed to set VCPKG_ROOT environment variable");
logger::print_status("Please set VCPKG_ROOT to: " + vcpkg_root);
cforge::logger::print_warning("Failed to set VCPKG_ROOT environment variable");
cforge::logger::print_status("Please set VCPKG_ROOT to: " + vcpkg_root);
}
logger::finished("vcpkg has been successfully installed");
cforge::logger::finished("vcpkg has been successfully installed");
return 0;
} else if (command == "update") {
// Get vcpkg path
std::filesystem::path vcpkg_path = get_vcpkg_path(nullptr);
if (vcpkg_path.empty()) {
logger::print_error("Could not determine vcpkg path");
cforge::logger::print_error("Could not determine vcpkg path");
return 1;
}
// Update vcpkg
logger::updating("vcpkg");
cforge::logger::updating("vcpkg");
std::string git_cmd = "git";
std::vector<std::string> git_args = {"pull"};
auto result = execute_process(
auto result = cforge::execute_process(
git_cmd, git_args, vcpkg_path.string(),
[](const std::string &line) { logger::print_verbose(line); },
[](const std::string &line) { logger::print_error(line); });
[](const std::string &line) { cforge::logger::print_verbose(line); },
[](const std::string &line) { cforge::logger::print_error(line); });
if (!result.success) {
logger::print_error("Failed to update vcpkg");
cforge::logger::print_error("Failed to update vcpkg");
return 1;
}
// Update packages
logger::updating("packages");
cforge::logger::updating("packages");
std::string vcpkg_cmd = (vcpkg_path / "vcpkg").string();
std::vector<std::string> vcpkg_args = {"upgrade", "--no-dry-run"};
auto update_result = execute_process(
auto update_result = cforge::execute_process(
vcpkg_cmd, vcpkg_args,
"", // working directory
[](const std::string &line) { logger::print_verbose(line); },
[](const std::string &line) { logger::print_error(line); });
[](const std::string &line) { cforge::logger::print_verbose(line); },
[](const std::string &line) { cforge::logger::print_error(line); });
if (!update_result.success) {
logger::print_error("Failed to update packages");
cforge::logger::print_error("Failed to update packages");
return 1;
}
logger::finished("vcpkg and packages have been updated");
cforge::logger::finished("vcpkg and packages have been updated");
return 0;
} else if (command == "list") {
// Get vcpkg path
std::filesystem::path vcpkg_path = get_vcpkg_path(nullptr);
if (vcpkg_path.empty()) {
logger::print_error("Could not determine vcpkg path");
cforge::logger::print_error("Could not determine vcpkg path");
return 1;
}
@@ -469,20 +468,20 @@ cforge_int_t cforge_cmd_vcpkg(const cforge_context_t *ctx) {
std::string vcpkg_cmd = (vcpkg_path / "vcpkg").string();
std::vector<std::string> vcpkg_args = {"list"};
auto result = execute_process(
auto result = cforge::execute_process(
vcpkg_cmd, vcpkg_args,
"", // working directory
[](const std::string &line) { logger::print_status(line); },
[](const std::string &line) { logger::print_error(line); });
[](const std::string &line) { cforge::logger::print_status(line); },
[](const std::string &line) { cforge::logger::print_error(line); });
if (!result.success) {
logger::print_error("Failed to list packages");
cforge::logger::print_error("Failed to list packages");
return 1;
}
return 0;
} else {
logger::print_error("Unknown command: " + command);
cforge::logger::print_error("Unknown command: " + command);
return 1;
}
}
@@ -10,8 +10,6 @@
#include <iostream>
#include <string>
using namespace cforge;
/**
* @brief Display cforge version information
*
@@ -19,10 +17,10 @@ using namespace cforge;
* @return cforge_int_t Exit code (0 for success)
*/
cforge_int_t cforge_cmd_version([[maybe_unused]] const cforge_context_t *ctx) {
logger::print_action("Version",
cforge::logger::print_action("Version",
"cforge version " + std::string(CFORGE_VERSION));
logger::print_action("Info", "C++ Project Management Tool");
logger::print_action("Info", "Copyright (c) 2023-2024");
cforge::logger::print_action("Info", "C++ Project Management Tool");
cforge::logger::print_action("Info", "Copyright (c) 2023-2024");
return 0;
}
@@ -10,6 +10,7 @@
#include "cforge/log.hpp"
#include "core/commands.hpp"
#include "core/types.h"
#include "core/process_utils.hpp"
#include "core/toml_reader.hpp"
@@ -125,7 +126,7 @@ public:
return files;
}
size_t file_count() const { return m_files.size(); }
cforge_size_t file_count() const { return m_files.size(); }
private:
void scan_files() { scan_directory(m_root); }
@@ -211,8 +212,6 @@ private:
*/
bool run_build(const fs::path &project_dir, const std::string &config,
bool verbose) {
using namespace cforge;
std::vector<std::string> args = {"--build", "build"};
if (!config.empty()) {
args.push_back("--config");
@@ -221,7 +220,7 @@ bool run_build(const fs::path &project_dir, const std::string &config,
auto start = std::chrono::steady_clock::now();
auto result = execute_process(
auto result = cforge::execute_process(
"cmake", args, project_dir.string(),
[verbose](const std::string &line) {
if (verbose || line.find("error") != std::string::npos ||
@@ -242,11 +241,11 @@ bool run_build(const fs::path &project_dir, const std::string &config,
std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
if (result.exit_code == 0) {
logger::finished("build",
cforge::logger::finished("build",
fmt::format("{:.2f}s", duration.count() / 1000.0));
return true;
} else {
logger::print_error("Build failed");
cforge::logger::print_error("Build failed");
return false;
}
}
@@ -257,17 +256,15 @@ bool run_build(const fs::path &project_dir, const std::string &config,
* @brief Handle the 'watch' command for auto-rebuild
*/
cforge_int_t cforge_cmd_watch(const cforge_context_t *ctx) {
using namespace cforge;
fs::path project_dir = ctx->working_dir;
// Parse arguments
std::string config = "";
bool verbose = false;
bool run_after_build = false;
int poll_interval_ms = 500; // Default: check every 500ms
cforge_int_t poll_interval_ms = 500; // Default: check every 500ms
for (int i = 0; i < ctx->args.arg_count; i++) {
for (cforge_int_t i = 0; i < ctx->args.arg_count; i++) {
std::string arg = ctx->args.args[i];
if ((arg == "-c" || arg == "--config") && i + 1 < ctx->args.arg_count) {
config = ctx->args.args[++i];
@@ -287,7 +284,7 @@ cforge_int_t cforge_cmd_watch(const cforge_context_t *ctx) {
// Check for cforge.toml
fs::path config_file = project_dir / "cforge.toml";
if (!fs::exists(config_file)) {
logger::print_error("No cforge.toml found in current directory");
cforge::logger::print_error("No cforge.toml found in current directory");
return 1;
}
@@ -307,14 +304,14 @@ cforge_int_t cforge_cmd_watch(const cforge_context_t *ctx) {
FileWatcher watcher(project_dir, extensions);
logger::print_header("Watching for changes...");
logger::print_status("Tracking " + std::to_string(watcher.file_count()) +
cforge::logger::print_header("Watching for changes...");
cforge::logger::print_status("Tracking " + std::to_string(watcher.file_count()) +
" files");
logger::print_status("Press Ctrl+C to stop");
cforge::logger::print_status("Press Ctrl+C to stop");
fmt::print("\n");
// Do an initial build
logger::building(project_dir.filename().string());
cforge::logger::building(project_dir.filename().string());
bool last_build_succeeded = run_build(project_dir, config, verbose);
fmt::print("\n");
@@ -332,28 +329,28 @@ cforge_int_t cforge_cmd_watch(const cforge_context_t *ctx) {
// Report changes
fmt::print("\n");
logger::print_status("Changes detected:");
cforge::logger::print_status("Changes detected:");
for (const auto &file : changed) {
logger::print_action("Modified", file.filename().string());
cforge::logger::print_action("Modified", file.filename().string());
}
for (const auto &file : added) {
logger::print_action("Added", file.filename().string());
cforge::logger::print_action("Added", file.filename().string());
}
for (const auto &file : deleted) {
logger::print_action("Removed", file.filename().string());
cforge::logger::print_action("Removed", file.filename().string());
}
fmt::print("\n");
// Rebuild
logger::building(project_dir.filename().string());
cforge::logger::building(project_dir.filename().string());
last_build_succeeded = run_build(project_dir, config, verbose);
// Run if requested and build succeeded
if (run_after_build && last_build_succeeded) {
// Find and run the executable
toml_reader reader;
cforge::toml_reader reader;
reader.load(config_file.string());
std::string project_name = reader.get_string("project.name");
@@ -378,10 +375,10 @@ cforge_int_t cforge_cmd_watch(const cforge_context_t *ctx) {
if (fs::exists(exe_path)) {
fmt::print("\n");
logger::running(exe_path.filename().string());
cforge::logger::running(exe_path.filename().string());
fmt::print("{}\n", std::string(40, '-'));
auto run_result = execute_process(
auto run_result = cforge::execute_process(
exe_path.string(), {}, project_dir.string(),
[](const std::string &line) { fmt::print("{}\n", line); },
[](const std::string &line) {
@@ -390,7 +387,7 @@ cforge_int_t cforge_cmd_watch(const cforge_context_t *ctx) {
fmt::print("{}\n", std::string(40, '-'));
if (run_result.exit_code != 0) {
logger::print_warning("Process exited with code " +
cforge::logger::print_warning("Process exited with code " +
std::to_string(run_result.exit_code));
}
}
@@ -398,12 +395,12 @@ cforge_int_t cforge_cmd_watch(const cforge_context_t *ctx) {
}
fmt::print("\n");
logger::print_status("Watching for changes... (Ctrl+C to stop)");
cforge::logger::print_status("Watching for changes... (Ctrl+C to stop)");
}
}
fmt::print("\n");
logger::print_status("Watch mode stopped");
cforge::logger::print_status("Watch mode stopped");
return 0;
}
+1
View File
@@ -5,6 +5,7 @@
#include "core/config_resolver.hpp"
#include "cforge/log.hpp"
#include "core/types.h"
#include <algorithm>
#include <cctype>
+3 -3
View File
@@ -259,7 +259,7 @@ static cforge_fs_error_t remove_dir_recursive(cforge_cstring_t dir_path) {
#ifdef _WIN32
WIN32_FIND_DATAA find_data;
HANDLE find_handle;
char path[MAX_PATH];
cforge_char_t path[MAX_PATH];
// Create a search pattern
snprintf(path, MAX_PATH, "%s\\*", dir_path);
@@ -310,7 +310,7 @@ static cforge_fs_error_t remove_dir_recursive(cforge_cstring_t dir_path) {
}
struct dirent *entry;
char path[PATH_MAX];
cforge_char_t path[PATH_MAX];
while ((entry = readdir(dir)) != NULL) {
// Skip "." and ".."
@@ -399,7 +399,7 @@ cforge_fs_error_t cforge_read_file(const cforge_path_t *path,
// Get file size
fseek(file, 0, SEEK_END);
long file_size = ftell(file);
cforge_long_t file_size = ftell(file);
fseek(file, 0, SEEK_SET);
if (file_size < 0) {
+18 -27
View File
@@ -7,6 +7,7 @@
#include "cforge/log.hpp"
#include "core/process_utils.hpp"
#include "core/toml_reader.hpp"
#include "core/types.h"
#include <algorithm>
#include <chrono>
#include <cstdlib>
@@ -14,19 +15,12 @@
#include <regex>
#include <sstream>
#ifdef _WIN32
#ifndef NOMINMAX
#define NOMINMAX
#endif
#endif
namespace cforge {
// Index repository URL
// Cache validity duration (24 hours)
static const int CACHE_VALIDITY_HOURS = 24;
static const cforge_int_t CACHE_VALIDITY_HOURS = 24;
std::string registry::get_index_url() { return INDEX_REPO_URL; }
@@ -144,7 +138,7 @@ bool registry::update(bool force) {
}
std::vector<std::string> registry::search(const std::string &query,
size_t limit) const {
cforge_size_t limit) const {
std::vector<std::string> results;
std::filesystem::path packages_dir = index_dir_ / "packages";
@@ -225,7 +219,7 @@ std::vector<std::string> registry::search(const std::string &query,
[](const auto &a, const auto &b) { return a.second > b.second; });
// Extract names up to limit
for (size_t i = 0; i < std::min(limit, scored_results.size()); ++i) {
for (cforge_size_t i = 0; i < std::min(limit, scored_results.size()); ++i) {
results.push_back(scored_results[i].first);
}
@@ -239,8 +233,7 @@ std::optional<package_info> registry::get_package(const std::string &name) const
std::string registry::pattern_to_regex(const std::string &pattern) {
// Convert pattern like "v{version}" to regex like "^v([0-9]+\\.[0-9]+\\.?[0-9]*)$"
std::string regex_str = "^";
size_t pos = 0;
size_t version_pos = pattern.find("{version}");
cforge_size_t version_pos = pattern.find("{version}");
if (version_pos == std::string::npos) {
// No {version} placeholder, treat whole pattern as version
@@ -248,7 +241,7 @@ std::string registry::pattern_to_regex(const std::string &pattern) {
}
// Escape everything before {version}
for (size_t i = 0; i < version_pos; ++i) {
for (cforge_size_t i = 0; i < version_pos; ++i) {
char c = pattern[i];
if (c == '.' || c == '*' || c == '+' || c == '?' || c == '^' ||
c == '$' || c == '[' || c == ']' || c == '(' || c == ')' ||
@@ -262,7 +255,7 @@ std::string registry::pattern_to_regex(const std::string &pattern) {
regex_str += "([0-9]+(?:\\.[0-9]+)*)";
// Escape everything after {version}
for (size_t i = version_pos + 9; i < pattern.size(); ++i) {
for (cforge_size_t i = version_pos + 9; i < pattern.size(); ++i) {
char c = pattern[i];
if (c == '.' || c == '*' || c == '+' || c == '?' || c == '^' ||
c == '$' || c == '[' || c == ']' || c == '(' || c == ')' ||
@@ -279,7 +272,7 @@ std::string registry::pattern_to_regex(const std::string &pattern) {
std::string registry::version_to_tag(const std::string &version,
const std::string &pattern) {
std::string tag = pattern;
size_t pos = tag.find("{version}");
cforge_size_t pos = tag.find("{version}");
if (pos != std::string::npos) {
tag.replace(pos, 9, version);
}
@@ -326,7 +319,7 @@ registry::fetch_git_tags(const std::string &repo_url,
while (std::getline(iss, line)) {
// Find the tag name after refs/tags/
size_t tag_pos = line.find("refs/tags/");
cforge_size_t tag_pos = line.find("refs/tags/");
if (tag_pos == std::string::npos) {
continue;
}
@@ -365,7 +358,7 @@ registry::fetch_git_tags(const std::string &repo_url,
});
// Limit number of versions
if (versions.size() > static_cast<size_t>(config.max_versions)) {
if (versions.size() > static_cast<cforge_size_t>(config.max_versions)) {
versions.resize(config.max_versions);
}
@@ -400,7 +393,7 @@ registry::load_version_cache(const std::string &name) const {
std::string line;
while (std::getline(file, line)) {
size_t tab_pos = line.find('\t');
cforge_size_t tab_pos = line.find('\t');
if (tab_pos != std::string::npos) {
package_version ver;
ver.version = line.substr(0, tab_pos);
@@ -539,18 +532,16 @@ registry::load_package_file(const std::string &name) const {
std::string line;
package_version current_ver;
bool in_version = false;
bool has_explicit_versions = false;
while (std::getline(file, line)) {
// Trim whitespace
size_t start = line.find_first_not_of(" \t");
cforge_size_t start = line.find_first_not_of(" \t");
if (start == std::string::npos) {
continue;
}
line = line.substr(start);
if (line.find("[[versions]]") == 0) {
has_explicit_versions = true;
if (in_version && !current_ver.version.empty()) {
info.versions.push_back(current_ver);
}
@@ -565,7 +556,7 @@ registry::load_package_file(const std::string &name) const {
in_version = false;
} else {
// Parse version fields
size_t eq_pos = line.find('=');
cforge_size_t eq_pos = line.find('=');
if (eq_pos != std::string::npos) {
std::string key = line.substr(0, eq_pos);
std::string value = line.substr(eq_pos + 1);
@@ -661,7 +652,7 @@ bool registry::version_matches(const std::string &version,
}
// Check for wildcard
size_t wildcard_pos = spec.find('*');
cforge_size_t wildcard_pos = spec.find('*');
if (wildcard_pos != std::string::npos) {
// Get the prefix before the wildcard
std::string prefix = spec.substr(0, wildcard_pos);
@@ -685,7 +676,7 @@ bool registry::version_matches(const std::string &version,
}
// Compare parts
for (size_t i = 0; i < spec_parts.size(); ++i) {
for (cforge_size_t i = 0; i < spec_parts.size(); ++i) {
if (ver_parts[i] != spec_parts[i]) {
return false;
}
@@ -702,11 +693,11 @@ int registry::compare_versions(const std::string &v1, const std::string &v2) {
auto parts1 = parse_version(v1);
auto parts2 = parse_version(v2);
size_t max_parts = std::max(parts1.size(), parts2.size());
cforge_size_t max_parts = std::max(parts1.size(), parts2.size());
parts1.resize(max_parts, 0);
parts2.resize(max_parts, 0);
for (size_t i = 0; i < max_parts; ++i) {
for (cforge_size_t i = 0; i < max_parts; ++i) {
if (parts1[i] < parts2[i]) {
return -1;
}
@@ -726,7 +717,7 @@ std::vector<int> registry::parse_version(const std::string &version) {
while (std::getline(ss, part, '.')) {
try {
// Handle versions like "3.11.3-rc1" by stripping suffix
size_t dash = part.find('-');
cforge_size_t dash = part.find('-');
if (dash != std::string::npos) {
part = part.substr(0, dash);
}
+1
View File
@@ -5,6 +5,7 @@
#include "core/toml_reader.hpp"
#include "cforge/log.hpp"
#include "core/types.h"
#include <filesystem>
#include <fstream>
@@ -4,6 +4,7 @@
*/
#include "core/build_progress.hpp"
#include "core/types.h"
#include <algorithm>
#include <fmt/core.h>
#include <iostream>
@@ -43,8 +44,8 @@ bool build_progress::parse_ninja_progress(const std::string &line) {
if (std::regex_search(line, match, ninja_regex)) {
std::lock_guard<std::mutex> lock(mutex_);
int new_step = std::stoi(match[1].str());
int new_total = std::stoi(match[2].str());
cforge_int_t new_step = std::stoi(match[1].str());
cforge_int_t new_total = std::stoi(match[2].str());
// If we moved to a new step, finish the previous file
if (has_progress_ && new_step > current_step_ && !current_file_.empty()) {
@@ -81,11 +82,11 @@ bool build_progress::parse_make_progress(const std::string &line) {
if (std::regex_search(line, match, make_regex)) {
std::lock_guard<std::mutex> lock(mutex_);
int percentage = std::stoi(match[1].str());
cforge_int_t percentage = std::stoi(match[1].str());
// Estimate step from percentage
int new_step = percentage;
int new_total = 100;
cforge_int_t new_step = percentage;
cforge_int_t new_total = 100;
// Finish previous file if step changed
if (has_progress_ && new_step > current_step_ && !current_file_.empty()) {
@@ -166,7 +167,7 @@ std::string build_progress::extract_filename(const std::string &line) {
if (std::regex_search(line, match, link_regex)) {
std::string path = match[1].str();
// Extract just the filename
size_t last_slash = path.find_last_of("/\\");
cforge_size_t last_slash = path.find_last_of("/\\");
if (last_slash != std::string::npos) {
return "[link] " + path.substr(last_slash + 1);
}
@@ -190,19 +191,19 @@ std::string build_progress::get_current_file() const {
return current_file_;
}
double build_progress::get_progress() const {
cforge_double_t build_progress::get_progress() const {
std::lock_guard<std::mutex> lock(mutex_);
if (total_steps_ == 0)
return 0.0;
return static_cast<double>(current_step_) / static_cast<double>(total_steps_);
return static_cast<cforge_double_t>(current_step_) / static_cast<cforge_double_t>(total_steps_);
}
int build_progress::get_current_step() const {
cforge_int_t build_progress::get_current_step() const {
std::lock_guard<std::mutex> lock(mutex_);
return current_step_;
}
int build_progress::get_total_steps() const {
cforge_int_t build_progress::get_total_steps() const {
std::lock_guard<std::mutex> lock(mutex_);
return total_steps_;
}
@@ -216,7 +217,7 @@ const std::vector<file_timing> &build_progress::get_timings() const {
return timings_;
}
std::vector<file_timing> build_progress::get_slowest_files(size_t count) const {
std::vector<file_timing> build_progress::get_slowest_files(cforge_size_t count) const {
std::lock_guard<std::mutex> lock(mutex_);
std::vector<file_timing> sorted = timings_;
@@ -250,18 +251,18 @@ void build_progress::file_finished(const std::string &filename) {
timings_.push_back(timing);
}
void display_progress_bar(int current, int total, int width,
void display_progress_bar(cforge_int_t current, cforge_int_t total, cforge_int_t width,
bool show_percentage) {
if (total <= 0)
return;
double progress = static_cast<double>(current) / static_cast<double>(total);
int filled = static_cast<int>(progress * width);
cforge_double_t progress = static_cast<cforge_double_t>(current) / static_cast<cforge_double_t>(total);
cforge_int_t filled = static_cast<cforge_int_t>(progress * width);
std::string bar;
bar.reserve(width);
for (int i = 0; i < width; ++i) {
for (cforge_int_t i = 0; i < width; ++i) {
if (i < filled) {
bar += "\xe2\x96\x88"; // Unicode full block (UTF-8)
} else {
@@ -272,7 +273,7 @@ void display_progress_bar(int current, int total, int width,
std::string line = fmt::format(" [{}]", bar);
if (show_percentage) {
int percent = static_cast<int>(progress * 100);
cforge_int_t percent = static_cast<cforge_int_t>(progress * 100);
line += fmt::format(" {:3d}% ({}/{})", percent, current, total);
}
@@ -4,6 +4,7 @@
*/
#include "core/dependency_hash.hpp"
#include "core/types.h"
#include <algorithm>
#include <filesystem>
#include <fstream>
@@ -53,7 +54,7 @@ bool dependency_hash::load(const std::filesystem::path &project_dir) {
}
// Parse key = "value" pairs
size_t eq_pos = line.find('=');
cforge_size_t eq_pos = line.find('=');
if (eq_pos != std::string::npos) {
std::string key = trim(line.substr(0, eq_pos));
std::string value = trim(line.substr(eq_pos + 1));
@@ -167,11 +168,11 @@ uint64_t dependency_hash::fnv1a_hash(const std::string &str) {
return fnv1a_hash(str.data(), str.size());
}
uint64_t dependency_hash::fnv1a_hash(const void *data, size_t size) {
uint64_t dependency_hash::fnv1a_hash(const void *data, cforge_size_t size) {
uint64_t hash = FNV_OFFSET_BASIS;
const uint8_t *bytes = static_cast<const uint8_t *>(data);
for (size_t i = 0; i < size; ++i) {
for (cforge_size_t i = 0; i < size; ++i) {
hash ^= bytes[i];
hash *= FNV_PRIME;
}
@@ -4,6 +4,7 @@
*/
#include "core/error_format.hpp"
#include "core/types.h"
#include <algorithm>
#include <fstream>
#include <iomanip>
@@ -126,7 +127,7 @@ std::string format_build_errors(const std::string &error_output) {
}
// Helper function to read a specific line from a file
static std::string read_line_from_file(const std::string &file_path, int line_number) {
static std::string read_line_from_file(const std::string &file_path, cforge_int_t line_number) {
if (file_path.empty() || line_number <= 0) {
return "";
}
@@ -138,7 +139,7 @@ static std::string read_line_from_file(const std::string &file_path, int line_nu
}
std::string line;
int current_line = 0;
cforge_int_t current_line = 0;
while (std::getline(file, line) && current_line < line_number) {
current_line++;
if (current_line == line_number) {
@@ -221,7 +222,7 @@ std::string format_diagnostic_to_string(const diagnostic &diag) {
// Code snippet with line numbers
if (!line_content.empty() && diag.line_number > 0) {
// Calculate gutter width based on line number
int gutter_width = std::to_string(diag.line_number).length();
cforge_int_t gutter_width = std::to_string(diag.line_number).length();
if (gutter_width < 2)
gutter_width = 2;
@@ -237,15 +238,15 @@ std::string format_diagnostic_to_string(const diagnostic &diag) {
ss << fmt::format(fg(fmt::color::cyan), "{:>{}} | ", "", gutter_width);
if (diag.column_number > 0) {
size_t col = static_cast<size_t>(diag.column_number - 1);
size_t token_length = 1;
cforge_size_t col = static_cast<cforge_size_t>(diag.column_number - 1);
cforge_size_t token_length = 1;
// Try to find the token length
if (col < line_content.length()) {
if (std::isalnum(line_content[col]) ||
line_content[col] == '_') {
size_t start = col;
size_t end = col;
cforge_size_t start = col;
cforge_size_t end = col;
// Find token boundaries
while (end < line_content.length() &&
@@ -285,7 +286,7 @@ std::string format_diagnostic_to_string(const diagnostic &diag) {
// Fix suggestions
if (!diag.fixes.empty()) {
for (size_t i = 0; i < diag.fixes.size() && i < 3;
for (cforge_size_t i = 0; i < diag.fixes.size() && i < 3;
++i) { // Limit to 3 suggestions
const auto &fix = diag.fixes[i];
ss << fmt::format(fg(fmt::color::magenta) | fmt::emphasis::bold,
@@ -713,7 +714,7 @@ parse_gcc_clang_errors(const std::string &error_output) {
if (file_contents.find(diag.file_path) != file_contents.end()) {
std::istringstream file_stream(file_contents[diag.file_path]);
std::string file_line;
int current_line = 0;
cforge_int_t current_line = 0;
while (std::getline(file_stream, file_line) &&
current_line < diag.line_number) {
@@ -802,7 +803,7 @@ std::vector<diagnostic> parse_msvc_errors(const std::string &error_output) {
if (file_contents.find(diag.file_path) != file_contents.end()) {
std::istringstream file_stream(file_contents[diag.file_path]);
std::string file_line;
int current_line = 0;
cforge_int_t current_line = 0;
// Read until we reach the target line
while (std::getline(file_stream, file_line) &&
@@ -1157,7 +1158,7 @@ std::vector<diagnostic> parse_linker_errors(const std::string &error_output) {
// Parse the error code number if it's an LNK error
if (error_code.find("LNK") == 0) {
try {
int code_num = std::stoi(error_code.substr(3));
cforge_int_t code_num = std::stoi(error_code.substr(3));
switch (code_num) {
case 1104:
diag.help_text = "The file name specified could not be found. Check "
@@ -1493,11 +1494,11 @@ std::vector<diagnostic> parse_linker_errors(const std::string &error_output) {
// Reference context lines (add to current diagnostic)
if (std::regex_search(line, matches, reference_regex)) {
if (current_diag != nullptr) {
std::string file_ref = matches[1].str();
int ref_line = matches[2].matched ? std::stoi(matches[2].str()) : 0;
cforge_int_t ref_line = matches[2].matched ? std::stoi(matches[2].str()) : 0;
if (current_diag->file_path.empty()) {
current_diag->file_path = file_ref;
@@ -1672,8 +1673,8 @@ std::vector<diagnostic> parse_template_errors(const std::string &error_output) {
std::string line;
std::istringstream stream(error_output);
diagnostic *current_template_error = nullptr;
int instantiation_depth = 0;
const int MAX_INSTANTIATION_DEPTH = 3; // Only show top 3 levels
cforge_int_t instantiation_depth = 0;
const cforge_int_t MAX_INSTANTIATION_DEPTH = 3; // Only show top 3 levels
while (std::getline(stream, line)) {
std::smatch matches;
@@ -1689,7 +1690,7 @@ std::vector<diagnostic> parse_template_errors(const std::string &error_output) {
diag.message = simplify_template_type(matches[5].str());
// Add help based on error code
int code_num = std::stoi(diag.code.substr(1));
cforge_int_t code_num = std::stoi(diag.code.substr(1));
switch (code_num) {
case 2782:
diag.help_text = "Template argument deduction failed. Try specifying "
@@ -1783,7 +1784,7 @@ std::vector<diagnostic> parse_template_errors(const std::string &error_output) {
std::string line_num = matches[2].str();
// Extract just the filename for brevity
size_t last_slash = file.find_last_of("/\\");
cforge_size_t last_slash = file.find_last_of("/\\");
if (last_slash != std::string::npos) {
file = file.substr(last_slash + 1);
}
@@ -1867,7 +1868,7 @@ deduplicate_diagnostics(std::vector<diagnostic> diagnostics) {
std::string base_code = d.code;
if (base_code.find("-") != std::string::npos) {
// Extract the suffix after the last hyphen as the error type
size_t pos = base_code.rfind("-");
cforge_size_t pos = base_code.rfind("-");
base_code = base_code.substr(pos + 1);
}
return d.file_path + ":" + std::to_string(d.line_number) + "::" + base_code;
@@ -1893,7 +1894,7 @@ deduplicate_diagnostics(std::vector<diagnostic> diagnostics) {
if (new_has_better_info) {
// Replace with the more detailed diagnostic but keep occurrence count
int count = existing.occurrence_count + 1;
cforge_int_t count = existing.occurrence_count + 1;
auto old_notes = std::move(existing.notes);
existing = std::move(diag);
existing.occurrence_count = count;
@@ -1937,10 +1938,10 @@ deduplicate_diagnostics(std::vector<diagnostic> diagnostics) {
error_summary
calculate_error_summary(const std::vector<diagnostic> &diagnostics) {
error_summary summary;
std::map<std::string, int> category_counts;
std::map<std::string, cforge_int_t> category_counts;
for (const auto &diag : diagnostics) {
int count = diag.occurrence_count;
cforge_int_t count = diag.occurrence_count;
switch (diag.level) {
case diagnostic_level::ERROR:
@@ -2209,7 +2210,7 @@ std::string suggest_library_for_symbol(const std::string &symbol) {
if (clean_symbol.find("__imp_") == 0) {
clean_symbol = clean_symbol.substr(6);
}
size_t at_pos = clean_symbol.find('@');
cforge_size_t at_pos = clean_symbol.find('@');
if (at_pos != std::string::npos) {
clean_symbol = clean_symbol.substr(0, at_pos);
}
@@ -2447,7 +2448,7 @@ std::string suggest_include_for_type(const std::string &type_name) {
}
// Try extracting template base (e.g., "vector<int>" -> "vector")
size_t template_start = type_name.find('<');
cforge_size_t template_start = type_name.find('<');
if (template_start != std::string::npos) {
std::string base_type = type_name.substr(0, template_start);
it = type_to_header.find(base_type);
@@ -2460,25 +2461,25 @@ std::string suggest_include_for_type(const std::string &type_name) {
}
// Simple Levenshtein distance for typo detection
static int levenshtein_distance(const std::string &s1, const std::string &s2) {
const size_t m = s1.size();
const size_t n = s2.size();
static cforge_int_t levenshtein_distance(const std::string &s1, const std::string &s2) {
const cforge_size_t m = s1.size();
const cforge_size_t n = s2.size();
if (m == 0)
return static_cast<int>(n);
return static_cast<cforge_int_t>(n);
if (n == 0)
return static_cast<int>(m);
return static_cast<cforge_int_t>(m);
std::vector<std::vector<int>> dp(m + 1, std::vector<int>(n + 1));
std::vector<std::vector<cforge_int_t>> dp(m + 1, std::vector<cforge_int_t>(n + 1));
for (size_t i = 0; i <= m; ++i)
dp[i][0] = static_cast<int>(i);
for (size_t j = 0; j <= n; ++j)
dp[0][j] = static_cast<int>(j);
for (cforge_size_t i = 0; i <= m; ++i)
dp[i][0] = static_cast<cforge_int_t>(i);
for (cforge_size_t j = 0; j <= n; ++j)
dp[0][j] = static_cast<cforge_int_t>(j);
for (size_t i = 1; i <= m; ++i) {
for (size_t j = 1; j <= n; ++j) {
int cost = (s1[i - 1] == s2[j - 1]) ? 0 : 1;
for (cforge_size_t i = 1; i <= m; ++i) {
for (cforge_size_t j = 1; j <= n; ++j) {
cforge_int_t cost = (s1[i - 1] == s2[j - 1]) ? 0 : 1;
dp[i][j] = std::min({
dp[i - 1][j] + 1, // deletion
dp[i][j - 1] + 1, // insertion
@@ -2493,18 +2494,18 @@ static int levenshtein_distance(const std::string &s1, const std::string &s2) {
std::vector<std::string>
find_similar_identifiers(const std::string &unknown_identifier,
const std::vector<std::string> &available_identifiers,
int max_distance) {
cforge_int_t max_distance) {
std::vector<std::pair<std::string, int>> matches;
std::vector<std::pair<std::string, cforge_int_t>> matches;
for (const auto &candidate : available_identifiers) {
// Skip if length difference is too large
int len_diff = std::abs(static_cast<int>(unknown_identifier.length()) -
static_cast<int>(candidate.length()));
cforge_int_t len_diff = std::abs(static_cast<cforge_int_t>(unknown_identifier.length()) -
static_cast<cforge_int_t>(candidate.length()));
if (len_diff > max_distance)
continue;
int distance = levenshtein_distance(unknown_identifier, candidate);
cforge_int_t distance = levenshtein_distance(unknown_identifier, candidate);
if (distance <= max_distance && distance > 0) {
matches.push_back({candidate, distance});
}
@@ -2549,16 +2550,16 @@ std::vector<fix_suggestion> generate_fix_suggestions(const diagnostic &diag) {
if (!diag.line_content.empty()) {
// Find the end of the statement (before any comment)
std::string line = diag.line_content;
size_t comment_pos = line.find("//");
cforge_size_t comment_pos = line.find("//");
if (comment_pos != std::string::npos) {
line = line.substr(0, comment_pos);
}
// Trim trailing whitespace
size_t end = line.find_last_not_of(" \t");
cforge_size_t end = line.find_last_not_of(" \t");
if (end != std::string::npos) {
fix.start_column =
static_cast<int>(end + 2); // After last non-space char
static_cast<cforge_int_t>(end + 2); // After last non-space char
}
}
@@ -3597,7 +3598,7 @@ std::vector<diagnostic> parse_test_framework_errors(const std::string &error_out
// Collect next few lines for context
std::string next_line;
for (int i = 0; i < 6 && std::getline(stream, next_line); ++i) {
for (cforge_int_t i = 0; i < 6 && std::getline(stream, next_line); ++i) {
if (!next_line.empty() && next_line[0] != '[') {
diag.notes.push_back(next_line);
}
@@ -3634,7 +3635,7 @@ std::vector<diagnostic> parse_test_framework_errors(const std::string &error_out
// Collect context
std::string next_line;
for (int i = 0; i < 5 && std::getline(stream, next_line); ++i) {
for (cforge_int_t i = 0; i < 5 && std::getline(stream, next_line); ++i) {
if (!next_line.empty()) {
diag.notes.push_back(next_line);
}
@@ -4,6 +4,7 @@
*/
#include "core/include_analyzer.hpp"
#include "core/types.h"
#include <algorithm>
#include <fstream>
#include <regex>
@@ -250,7 +251,7 @@ include_analysis_result include_analyzer::analyze(bool include_deps) {
// Build the include graph
result.include_graph = build_include_graph(include_deps);
result.total_files_analyzed = static_cast<int>(result.include_graph.size());
result.total_files_analyzed = static_cast<cforge_int_t>(result.include_graph.size());
// Find all cycles
result.chains = find_cycles(result.include_graph);
@@ -267,18 +268,18 @@ std::string format_circular_chains(const std::vector<circular_chain> &chains) {
std::ostringstream ss;
ss << "Circular Dependencies Found:\n\n";
for (size_t i = 0; i < chains.size(); ++i) {
for (cforge_size_t i = 0; i < chains.size(); ++i) {
const auto &chain = chains[i];
// Print the chain as a tree
for (size_t j = 0; j < chain.files.size(); ++j) {
for (cforge_size_t j = 0; j < chain.files.size(); ++j) {
std::string prefix;
if (j == 0) {
prefix = " ";
} else {
// Build tree prefix
prefix = " ";
for (size_t k = 1; k < j; ++k) {
for (cforge_size_t k = 1; k < j; ++k) {
prefix += "\xe2\x94\x82 "; // │
}
if (j == chain.files.size() - 1) {
@@ -311,13 +312,13 @@ format_circular_chains_json(const std::vector<circular_chain> &chains) {
ss << "{\n";
ss << " \"circular_dependencies\": [\n";
for (size_t i = 0; i < chains.size(); ++i) {
for (cforge_size_t i = 0; i < chains.size(); ++i) {
const auto &chain = chains[i];
ss << " {\n";
ss << " \"root\": \"" << chain.root << "\",\n";
ss << " \"chain\": [";
for (size_t j = 0; j < chain.files.size(); ++j) {
for (cforge_size_t j = 0; j < chain.files.size(); ++j) {
ss << "\"" << chain.files[j] << "\"";
if (j < chain.files.size() - 1) {
ss << ", ";
@@ -6,6 +6,7 @@
#include "core/installer.hpp"
#include "cforge/log.hpp"
#include "core/constants.h"
#include "core/types.h"
#include "core/file_system.h"
#include "core/process.h"
#include "core/process_utils.hpp"
@@ -335,13 +336,13 @@ bool installer::install_project(const std::string &project_path,
} else {
// Try different make tools
if (is_command_available("make")) {
int make_result = system("make");
cforge_int_t make_result = system("make");
build_success = (make_result == 0);
} else if (is_command_available("mingw32-make")) {
int make_result = system("mingw32-make");
cforge_int_t make_result = system("mingw32-make");
build_success = (make_result == 0);
} else if (is_command_available("nmake")) {
int make_result = system("nmake");
cforge_int_t make_result = system("nmake");
build_success = (make_result == 0);
}
}
@@ -351,7 +352,7 @@ bool installer::install_project(const std::string &project_path,
logger::print_warning("Make is not available. Skipping build");
build_success = false;
} else {
int make_result = system("make");
cforge_int_t make_result = system("make");
build_success = (make_result == 0);
}
#endif
@@ -780,7 +781,7 @@ std::string installer::get_install_location() const {
std::string delimiter = ":";
#endif
size_t pos = 0;
cforge_size_t pos = 0;
std::string token;
while ((pos = path_env.find(delimiter)) != std::string::npos) {
token = path_env.substr(0, pos);
+588
View File
@@ -0,0 +1,588 @@
/**
* @file portable_flags.cpp
* @brief Implementation of portable compiler flags translation
*/
#include "core/portable_flags.hpp"
#include "core/toml_reader.hpp"
#include <algorithm>
#include <sstream>
namespace cforge {
bool portable_options::has_any() const {
return !optimize.empty() || !warnings.empty() || warnings_as_errors ||
debug_info || !sanitizers.empty() || lto || !exceptions || !rtti ||
!stdlib.empty() || !hardening.empty() || !visibility.empty();
}
bool cmake_options::has_any() const {
return export_compile_commands || position_independent_code ||
interprocedural_optimization || visibility_hidden ||
!variables.empty();
}
portable_options parse_portable_options(const toml_reader &config,
const std::string &section) {
portable_options opts;
// Parse string options
opts.optimize = config.get_string(section + ".optimize", "");
opts.warnings = config.get_string(section + ".warnings", "");
opts.stdlib = config.get_string(section + ".stdlib", "");
opts.hardening = config.get_string(section + ".hardening", "");
opts.visibility = config.get_string(section + ".visibility", "");
// Parse boolean options
opts.warnings_as_errors =
config.get_bool(section + ".warnings_as_errors", false);
opts.debug_info = config.get_bool(section + ".debug_info", false);
opts.lto = config.get_bool(section + ".lto", false);
opts.exceptions = config.get_bool(section + ".exceptions", true);
opts.rtti = config.get_bool(section + ".rtti", true);
// Parse sanitizers array
opts.sanitizers = config.get_string_array(section + ".sanitizers");
return opts;
}
cmake_options parse_cmake_options(const toml_reader &config) {
cmake_options opts;
// Parse from [build] section
opts.export_compile_commands =
config.get_bool("build.export_compile_commands", false);
opts.position_independent_code =
config.get_bool("build.position_independent_code", false);
opts.interprocedural_optimization =
config.get_bool("build.interprocedural_optimization", false);
opts.visibility_hidden = config.get_bool("build.visibility_hidden", false);
// Parse custom variables from [build.cmake_variables]
opts.variables = config.get_string_map("build.cmake_variables");
return opts;
}
std::vector<std::string> translate_to_msvc(const portable_options &opts) {
std::vector<std::string> flags;
// Optimization
if (opts.optimize == "none" || opts.optimize == "debug") {
flags.push_back("/Od");
} else if (opts.optimize == "size") {
flags.push_back("/O1");
flags.push_back("/Os");
} else if (opts.optimize == "speed") {
flags.push_back("/O2");
} else if (opts.optimize == "aggressive") {
flags.push_back("/Ox");
}
// Warnings
if (opts.warnings == "none") {
flags.push_back("/W0");
} else if (opts.warnings == "default") {
flags.push_back("/W3");
} else if (opts.warnings == "all") {
flags.push_back("/W4");
} else if (opts.warnings == "strict") {
flags.push_back("/W4");
flags.push_back("/WX");
} else if (opts.warnings == "pedantic") {
flags.push_back("/W4");
flags.push_back("/WX");
flags.push_back("/permissive-");
}
// Warnings as errors (if not already set by warnings level)
if (opts.warnings_as_errors && opts.warnings != "strict" &&
opts.warnings != "pedantic") {
flags.push_back("/WX");
}
// Debug info
if (opts.debug_info) {
flags.push_back("/Zi");
}
// Sanitizers (MSVC only supports address sanitizer)
for (const auto &san : opts.sanitizers) {
if (san == "address") {
flags.push_back("/fsanitize=address");
}
// Note: undefined, thread, memory, leak are not supported on MSVC
}
// LTO (compile-time flag)
if (opts.lto) {
flags.push_back("/GL");
}
// Exceptions
if (opts.exceptions) {
flags.push_back("/EHsc");
} else {
flags.push_back("/EHs-c-");
}
// RTTI
if (opts.rtti) {
flags.push_back("/GR");
} else {
flags.push_back("/GR-");
}
// Security hardening
if (opts.hardening == "basic") {
flags.push_back("/GS");
flags.push_back("/sdl");
} else if (opts.hardening == "full") {
flags.push_back("/GS");
flags.push_back("/sdl");
flags.push_back("/GUARD:CF");
}
return flags;
}
std::vector<std::string> translate_to_msvc_link(const portable_options &opts) {
std::vector<std::string> flags;
// LTO (link-time flag)
if (opts.lto) {
flags.push_back("/LTCG");
}
// Security hardening (linker flags)
if (opts.hardening == "full") {
flags.push_back("/DYNAMICBASE");
flags.push_back("/NXCOMPAT");
flags.push_back("/GUARD:CF");
}
return flags;
}
std::vector<std::string> translate_to_gcc(const portable_options &opts) {
std::vector<std::string> flags;
// Optimization
if (opts.optimize == "none") {
flags.push_back("-O0");
} else if (opts.optimize == "debug") {
flags.push_back("-Og");
} else if (opts.optimize == "size") {
flags.push_back("-Os");
} else if (opts.optimize == "speed") {
flags.push_back("-O2");
} else if (opts.optimize == "aggressive") {
flags.push_back("-O3");
}
// Warnings
if (opts.warnings == "none") {
flags.push_back("-w");
} else if (opts.warnings == "all") {
flags.push_back("-Wall");
flags.push_back("-Wextra");
} else if (opts.warnings == "strict") {
flags.push_back("-Wall");
flags.push_back("-Wextra");
flags.push_back("-Werror");
} else if (opts.warnings == "pedantic") {
flags.push_back("-Wall");
flags.push_back("-Wextra");
flags.push_back("-Wpedantic");
flags.push_back("-Werror");
}
// Warnings as errors
if (opts.warnings_as_errors && opts.warnings != "strict" &&
opts.warnings != "pedantic") {
flags.push_back("-Werror");
}
// Debug info
if (opts.debug_info) {
flags.push_back("-g");
}
// Sanitizers
for (const auto &san : opts.sanitizers) {
if (san == "address") {
flags.push_back("-fsanitize=address");
} else if (san == "undefined") {
flags.push_back("-fsanitize=undefined");
} else if (san == "thread") {
flags.push_back("-fsanitize=thread");
} else if (san == "memory") {
// Memory sanitizer is Clang-only, skip for GCC
} else if (san == "leak") {
flags.push_back("-fsanitize=leak");
}
}
// LTO
if (opts.lto) {
flags.push_back("-flto");
}
// Exceptions
if (!opts.exceptions) {
flags.push_back("-fno-exceptions");
}
// RTTI
if (!opts.rtti) {
flags.push_back("-fno-rtti");
}
// Standard library (GCC defaults to libstdc++)
if (opts.stdlib == "libc++") {
// GCC doesn't support libc++, ignore
}
// Security hardening
if (opts.hardening == "basic") {
flags.push_back("-fstack-protector-strong");
flags.push_back("-D_FORTIFY_SOURCE=2");
} else if (opts.hardening == "full") {
flags.push_back("-fstack-protector-all");
flags.push_back("-D_FORTIFY_SOURCE=2");
flags.push_back("-fPIE");
}
// Visibility
if (opts.visibility == "hidden") {
flags.push_back("-fvisibility=hidden");
flags.push_back("-fvisibility-inlines-hidden");
} else if (opts.visibility == "default") {
flags.push_back("-fvisibility=default");
}
// Colored diagnostics
flags.push_back("-fdiagnostics-color=always");
return flags;
}
std::vector<std::string> translate_to_gcc_link(const portable_options &opts) {
std::vector<std::string> flags;
// Sanitizers need to be passed to linker too
for (const auto &san : opts.sanitizers) {
if (san == "address") {
flags.push_back("-fsanitize=address");
} else if (san == "undefined") {
flags.push_back("-fsanitize=undefined");
} else if (san == "thread") {
flags.push_back("-fsanitize=thread");
} else if (san == "leak") {
flags.push_back("-fsanitize=leak");
}
}
// LTO
if (opts.lto) {
flags.push_back("-flto");
}
// Security hardening (linker flags)
if (opts.hardening == "full") {
flags.push_back("-pie");
}
return flags;
}
std::vector<std::string> translate_to_clang(const portable_options &opts) {
std::vector<std::string> flags;
// Optimization (same as GCC)
if (opts.optimize == "none") {
flags.push_back("-O0");
} else if (opts.optimize == "debug") {
flags.push_back("-Og");
} else if (opts.optimize == "size") {
flags.push_back("-Os");
} else if (opts.optimize == "speed") {
flags.push_back("-O2");
} else if (opts.optimize == "aggressive") {
flags.push_back("-O3");
}
// Warnings (same as GCC)
if (opts.warnings == "none") {
flags.push_back("-w");
} else if (opts.warnings == "all") {
flags.push_back("-Wall");
flags.push_back("-Wextra");
} else if (opts.warnings == "strict") {
flags.push_back("-Wall");
flags.push_back("-Wextra");
flags.push_back("-Werror");
} else if (opts.warnings == "pedantic") {
flags.push_back("-Wall");
flags.push_back("-Wextra");
flags.push_back("-Wpedantic");
flags.push_back("-Werror");
}
// Warnings as errors
if (opts.warnings_as_errors && opts.warnings != "strict" &&
opts.warnings != "pedantic") {
flags.push_back("-Werror");
}
// Debug info
if (opts.debug_info) {
flags.push_back("-g");
}
// Sanitizers (Clang supports all)
for (const auto &san : opts.sanitizers) {
if (san == "address") {
flags.push_back("-fsanitize=address");
} else if (san == "undefined") {
flags.push_back("-fsanitize=undefined");
} else if (san == "thread") {
flags.push_back("-fsanitize=thread");
} else if (san == "memory") {
flags.push_back("-fsanitize=memory");
} else if (san == "leak") {
flags.push_back("-fsanitize=leak");
}
}
// LTO
if (opts.lto) {
flags.push_back("-flto");
}
// Exceptions
if (!opts.exceptions) {
flags.push_back("-fno-exceptions");
}
// RTTI
if (!opts.rtti) {
flags.push_back("-fno-rtti");
}
// Standard library (Clang supports both)
if (opts.stdlib == "libc++") {
flags.push_back("-stdlib=libc++");
} else if (opts.stdlib == "libstdc++") {
flags.push_back("-stdlib=libstdc++");
}
// Security hardening
if (opts.hardening == "basic") {
flags.push_back("-fstack-protector-strong");
flags.push_back("-D_FORTIFY_SOURCE=2");
} else if (opts.hardening == "full") {
flags.push_back("-fstack-protector-all");
flags.push_back("-D_FORTIFY_SOURCE=2");
flags.push_back("-fPIE");
}
// Visibility
if (opts.visibility == "hidden") {
flags.push_back("-fvisibility=hidden");
flags.push_back("-fvisibility-inlines-hidden");
} else if (opts.visibility == "default") {
flags.push_back("-fvisibility=default");
}
// Colored diagnostics (Clang uses different flag)
flags.push_back("-fcolor-diagnostics");
return flags;
}
std::vector<std::string> translate_to_clang_link(const portable_options &opts) {
std::vector<std::string> flags;
// Sanitizers
for (const auto &san : opts.sanitizers) {
if (san == "address") {
flags.push_back("-fsanitize=address");
} else if (san == "undefined") {
flags.push_back("-fsanitize=undefined");
} else if (san == "thread") {
flags.push_back("-fsanitize=thread");
} else if (san == "memory") {
flags.push_back("-fsanitize=memory");
} else if (san == "leak") {
flags.push_back("-fsanitize=leak");
}
}
// LTO
if (opts.lto) {
flags.push_back("-flto");
}
// Standard library
if (opts.stdlib == "libc++") {
flags.push_back("-stdlib=libc++");
} else if (opts.stdlib == "libstdc++") {
flags.push_back("-stdlib=libstdc++");
}
// Security hardening
if (opts.hardening == "full") {
flags.push_back("-pie");
}
return flags;
}
std::string join_flags(const std::vector<std::string> &flags) {
std::ostringstream oss;
for (cforge_size_t i = 0; i < flags.size(); ++i) {
if (i > 0)
oss << " ";
oss << flags[i];
}
return oss.str();
}
std::string generate_portable_flags_cmake(const portable_options &opts,
const std::string &target_name,
const std::string &indent) {
if (!opts.has_any()) {
return "";
}
std::ostringstream cmake;
auto msvc_flags = translate_to_msvc(opts);
auto msvc_link = translate_to_msvc_link(opts);
auto gcc_flags = translate_to_gcc(opts);
auto gcc_link = translate_to_gcc_link(opts);
auto clang_flags = translate_to_clang(opts);
auto clang_link = translate_to_clang_link(opts);
cmake << indent << "# Portable compiler flags\n";
cmake << indent
<< "if(MSVC AND NOT CMAKE_CXX_COMPILER_ID STREQUAL \"Clang\")\n";
if (!msvc_flags.empty()) {
cmake << indent << " target_compile_options(" << target_name
<< " PRIVATE";
for (const auto &flag : msvc_flags) {
cmake << " " << flag;
}
cmake << ")\n";
}
if (!msvc_link.empty()) {
cmake << indent << " target_link_options(" << target_name << " PRIVATE";
for (const auto &flag : msvc_link) {
cmake << " " << flag;
}
cmake << ")\n";
}
cmake << indent << "elseif(CMAKE_CXX_COMPILER_ID STREQUAL \"GNU\")\n";
if (!gcc_flags.empty()) {
cmake << indent << " target_compile_options(" << target_name
<< " PRIVATE";
for (const auto &flag : gcc_flags) {
cmake << " " << flag;
}
cmake << ")\n";
}
if (!gcc_link.empty()) {
cmake << indent << " target_link_options(" << target_name << " PRIVATE";
for (const auto &flag : gcc_link) {
cmake << " " << flag;
}
cmake << ")\n";
}
cmake << indent << "elseif(CMAKE_CXX_COMPILER_ID MATCHES \"Clang\")\n";
if (!clang_flags.empty()) {
cmake << indent << " target_compile_options(" << target_name
<< " PRIVATE";
for (const auto &flag : clang_flags) {
cmake << " " << flag;
}
cmake << ")\n";
}
if (!clang_link.empty()) {
cmake << indent << " target_link_options(" << target_name << " PRIVATE";
for (const auto &flag : clang_link) {
cmake << " " << flag;
}
cmake << ")\n";
}
cmake << indent << "endif()\n";
return cmake.str();
}
std::string generate_cmake_options(const cmake_options &opts) {
if (!opts.has_any()) {
return "";
}
std::ostringstream cmake;
cmake << "# CMake options\n";
if (opts.export_compile_commands) {
cmake << "set(CMAKE_EXPORT_COMPILE_COMMANDS ON)\n";
}
if (opts.position_independent_code) {
cmake << "set(CMAKE_POSITION_INDEPENDENT_CODE ON)\n";
}
if (opts.interprocedural_optimization) {
cmake << "set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON)\n";
}
if (opts.visibility_hidden) {
cmake << "set(CMAKE_CXX_VISIBILITY_PRESET hidden)\n";
cmake << "set(CMAKE_VISIBILITY_INLINES_HIDDEN ON)\n";
}
// Custom variables
for (const auto &[key, value] : opts.variables) {
cmake << "set(" << key << " \"" << value << "\")\n";
}
cmake << "\n";
return cmake.str();
}
std::string
generate_config_portable_flags_cmake(const std::string &config_name,
const portable_options &opts,
const std::string &target_name) {
if (!opts.has_any()) {
return "";
}
std::ostringstream cmake;
cmake << "# " << config_name << " configuration flags\n";
cmake << "if(CMAKE_BUILD_TYPE STREQUAL \"" << config_name << "\")\n";
cmake << generate_portable_flags_cmake(opts, target_name, " ");
cmake << "endif()\n\n";
return cmake.str();
}
} // namespace cforge
@@ -6,6 +6,7 @@
#include "core/process_utils.hpp"
#include "core/build_progress.hpp"
#include "core/error_format.hpp"
#include "core/types.h"
#include <algorithm>
#include <array>
#include <chrono>
@@ -29,7 +30,7 @@ execute_process(const std::string &command,
const std::string &working_dir,
std::function<void(const std::string &)> stdout_callback,
std::function<void(const std::string &)> stderr_callback,
int timeout_seconds) {
cforge_int_t timeout_seconds) {
process_result result;
result.exit_code = -1;
result.success = false;
@@ -119,7 +120,7 @@ execute_process(const std::string &command,
}
// Read output from the child process's pipes
const int BUFFER_SIZE = 4096;
const cforge_int_t BUFFER_SIZE = 4096;
std::array<char, BUFFER_SIZE> buffer;
DWORD bytes_read;
std::stringstream stdout_stream, stderr_stream;
@@ -173,7 +174,7 @@ execute_process(const std::string &command,
DWORD exit_code;
if (GetExitCodeProcess(pi.hProcess, &exit_code) &&
exit_code != STILL_ACTIVE) {
result.exit_code = static_cast<int>(exit_code);
result.exit_code = static_cast<cforge_int_t>(exit_code);
}
[[maybe_unused]] bool had_activity = false;
@@ -265,14 +266,14 @@ execute_process(const std::string &command,
const std::string &working_dir,
std::function<void(const std::string &)> stdout_callback,
std::function<void(const std::string &)> stderr_callback,
int timeout_seconds) {
cforge_int_t timeout_seconds) {
process_result result;
result.exit_code = -1;
result.success = false;
// Create pipes for stdout and stderr
int stdout_pipe[2];
int stderr_pipe[2];
cforge_int_t stdout_pipe[2];
cforge_int_t stderr_pipe[2];
if (pipe(stdout_pipe) == -1 || pipe(stderr_pipe) == -1) {
return result;
@@ -333,14 +334,14 @@ execute_process(const std::string &command,
fcntl(stderr_pipe[0], F_SETFL, O_NONBLOCK);
// Read from pipes
const int BUFFER_SIZE = 4096;
const cforge_int_t BUFFER_SIZE = 4096;
std::array<char, BUFFER_SIZE> buffer;
std::stringstream stdout_stream, stderr_stream;
bool child_running = true;
while (child_running) {
// Check child process status
int status;
cforge_int_t status;
pid_t wait_result = waitpid(pid, &status, WNOHANG);
if (wait_result == -1) {
@@ -430,7 +431,7 @@ std::string string_to_lower(const std::string &str) {
bool execute_tool(const std::string &command,
const std::vector<std::string> &args,
const std::string &working_dir, const std::string &tool_name,
bool verbose, int timeout_seconds) {
bool verbose, cforge_int_t timeout_seconds) {
std::string tool_name_lower = string_to_lower(tool_name);
// Check if the command is for a build tool that should use the error
@@ -674,7 +675,7 @@ bool execute_tool(const std::string &command,
fmt::print(fg(fmt::color::light_pink), " stderr:\n");
std::istringstream stderr_stream(result.stderr_output);
std::string line;
int line_count = 0;
cforge_int_t line_count = 0;
while (std::getline(stderr_stream, line) && line_count < 20) {
if (!line.empty()) {
fmt::print(fg(fmt::color::light_pink), " {}\n", line);
@@ -712,7 +713,7 @@ bool execute_tool(const std::string &command,
}
// Add a utility function to check if a command is available in the PATH
bool is_command_available(const std::string &command, int timeout_seconds) {
bool is_command_available(const std::string &command, cforge_int_t timeout_seconds) {
std::vector<std::string> args;
// Adjust default timeout based on command
@@ -4,6 +4,7 @@
*/
#include "core/test_output_formatter.hpp"
#include "core/types.h"
#include <algorithm>
#include <filesystem>
#include <fmt/color.h>
@@ -27,7 +28,7 @@ TestOutputFormatter::TestOutputFormatter(Style style) : m_style(style) {}
// Formatting Methods (return strings)
// ============================================================================
std::string TestOutputFormatter::format_run_start(int total_tests) {
std::string TestOutputFormatter::format_run_start(cforge_int_t total_tests) {
std::ostringstream ss;
ss << "\nrunning " << total_tests << " tests\n";
return ss.str();
@@ -118,7 +119,7 @@ std::string TestOutputFormatter::format_failure_details(const TestResult &result
std::string source_line = read_source_line(result.file_path, result.line_number);
if (!source_line.empty()) {
// Line number gutter
int gutter_width = 4;
cforge_int_t gutter_width = 4;
ss << " |\n";
ss << std::setw(gutter_width) << result.line_number << " | " << source_line << "\n";
ss << " | ";
@@ -126,7 +127,7 @@ std::string TestOutputFormatter::format_failure_details(const TestResult &result
// Add carets under the line (pointing to assertion)
if (!result.assertion_expr.empty()) {
ss << "^";
for (size_t i = 1; i < source_line.length() && i < 40; ++i) {
for (cforge_size_t i = 1; i < source_line.length() && i < 40; ++i) {
ss << "^";
}
}
@@ -228,7 +229,7 @@ std::string TestOutputFormatter::format_test_list(
// Printing Methods (output with colors)
// ============================================================================
void TestOutputFormatter::print_run_start(int total_tests) {
void TestOutputFormatter::print_run_start(cforge_int_t total_tests) {
fmt::print("\nrunning {} tests\n", total_tests);
}
@@ -318,7 +319,7 @@ void TestOutputFormatter::print_failure_details(const TestResult &result) {
// Add carets under the line
fmt::print(fg(fmt::color::red), "");
for (size_t i = 0; i < source_line.length() && i < 40; ++i) {
for (cforge_size_t i = 0; i < source_line.length() && i < 40; ++i) {
fmt::print(fg(fmt::color::red), "^");
}
fmt::print("\n");
@@ -447,19 +448,19 @@ void TestOutputFormatter::print_native_output(const std::string &output) {
// ============================================================================
std::string TestOutputFormatter::read_source_line(const std::string &file_path,
int line_number) {
cforge_int_t line_number) {
if (line_number <= 0) return "";
std::ifstream file(file_path);
if (!file) return "";
std::string line;
int current_line = 0;
cforge_int_t current_line = 0;
while (std::getline(file, line)) {
current_line++;
if (current_line == line_number) {
// Trim leading whitespace but preserve some indent
size_t first_non_space = line.find_first_not_of(" \t");
cforge_size_t first_non_space = line.find_first_not_of(" \t");
if (first_non_space != std::string::npos && first_non_space > 0) {
line = line.substr(first_non_space);
}
@@ -471,7 +472,7 @@ std::string TestOutputFormatter::read_source_line(const std::string &file_path,
}
std::string TestOutputFormatter::shorten_path(const std::string &path,
size_t max_length) {
cforge_size_t max_length) {
if (path.length() <= max_length) {
return path;
}
@@ -502,13 +503,13 @@ std::string TestOutputFormatter::format_duration(std::chrono::milliseconds durat
if (ms < 1000) {
return std::to_string(ms) + "ms";
} else if (ms < 60000) {
double secs = ms / 1000.0;
cforge_double_t secs = ms / 1000.0;
std::ostringstream ss;
ss << std::fixed << std::setprecision(2) << secs << "s";
return ss.str();
} else {
int mins = static_cast<int>(ms / 60000);
int secs = static_cast<int>((ms % 60000) / 1000);
cforge_int_t mins = static_cast<cforge_int_t>(ms / 60000);
cforge_int_t secs = static_cast<cforge_int_t>((ms % 60000) / 1000);
std::ostringstream ss;
ss << mins << "m " << secs << "s";
return ss.str();
@@ -521,7 +522,7 @@ TestOutputFormatter::group_tests_by_suite(const std::vector<std::string> &tests)
for (const auto &test : tests) {
// Find suite separator (::, ., or /)
size_t sep = test.find("::");
cforge_size_t sep = test.find("::");
if (sep == std::string::npos) {
sep = test.find('.');
}
@@ -5,6 +5,7 @@
#include "core/test_runner.hpp"
#include "cforge/log.hpp"
#include "core/types.h"
#include "core/process_utils.hpp"
#include "core/test_adapters.hpp"
#include "core/workspace.hpp"
@@ -42,9 +43,9 @@ bool TestRunner::load_config() {
// Load [test] section defaults
m_test_config.directory = m_project_config.get_string("test.directory", "tests");
m_test_config.default_timeout =
static_cast<int>(m_project_config.get_int("test.timeout", 300));
static_cast<cforge_int_t>(m_project_config.get_int("test.timeout", 300));
m_test_config.jobs =
static_cast<int>(m_project_config.get_int("test.jobs", 0));
static_cast<cforge_int_t>(m_project_config.get_int("test.jobs", 0));
m_test_config.auto_link_project =
m_project_config.get_bool("test.auto_link_project", true);
m_test_config.discovery_mode =
@@ -143,7 +144,7 @@ std::vector<TestTarget> TestRunner::load_explicit_targets() {
target.defines = table.get_string_array("defines");
target.includes = table.get_string_array("includes");
target.timeout_seconds =
static_cast<int>(table.get_int("timeout", m_test_config.default_timeout));
static_cast<cforge_int_t>(table.get_int("timeout", m_test_config.default_timeout));
target.enabled = table.get_bool("enabled", true);
// Parse framework
@@ -629,7 +630,7 @@ std::vector<TestResult> TestRunner::run_target(const TestTarget &target,
}
// Execute test
int timeout = options.timeout_override > 0 ? options.timeout_override : target.timeout_seconds;
cforge_int_t timeout = options.timeout_override > 0 ? options.timeout_override : target.timeout_seconds;
auto proc_result = execute_process(exe.string(), args,
m_project_dir.string(),
@@ -8,9 +8,11 @@
#include "core/config_resolver.hpp"
#include "core/constants.h"
#include "core/dependency_hash.hpp"
#include "core/portable_flags.hpp"
#include "core/process_utils.hpp"
#include "core/registry.hpp"
#include "core/toml_reader.hpp"
#include "core/types.h"
#include <algorithm>
#include <fstream>
@@ -79,9 +81,9 @@ static bool run_cmake_configure(const std::vector<std::string> &cmake_args,
const std::string &build_dir, bool verbose) {
// Set a longer timeout for Windows
#ifdef _WIN32
int timeout = 180; // 3 minutes for Windows
cforge_int_t timeout = 180; // 3 minutes for Windows
#else
int timeout = 120; // 2 minutes for other platforms
cforge_int_t timeout = 120; // 2 minutes for other platforms
#endif
// Run the CMake command with appropriate timeout
@@ -975,7 +977,7 @@ void configure_index_dependencies_fetchcontent_phase1(
// If using tag_pattern, construct the tag
if (git_tag == resolved_version && !pkg_info->tags.pattern.empty()) {
git_tag = pkg_info->tags.pattern;
size_t pos = git_tag.find("{version}");
cforge_size_t pos = git_tag.find("{version}");
if (pos != std::string::npos) {
git_tag.replace(pos, 9, resolved_version);
}
@@ -1002,7 +1004,7 @@ void configure_index_dependencies_fetchcontent_phase1(
// FetchContent_MakeAvailable for all dependencies
if (!deps_to_fetch.empty()) {
cmakelists << "FetchContent_MakeAvailable(";
for (size_t i = 0; i < deps_to_fetch.size(); ++i) {
for (cforge_size_t i = 0; i < deps_to_fetch.size(); ++i) {
if (i > 0) cmakelists << " ";
cmakelists << deps_to_fetch[i];
}
@@ -1039,14 +1041,14 @@ void configure_index_dependencies_fetchcontent_phase1(
// Replace placeholders
std::string cmake_cmd = cmd;
// Replace {package_dir} with CMake variable
size_t pos;
cforge_size_t pos;
while ((pos = cmake_cmd.find("{package_dir}")) != std::string::npos) {
cmake_cmd.replace(pos, 13, "${" + source_dir_var + "}");
}
// Replace {option:name} placeholders with CMake defaults
size_t start = 0;
cforge_size_t start = 0;
while ((start = cmake_cmd.find("{option:", start)) != std::string::npos) {
size_t end = cmake_cmd.find("}", start);
cforge_size_t end = cmake_cmd.find("}", start);
if (end == std::string::npos) break;
std::string option_name = cmake_cmd.substr(start + 8, end - start - 8);
// Use default value from package
@@ -1074,13 +1076,13 @@ void configure_index_dependencies_fetchcontent_phase1(
pkg_info->setup.commands : pkg_info->setup.macos.commands;
for (const auto &cmd : mac_cmds) {
std::string cmake_cmd = cmd;
size_t pos;
cforge_size_t pos;
while ((pos = cmake_cmd.find("{package_dir}")) != std::string::npos) {
cmake_cmd.replace(pos, 13, "${" + source_dir_var + "}");
}
size_t start = 0;
cforge_size_t start = 0;
while ((start = cmake_cmd.find("{option:", start)) != std::string::npos) {
size_t end = cmake_cmd.find("}", start);
cforge_size_t end = cmake_cmd.find("}", start);
if (end == std::string::npos) break;
std::string option_name = cmake_cmd.substr(start + 8, end - start - 8);
std::string default_val;
@@ -1107,13 +1109,13 @@ void configure_index_dependencies_fetchcontent_phase1(
pkg_info->setup.commands : pkg_info->setup.linux.commands;
for (const auto &cmd : linux_cmds) {
std::string cmake_cmd = cmd;
size_t pos;
cforge_size_t pos;
while ((pos = cmake_cmd.find("{package_dir}")) != std::string::npos) {
cmake_cmd.replace(pos, 13, "${" + source_dir_var + "}");
}
size_t start = 0;
cforge_size_t start = 0;
while ((start = cmake_cmd.find("{option:", start)) != std::string::npos) {
size_t end = cmake_cmd.find("}", start);
cforge_size_t end = cmake_cmd.find("}", start);
if (end == std::string::npos) break;
std::string option_name = cmake_cmd.substr(start + 8, end - start - 8);
std::string default_val;
@@ -1274,9 +1276,9 @@ bool generate_cmakelists_from_toml(const std::filesystem::path &project_dir,
// Platform override: read [platform.<plat>] in cforge.toml
std::string cforge_platform;
#ifdef _WIN32
#if defined(_WIN32)
cforge_platform = "windows";
#elif __APPLE__
#elif defined(__APPLE__)
cforge_platform = "macos";
#else
cforge_platform = "linux";
@@ -1372,7 +1374,7 @@ bool generate_cmakelists_from_toml(const std::filesystem::path &project_dir,
project_config.get_string_array("project.authors");
std::string author_string;
if (!authors.empty()) {
for (size_t i = 0; i < authors.size(); ++i) {
for (cforge_size_t i = 0; i < authors.size(); ++i) {
if (i > 0)
author_string += ", ";
author_string += authors[i];
@@ -1432,6 +1434,12 @@ bool generate_cmakelists_from_toml(const std::filesystem::path &project_dir,
cmakelists << "endif()\n";
cmakelists << "message(STATUS \"Platform: ${CFORGE_PLATFORM}, Compiler: ${CFORGE_COMPILER}\")\n\n";
// CMake options from [build] section
cmake_options cmake_opts = parse_cmake_options(project_config);
if (cmake_opts.has_any()) {
cmakelists << generate_cmake_options(cmake_opts);
}
// Get binary type (executable, shared_lib, static_lib, or header_only)
std::string binary_type =
project_config.get_string("project.binary_type", "executable");
@@ -1816,6 +1824,87 @@ bool generate_cmakelists_from_toml(const std::filesystem::path &project_dir,
}
}
// Portable flags from build.config sections
{
std::vector<std::string> configs = {"debug", "release", "relwithdebinfo", "minsizerel"};
bool has_any_config_portable = false;
for (const auto &cfg : configs) {
std::string section = "build.config." + cfg;
portable_options opts = parse_portable_options(project_config, section);
if (opts.has_any()) {
has_any_config_portable = true;
break;
}
}
if (has_any_config_portable && binary_type != "header_only") {
cmakelists << "# Portable compiler flags per configuration\n";
for (const auto &cfg : configs) {
std::string section = "build.config." + cfg;
portable_options opts = parse_portable_options(project_config, section);
if (opts.has_any()) {
// Capitalize first letter for CMake build type
std::string cmake_cfg = cfg;
cmake_cfg[0] = static_cast<char>(std::toupper(static_cast<unsigned char>(cmake_cfg[0])));
cmakelists << generate_config_portable_flags_cmake(cmake_cfg, opts, "${PROJECT_NAME}");
}
}
}
}
// Portable flags from platform sections
{
bool has_any_platform_portable = false;
for (const auto &plat : platforms) {
std::string section = "platform." + plat;
portable_options opts = parse_portable_options(project_config, section);
if (opts.has_any()) {
has_any_platform_portable = true;
break;
}
}
if (has_any_platform_portable && binary_type != "header_only") {
cmakelists << "# Portable compiler flags per platform\n";
for (const auto &plat : platforms) {
std::string section = "platform." + plat;
portable_options opts = parse_portable_options(project_config, section);
if (opts.has_any()) {
cmakelists << "if(CFORGE_PLATFORM STREQUAL \"" << plat << "\")\n";
cmakelists << generate_portable_flags_cmake(opts, "${PROJECT_NAME}", " ");
cmakelists << "endif()\n\n";
}
}
}
}
// Portable flags from compiler sections
{
bool has_any_compiler_portable = false;
for (const auto &comp : compilers) {
std::string section = "compiler." + comp;
portable_options opts = parse_portable_options(project_config, section);
if (opts.has_any()) {
has_any_compiler_portable = true;
break;
}
}
if (has_any_compiler_portable && binary_type != "header_only") {
cmakelists << "# Portable compiler flags per compiler\n";
for (const auto &comp : compilers) {
std::string section = "compiler." + comp;
portable_options opts = parse_portable_options(project_config, section);
if (opts.has_any()) {
cmakelists << "if(CFORGE_COMPILER STREQUAL \"" << comp << "\")\n";
cmakelists << generate_portable_flags_cmake(opts, "${PROJECT_NAME}", " ");
cmakelists << "endif()\n\n";
}
}
}
}
// Add config-specific build.config.<config>.defines
{
std::string defs_key =
@@ -2392,7 +2481,7 @@ bool generate_cmakelists_from_toml(const std::filesystem::path &project_dir,
return true;
}
bool workspace::build_all(const std::string &config, int num_jobs,
bool workspace::build_all(const std::string &config, cforge_int_t num_jobs,
bool verbose) const {
if (projects_.empty()) {
logger::print_warning("No projects in workspace");
@@ -2436,7 +2525,7 @@ bool workspace::build_all(const std::string &config, int num_jobs,
if (verbose) {
logger::print_action("Build order", "");
for (size_t i = 0; i < build_order.size(); ++i) {
for (cforge_size_t i = 0; i < build_order.size(); ++i) {
logger::print_action("", " " + std::to_string(i + 1) + ". " +
build_order[i]);
}
@@ -2566,7 +2655,7 @@ bool workspace::build_all(const std::string &config, int num_jobs,
}
bool workspace::build_project(const std::string &project_name,
const std::string &config, int num_jobs,
const std::string &config, cforge_int_t num_jobs,
bool verbose, const std::string &target) const {
// Find the project
const workspace_project *project = get_project_by_name(project_name);