mirror of
https://github.com/dexoron/dcr.git
synced 2026-10-21 08:48:30 +00:00
feat: enhance build configuration with C++ standard support and variable substitution
- Added `cxx_standard` field to the build configuration for specifying C++ standards. - Updated documentation to reflect new `cxx_standard` and variable substitution capabilities in `cflags`, `ldflags`, and `include`. - Implemented logic to handle C++ standard flags in the build process. - Modified object file naming to include source file extensions. - Updated various builder modules to support new language parsing logic. - Introduced issue and feature request templates for better community engagement.
This commit is contained in:
51
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
51
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: Something is broken or behaves unexpectedly
|
||||
title: "[bug] "
|
||||
labels: bug
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- A clear and concise description of the bug -->
|
||||
|
||||
## Steps to Reproduce
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
<!-- What should have happened -->
|
||||
|
||||
## Actual Behavior
|
||||
|
||||
<!-- What actually happened, include full error output -->
|
||||
|
||||
```
|
||||
paste error output here
|
||||
```
|
||||
|
||||
## Environment
|
||||
|
||||
- **DCR version** (`dcr --version`):
|
||||
- **OS**:
|
||||
- **Compiler / toolchain** (`gcc --version` or `clang --version`):
|
||||
|
||||
## Minimal Reproducible Project
|
||||
|
||||
<!-- If possible, share a minimal dcr.toml + source that reproduces the issue -->
|
||||
|
||||
```toml
|
||||
# dcr.toml
|
||||
```
|
||||
|
||||
```c
|
||||
// src/main.c
|
||||
```
|
||||
|
||||
## Additional Context
|
||||
|
||||
<!-- Anything else that might help -->
|
||||
35
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
35
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: Suggest an idea or improvement
|
||||
title: "[feat] "
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
## Problem / Motivation
|
||||
|
||||
<!-- What problem does this solve? What are you trying to do that's currently hard or impossible? -->
|
||||
|
||||
## Proposed Solution
|
||||
|
||||
<!-- Describe what you want. Be as specific as possible — CLI flags, config syntax, behavior -->
|
||||
|
||||
## Example
|
||||
|
||||
<!-- Show how it would look in practice (command, dcr.toml snippet, output) -->
|
||||
|
||||
```sh
|
||||
# example command
|
||||
```
|
||||
|
||||
```toml
|
||||
# example config
|
||||
```
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
<!-- Have you tried workarounds? Are there other ways to solve this? -->
|
||||
|
||||
## Additional Context
|
||||
|
||||
<!-- Links, prior art, related issues, etc. -->
|
||||
57
.github/workflows/release.yml
vendored
57
.github/workflows/release.yml
vendored
@@ -13,6 +13,10 @@ on:
|
||||
description: "Build Linux"
|
||||
type: boolean
|
||||
default: true
|
||||
run_bsd:
|
||||
description: "Build BSD"
|
||||
type: boolean
|
||||
default: true
|
||||
run_macos:
|
||||
description: "Build macOS"
|
||||
type: boolean
|
||||
@@ -190,6 +194,55 @@ jobs:
|
||||
body_path: .github/release-notes.md
|
||||
prerelease: ${{ needs.setup.outputs.prerelease == 'true' }}
|
||||
|
||||
# ── BSD ────────────────────────────────────────────────────────────────────
|
||||
build-bsd:
|
||||
if: ${{ github.event_name == 'push' || github.event.inputs.run_bsd == 'true' }}
|
||||
needs: [setup, notes]
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- target: x86_64-unknown-freebsd
|
||||
# При необходимости сюда можно докинуть x86_64-unknown-netbsd
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ needs.setup.outputs.tag }}
|
||||
|
||||
- name: Download Release Notes
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: release-notes
|
||||
path: .github
|
||||
|
||||
- name: Install cargo-zigbuild
|
||||
run: |
|
||||
pip install cargo-zigbuild
|
||||
echo "$HOME/.local/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Setup Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
|
||||
- name: Build
|
||||
run: cargo zigbuild --release --target ${{ matrix.target }}
|
||||
|
||||
- name: Prepare Artifact
|
||||
run: |
|
||||
ASSET="dcr-${{ matrix.target }}-${{ needs.setup.outputs.version }}"
|
||||
cp target/${{ matrix.target }}/release/dcr "$ASSET"
|
||||
echo "ASSET_NAME=${ASSET}" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Upload Release Asset
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: ${{ needs.setup.outputs.tag }}
|
||||
files: ${{ env.ASSET_NAME }}
|
||||
body_path: .github/release-notes.md
|
||||
prerelease: ${{ needs.setup.outputs.prerelease == 'true' }}
|
||||
|
||||
# ── Windows ────────────────────────────────────────────────────────────────
|
||||
build-windows:
|
||||
if: ${{ github.event_name == 'push' || github.event.inputs.run_windows == 'true' }}
|
||||
@@ -241,7 +294,7 @@ jobs:
|
||||
if: |
|
||||
(github.event_name == 'push' && needs.setup.outputs.prerelease == 'false') ||
|
||||
(github.event_name == 'workflow_dispatch' && github.event.inputs.run_publish == 'true')
|
||||
needs: [setup, build-linux, build-macos, build-windows]
|
||||
needs: [setup, build-linux, build-macos, build-windows, build-bsd]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -258,7 +311,7 @@ jobs:
|
||||
if: |
|
||||
(github.event_name == 'push' && needs.setup.outputs.prerelease == 'false') ||
|
||||
(github.event_name == 'workflow_dispatch' && github.event.inputs.run_brew == 'true')
|
||||
needs: [setup, build-linux, build-macos, build-windows]
|
||||
needs: [setup, build-linux, build-macos]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download release assets and compute sha256
|
||||
|
||||
51
CHANGELOG.md
51
CHANGELOG.md
@@ -1,5 +1,56 @@
|
||||
# Changelog
|
||||
|
||||
## [0.6.8] - 2026-05-27
|
||||
|
||||
Added:
|
||||
|
||||
- `build.cxx_standard` — separate C++ language standard for `.cpp`/`.cxx`/`.cc` files.
|
||||
When set, overrides `build.standard` for C++ sources. Example:
|
||||
|
||||
```toml
|
||||
[build]
|
||||
language = ["c", "c++"]
|
||||
standard = "gnu11"
|
||||
cxx_standard = "gnu++17"
|
||||
```
|
||||
|
||||
- Variable substitution for `cflags`, `ldflags`, and `include` directory entries.
|
||||
All `{version}`, `{version_major}`, `{name}`, `{profile}` and other step variables
|
||||
are now available. Example:
|
||||
|
||||
```toml
|
||||
[build]
|
||||
cflags = ['-DPROJECT_VERSION="{version}"']
|
||||
```
|
||||
|
||||
- FreeBSD build support in CI — new `run_bsd` workflow input and `build-bsd` job
|
||||
that cross-compiles `x86_64-unknown-freebsd` via `cargo-zigbuild`.
|
||||
|
||||
Changed:
|
||||
|
||||
- License changed from MIT to **GPL-3.0-or-later**. All source files now carry
|
||||
the GPL-3.0 header. Default license in `dcr new` / `dcr init` changed to
|
||||
`"GPL-3.0-or-later"`. README, FAQ, and CONTRIBUTING.md updated accordingly.
|
||||
- `get_list_with_profile_and_target` no longer duplicates list values when
|
||||
`inherit = true` and the profile/target value is identical to the base value.
|
||||
|
||||
Fixed:
|
||||
|
||||
- Multi-language `language` array (e.g. `["c", "c++", "asm"]`) no longer drops `c++`
|
||||
during internal parsing — `.cxx`/`.cpp`/`.cc` source files are correctly discovered
|
||||
and compiled.
|
||||
- `-std=` flag is no longer passed to C++ files when only `standard` (C standard) is
|
||||
configured — uses `cxx_standard` when available, otherwise skips `-std=` for C++
|
||||
sources, avoiding clang error `invalid argument '-std=c11' not allowed with 'C++'`.
|
||||
- Object file collision: source files with the same stem but different extensions
|
||||
(e.g. `gdt.cxx` and `gdt.S`) no longer overwrite each other. Object filenames
|
||||
now include the original source extension (e.g. `gdt.cxx.o`, `gdt.S.o`).
|
||||
- MSVC backend compile step now detects language per source file extension instead
|
||||
of using the global `language` field, fixing mixed C/C++ compilation with MSVC.
|
||||
- Assembler `-x` flag in `unix_cc.rs` and `gen.rs` is now placed **before** the
|
||||
source file, as required by clang/gcc.
|
||||
- `{version}` variables in `build.clean` are now correctly substituted.
|
||||
|
||||
## [0.6.7] - 2026-05-26
|
||||
|
||||
Added:
|
||||
|
||||
367
README.md
367
README.md
@@ -1,283 +1,208 @@
|
||||
# DCR (Dexoron Cargo Realization)
|
||||
<div align="center">
|
||||
|
||||
DCR is a utility for managing C/C++ projects in a Cargo-like style.
|
||||
Created and maintained by **Dexoron (Bezotechestvo Vladimir)**.
|
||||
# DCR — Dexoron Cargo Realization
|
||||
|
||||
The current implementation is written in Rust.
|
||||
**A Cargo-style build tool for C/C++ projects**
|
||||
|
||||
## Why DCR
|
||||
[](https://github.com/dexoron/dcr/actions/workflows/ci.yml)
|
||||
[](https://github.com/dexoron/dcr/releases/latest)
|
||||
[](https://github.com/dexoron/dcr/releases)
|
||||
<br/>
|
||||
[](https://aur.archlinux.org/packages/dcr)
|
||||
[](https://crates.io/crates/dcr)
|
||||
[](https://github.com/dexoron/homebrew-dexoron)
|
||||
<br/>
|
||||
[](https://github.com/dexoron/dcr/stargazers)
|
||||
[](LICENSE)
|
||||
|
||||
- Unified project structure without manual setup
|
||||
- Simple commands for common tasks
|
||||
- Transparent compilation and predictable build profiles
|
||||
</div>
|
||||
|
||||
## Features
|
||||
---
|
||||
|
||||
- Create a new project or initialize the current directory
|
||||
- Build a project with `debug` and `release` profiles
|
||||
- Run the compiled binary
|
||||
- Clean build artifacts
|
||||
- Generate IDE integration files (VS Code, CLion) and compilation databases
|
||||
- Generate a minimal C project template
|
||||
- Build static, shared libraries, UEFI PE32+ executables, and bare-metal ELF executables
|
||||
- Dependency management:
|
||||
- Git and Path dependencies with `dcr.lock`
|
||||
- Registry-based dependency resolution
|
||||
- Library packaging functionality (auto-generate `include/` and `lib/` for `type = "lib"` packages)
|
||||
- ASM projects with NASM/GAS or via GCC/Clang
|
||||
- Mixed language projects
|
||||
- Cross-compilation with `--target` (full triple support and short names)
|
||||
- Target-specific configurations and inheritance
|
||||
- Update the binary via `dcr --update` (GitHub Releases)
|
||||
## Why DCR?
|
||||
|
||||
## Supported Platforms
|
||||
- **No boilerplate** — one config file, predictable structure
|
||||
- **Cargo-like workflow** — `build`, `run`, `clean`, `test`, `add`
|
||||
- **Cross-compilation** — full target triple support with short names
|
||||
- **IDE integration** — VS Code, CLion, `compile_commands.json` out of the box
|
||||
- **Dependencies** — path, git, and registry-based with lock file
|
||||
|
||||
- Linux: x86_64/aarch64 (GNU and Musl)
|
||||
- macOS: x86_64/aarch64
|
||||
- Windows: x86_64/aarch64 (MSVC, GNU, LLVM)
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
### Package Manager
|
||||
|
||||
**Arch Linux**
|
||||
<table>
|
||||
<tr>
|
||||
<td><b>Arch Linux (AUR)</b></td>
|
||||
<td>
|
||||
|
||||
```sh
|
||||
yay -S dcr # or paru and other AUR package managers
|
||||
yay -S dcr
|
||||
```
|
||||
|
||||
### From Source
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>macOS/Linux(GNU) (Homebrew)</b></td>
|
||||
<td>
|
||||
|
||||
```sh
|
||||
git clone https://github.com/dexoron/dcr.git
|
||||
cd dcr
|
||||
cargo build --release
|
||||
mkdir -p ~/.local/bin
|
||||
ln -sf "$PWD/target/release/dcr" ~/.local/bin/dcr
|
||||
brew tap dexoron/dexoron
|
||||
brew install dcr
|
||||
```
|
||||
|
||||
### Via `install.sh` (Linux/macOS)
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Cargo (crates.io)</b></td>
|
||||
<td>
|
||||
|
||||
```sh
|
||||
cargo install dcr
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Linux / macOS (script)</b></td>
|
||||
<td>
|
||||
|
||||
```sh
|
||||
curl -fsSL https://dcr.dexoron.su/install.sh | bash
|
||||
```
|
||||
|
||||
### Via `install.ps1` (Windows)
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Windows (PowerShell)</b></td>
|
||||
<td>
|
||||
|
||||
```powershell
|
||||
irm https://dcr.dexoron.su/install.ps1 | iex
|
||||
```
|
||||
|
||||
When executed, both scripts ask whether to:
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>From source</b></td>
|
||||
<td>
|
||||
|
||||
- download a prebuilt binary from GitHub Releases
|
||||
- or build the project from `git`
|
||||
```sh
|
||||
git clone https://github.com/dexoron/dcr.git
|
||||
cd dcr && cargo build --release
|
||||
ln -sf "$PWD/target/release/dcr" ~/.local/bin/dcr
|
||||
```
|
||||
|
||||
Release assets:
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
- `dcr-x86_64-unknown-linux-gnu`
|
||||
- `dcr-x86_64-apple-darwin`
|
||||
- `dcr-aarch64-apple-darwin`
|
||||
- `dcr-x86_64-pc-windows-msvc.exe`
|
||||
|
||||
## Update
|
||||
|
||||
- If DCR was installed from GitHub release assets, `install.sh`, `install.ps1`, or built manually:
|
||||
- use `dcr --update`
|
||||
- If DCR is installed via `pacman/AUR`:
|
||||
- update with your package manager: `paru/yay -Syu dcr` or `sudo pacman -Syu dcr`
|
||||
- `dcr --update` detects package-managed installs and asks you to update via package manager
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
Create a new project:
|
||||
`dcr new hello`
|
||||
|
||||
Or initialize the current directory (the directory must be empty):
|
||||
`dcr init`
|
||||
|
||||
Project structure:
|
||||
|
||||
```txt
|
||||
hello/
|
||||
- src/
|
||||
- - main.c
|
||||
- dcr.toml
|
||||
```
|
||||
|
||||
Build and run the project:
|
||||
`dcr run` or `dcr run --release`
|
||||
|
||||
## Commands
|
||||
|
||||
### `dcr new <name>`
|
||||
|
||||
Creates a project with the specified name in the current directory.
|
||||
|
||||
### `dcr init`
|
||||
|
||||
Creates a project in the current directory. The project name is taken from the directory name. The directory must be empty.
|
||||
|
||||
### `dcr setup`
|
||||
|
||||
Initializes the registry system by reading `~/.dcr/config.toml` and verifying the availability of configured indices.
|
||||
|
||||
### `dcr add <name> <source>`
|
||||
|
||||
Adds a dependency to `dcr.toml`.
|
||||
If a registry is configured, `dcr` automatically finds the package.
|
||||
Git/Path syntax: `path:`, `github:`, `gitlab:`, `git:`, or a full URL.
|
||||
Use `--branch`, `--tag`, or `--rev` for Git dependencies.
|
||||
|
||||
### `dcr build [profile]`
|
||||
|
||||
Builds the project. If no profile is specified, `--debug` is used.
|
||||
Use `--force` to rebuild without cache, `--clean` to clean before build.
|
||||
|
||||
### `dcr run [profile]`
|
||||
|
||||
Builds the project and runs the binary. If no profile is specified, `--debug` is used.
|
||||
Use `--force` to rebuild without cache, `--clean` to clean before build.
|
||||
|
||||
Run manually:
|
||||
`./target/<profile>/<name>`
|
||||
|
||||
### `dcr clean`
|
||||
|
||||
Removes the `target` directory in the project root.
|
||||
Use `dcr clean --all` in a workspace root to clean all member projects.
|
||||
|
||||
### `dcr gen`
|
||||
|
||||
Generates IDE integration files and build tools.
|
||||
|
||||
- `dcr gen vscode`: VS Code workspace config
|
||||
- `dcr gen clion`: CLion project files
|
||||
- `dcr gen compile-commands`: `compile_commands.json` for clang tools
|
||||
|
||||
### `dcr test`
|
||||
|
||||
Runs the test suite.
|
||||
Use `dcr test --init` to create test files.
|
||||
|
||||
## Build Profiles
|
||||
|
||||
Two profiles are supported:
|
||||
|
||||
- `--debug` (default) - built-in flags per compiler
|
||||
- `--release` - built-in flags per compiler
|
||||
|
||||
Custom flags can be added in `dcr.toml` via `build.cflags` and `build.ldflags`.
|
||||
Profile-specific flags can be added via `[build.debug]` and `[build.release]`.
|
||||
You can also set `build.target` to override the output directory (profile-independent).
|
||||
You can set `build.platform` to pass `-march=<platform>` (GCC/Clang) or `/arch:*` (MSVC) where supported.
|
||||
Use `build.kind = "staticlib"` to build a static library instead of a binary.
|
||||
Use `build.kind = "sharedlib"` to build a shared library (`.so`/`.dylib`/`.dll`).
|
||||
Use `build.kind = "efi"` to build a UEFI PE32+ executable (`.efi`).
|
||||
Use `build.kind = "elf"` to build a bare-metal ELF executable.
|
||||
`dcr run` is only for `build.kind = "bin"` and will fail for libraries, EFI, and ELF.
|
||||
Use `build.exclude`/`build.include` to control source/header collection.
|
||||
Use `build.roots` and `build.src_disable` to replace the default `src/` root.
|
||||
Use `build.steps` and `build.post_steps` to run custom commands.
|
||||
Use `build.clean` for extra cleanup paths and `[run].cmd` to override run command.
|
||||
|
||||
## Cross-compilation
|
||||
|
||||
DCR supports cross-compilation via `--target <triple>` or short names:
|
||||
|
||||
```sh
|
||||
dcr build --target linux --release # x86_64-unknown-linux-gnu
|
||||
dcr build --target macos --release # x86_64-apple-darwin
|
||||
dcr build --target windows --release # x86_64-pc-windows-msvc
|
||||
dcr run --target aarch64-linux-gnu
|
||||
dcr new hello
|
||||
cd hello
|
||||
dcr run
|
||||
```
|
||||
|
||||
Configure multiple targets and target-specific settings in `dcr.toml`:
|
||||
**Project structure:**
|
||||
|
||||
```toml
|
||||
[build.targets]
|
||||
targets = ["linux", "windows"]
|
||||
|
||||
[build.linux]
|
||||
compiler = "gcc"
|
||||
cflags = ["-O2"]
|
||||
|
||||
[build.windows]
|
||||
compiler = "x86_64-w64-mingw32-gcc"
|
||||
```
|
||||
hello/
|
||||
├── dcr.toml # project config
|
||||
└── src/
|
||||
└── main.c
|
||||
```
|
||||
|
||||
See [Cross-compilation guide](docs/dependencies-and-build/cross-compilation.md) for details.
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
The main project file is `dcr.toml`.
|
||||
For multi-registry management, use `~/.dcr/config.toml`.
|
||||
|
||||
Example `dcr.toml`:
|
||||
## dcr.toml Example
|
||||
|
||||
```toml
|
||||
[package]
|
||||
name = "hello"
|
||||
version = "0.1.0"
|
||||
type = "app" # or "lib", "none"
|
||||
|
||||
[build]
|
||||
language = "c"
|
||||
standard = "c11"
|
||||
compiler = "clang"
|
||||
kind = "bin" # "bin", "staticlib", "sharedlib", "efi", "elf"
|
||||
# Optional platform hint
|
||||
# platform = "x86_64"
|
||||
# Optional custom flags
|
||||
cflags = ["-Wall", "-Wextra"]
|
||||
ldflags = ["-lm"]
|
||||
#
|
||||
# Optional include/exclude:
|
||||
# exclude = ["src/vendor", "src/legacy/**"]
|
||||
# include = ["src/boot/arch/**"]
|
||||
|
||||
[toolchain]
|
||||
# Optional tool overrides:
|
||||
# cc = "clang"
|
||||
# cxx = "clang++"
|
||||
# as = "as"
|
||||
# ar = "ar"
|
||||
# ld = "ld"
|
||||
# uic = "uic"
|
||||
# moc = "moc"
|
||||
# rcc = "rcc"
|
||||
kind = "bin"
|
||||
|
||||
[dependencies]
|
||||
my_lib = "path:./libs/my_lib"
|
||||
cool_lib = { git = "https://github.com/user/cool_lib", tag = "v1.0" }
|
||||
```
|
||||
|
||||
Workspace example:
|
||||
...
|
||||
[workspace]
|
||||
kernel = { path = "src/kernel", deps = ["core", "userspace"] }
|
||||
core = { path = "src/core", deps = ["userspace"] }
|
||||
userspace = { path = "src/userspace" }
|
||||
---
|
||||
|
||||
## Base Commands
|
||||
|
||||
| Command | Description |
|
||||
|---|---|
|
||||
| `dcr new <name>` | Create a new project |
|
||||
| `dcr init` | Initialize current directory |
|
||||
| `dcr build [--release]` | Build the project |
|
||||
| `dcr run [--release]` | Build and run |
|
||||
| `dcr clean` | Remove build artifacts |
|
||||
|
||||
other in [docs/](docs/) and `dcr --help`
|
||||
|
||||
---
|
||||
|
||||
## Platforms
|
||||
|
||||
<div align="center">
|
||||
|
||||
| OS | x86_64 | aarch64 | armv7 | i686 | riscv64 | GNU | Musl | MSVC | MinGW |
|
||||
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| Linux | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ |
|
||||
| macOS | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||
| Windows | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ |
|
||||
| FreeBSD | ⚠️ | ⚠️ | ❌ | ❌ | ❌ | ⚠️ | ❌ | ❌ | ❌ |
|
||||
| OpenBSD | ⚠️ | ⚠️ | ❌ | ❌ | ❌ | ⚠️ | ❌ | ❌ | ❌ |
|
||||
| NetBSD | ⚠️ | ⚠️ | ❌ | ❌ | ❌ | ⚠️ | ❌ | ❌ | ❌ |
|
||||
|
||||
✅ officially supported · ⚠️ community / best-effort · ❌ not supported
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## Documentation
|
||||
|
||||
Full docs at **[dcr.dexoron.su](https://dcr.dexoron.su)** or in the [`docs/`](docs/) directory.
|
||||
|
||||
---
|
||||
|
||||
## Contributors
|
||||
|
||||
<div align="center">
|
||||
|
||||
| | Name | Role | GitHub |
|
||||
|:---:|---|---|---|
|
||||
| 👤 | Dexoron (Bezotechestvo Vladimir) | Maintainer, Creator | [@dexoron](https://github.com/dexoron) |
|
||||
| 👤 | Kai | Maintainer | [@peoplemiau1](https://github.com/peoplemiau1) |
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md). Before a PR:
|
||||
|
||||
```sh
|
||||
cargo fmt --all
|
||||
cargo clippy --all-targets --all-features -- -D warnings
|
||||
cargo test
|
||||
```
|
||||
|
||||
Git and Path dependencies are supported. DCR will resolve them on build and generate `dcr.lock`.
|
||||
---
|
||||
|
||||
Incremental build note: object files are rebuilt when source `.c/.cpp` or included header files change.
|
||||
<div align="center">
|
||||
|
||||
GPL-3.0 — see [LICENSE](LICENSE)<br/>
|
||||
Made with ❤️ by [Dexoron](https://github.com/dexoron) and contributors.
|
||||
|
||||
## Requirements
|
||||
- Rust toolchain (`rustc`, `cargo`) - for building DCR from source
|
||||
- C compiler (`clang`, `gcc`, or 'cl'(msvc))
|
||||
|
||||
## Releases
|
||||
Releases are built automatically via GitHub Actions (`.github/workflows/release.yml`) when a tag matching `v*` is pushed.
|
||||
|
||||
## Authors
|
||||
|
||||
- **Dexoron (Bezotechestvo Vladimir)** — main@dexoron.su — [github.com/dexoron](https://github.com/dexoron)
|
||||
- **Kiedie** — [github.com/peoplemiau1](https://github.com/peoplemiau1)
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the **GNU General Public License v3.0**. See `LICENSE`.
|
||||
</div>
|
||||
@@ -31,7 +31,9 @@ kind = "bin"
|
||||
|
||||
- `language` (string or array, required): `c`, `c++`, `cpp`, `cxx`, or `asm`.
|
||||
Mixed languages can be specified as an array, for example `["c", "c++", "asm"]`.
|
||||
- `standard` (string, required): language standard passed to compiler.
|
||||
- `standard` (string, required): C language standard passed to compiler (e.g. `c11`, `c17`, `gnu11`).
|
||||
- `cxx_standard` (string, optional): C++ language standard passed to compiler for `.cpp`/`.cxx`/`.cc` files
|
||||
(e.g. `c++17`, `gnu++17`, `c++20`). When set, overrides `standard` for C++ sources.
|
||||
- `compiler` (string, required): compiler command (for example `clang`, `gcc`, `cl`).
|
||||
- `kind` (string, required): `bin`, `staticlib`, `sharedlib`, `efi`, or `elf`.
|
||||
- `filename` (string, optional): custom output filename (without extension). Overrides `package.name` for the final artifact.
|
||||
@@ -42,7 +44,9 @@ kind = "bin"
|
||||
When set, DCR automatically injects `--target=<value>` into `cflags` (useful for clang cross-compilation).
|
||||
- `platform` (string, optional): architecture hint for compiler (used for `-march` or `/arch`).
|
||||
- `cflags` (string array, optional): extra compile flags.
|
||||
Supports variable substitution — see [Variable substitution](#variable-substitution) below.
|
||||
- `ldflags` (string array, optional): extra link flags.
|
||||
Supports variable substitution — see [Variable substitution](#variable-substitution) below.
|
||||
- `ldscript` (string, optional): linker script path. Passed as `-T <path>` to the linker. Useful for bare-metal/embedded/freestanding targets.
|
||||
- `exclude` (string array, optional): paths or glob patterns to skip during source/header collection.
|
||||
- `include` (string array, optional): allowlist paths or globs that override `exclude`.
|
||||
@@ -60,9 +64,30 @@ Notes for `exclude`/`include`:
|
||||
- Use `include` to re-allow nested paths, for example exclude `src/boot` but allow `src/boot/arch/**`.
|
||||
When using glob patterns in `exclude`, DCR converts them internally, so `exclude` globs still work as expected.
|
||||
|
||||
### Build steps
|
||||
### Variable substitution
|
||||
|
||||
Build steps run shell commands before compilation. Post steps run after linking.
|
||||
`cflags`, `ldflags`, and `include` directory entries support the same `{placeholder}` variables
|
||||
as build/post steps:
|
||||
|
||||
| Variable | Description |
|
||||
|---|---|
|
||||
| `{profile}` | Build profile (`debug` / `release`) |
|
||||
| `{name}` | Package name |
|
||||
| `{version}` | Full package version (e.g. `0.2.1`) |
|
||||
| `{version_major}` | Major version number |
|
||||
| `{version_minor}` | Minor version number |
|
||||
| `{version_patch}` | Patch version number |
|
||||
| `{version_suffix}` | Version suffix (e.g. `-rc1`) |
|
||||
| `{version_suffix_dash}` | Suffix with leading dash (or empty) |
|
||||
|
||||
Example:
|
||||
|
||||
```toml
|
||||
[build]
|
||||
cflags = ['-DPROJECT_VERSION="{version}"', '-DVERSION_MAJOR={version_major}']
|
||||
```
|
||||
|
||||
## Build steps
|
||||
|
||||
```toml
|
||||
[[build.steps]]
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
name = "my-app"
|
||||
version = "0.1.0"
|
||||
type = "app"
|
||||
description = "My application"
|
||||
author = "John Doe"
|
||||
license = "MIT"
|
||||
```
|
||||
|
||||
## Fields
|
||||
@@ -14,6 +17,15 @@ type = "app"
|
||||
- `name` (string, required): used as output artifact base name.
|
||||
- `version` (string, required): used in `dcr.lock` project entry.
|
||||
- `type` (string, optional): project type (`app`, `lib`, `none`).
|
||||
- `description` (string, optional): human-readable project description.
|
||||
- `author` (string, optional): single author name.
|
||||
- `authors` (array of strings, optional): list of authors.
|
||||
- `license` (string, optional): project license identifier (e.g. `MIT`, `GPL-3.0-or-later`).
|
||||
- `homepage` (string, optional): project homepage URL.
|
||||
- `repository` (string, optional): source repository URL.
|
||||
- `readme` (string, optional): path to README file.
|
||||
- `keywords` (array of strings, optional): project keywords.
|
||||
- `categories` (array of strings, optional): project categories.
|
||||
|
||||
## Notes
|
||||
|
||||
|
||||
@@ -6,48 +6,65 @@
|
||||
[package]
|
||||
name = "string" # required
|
||||
version = "string" # required
|
||||
type = "string" # optional: "app", "lib", "none"
|
||||
description = "string" # optional
|
||||
author = "string" # optional
|
||||
authors = ["string", "..."] # optional
|
||||
license = "string" # optional
|
||||
homepage = "string" # optional
|
||||
repository = "string" # optional
|
||||
readme = "string" # optional
|
||||
keywords = ["string", "..."] # optional
|
||||
categories = ["string", "..."] # optional
|
||||
|
||||
[build]
|
||||
language = "c|c++|cpp|cxx|asm" # required (string or array)
|
||||
standard = "string" # required
|
||||
compiler = "string" # required
|
||||
kind = "bin|staticlib|sharedlib" # required
|
||||
filename = "string" # optional (custom output name without extension)
|
||||
extension = "string" # optional (custom extension without dot, e.g. "BIN")
|
||||
target = "string" # optional
|
||||
platform = "string" # optional
|
||||
cflags = ["string", "..."] # optional
|
||||
ldflags = ["string", "..."] # optional
|
||||
ldscript = "string" # optional (linker script, passed as -T<path>)
|
||||
exclude = ["string", "..."] # optional
|
||||
include = ["string", "..."] # optional
|
||||
roots = ["string", "..."] # optional
|
||||
src_disable = false # optional
|
||||
pkg_config = ["string", "..."] # optional
|
||||
generated = ["string", "..."] # optional
|
||||
expect = ["string", "..."] # optional
|
||||
clean = ["string", "..."] # optional
|
||||
standard = "string" # required (e.g. "c11", "gnu11")
|
||||
cxx_standard = "string" # optional (e.g. "c++17", "gnu++17")
|
||||
compiler = "string" # required (e.g. "clang", "gcc")
|
||||
kind = "bin|staticlib|sharedlib|efi|elf" # required
|
||||
target = "string" # optional (cross-compilation target triple)
|
||||
platform = "string" # optional (architecture hint, -march)
|
||||
type = "string" # optional
|
||||
cflags = ["string", "..."] # optional (supports {version} etc.)
|
||||
ldflags = ["string", "..."] # optional (supports {version} etc.)
|
||||
ldscript = "string" # optional (linker script path)
|
||||
exclude = ["string", "..."] # optional (glob patterns)
|
||||
include = ["string", "..."] # optional (allowlist + -I dirs)
|
||||
roots = ["string", "..."] # optional (source roots)
|
||||
src_disable = false # optional (disable default src/)
|
||||
pkg_config = ["string", "..."] # optional (pkg-config packages)
|
||||
generated = ["string", "..."] # optional (generated file patterns)
|
||||
expect = ["string", "..."] # optional (expected artifact patterns)
|
||||
clean = ["string", "..."] # optional (extra clean paths)
|
||||
targets = ["string", "..."] # optional (build targets)
|
||||
filename = "string" # optional (custom output name)
|
||||
extension = "string" # optional (custom extension)
|
||||
inherit = true # optional (inherit base settings)
|
||||
|
||||
[[build.steps]]
|
||||
name = "string"
|
||||
in = "glob"
|
||||
out = "path or glob"
|
||||
cmd = "string"
|
||||
name = "string" # required
|
||||
in = "glob" # required
|
||||
out = "path" # required
|
||||
cmd = "string" # required
|
||||
|
||||
# Inline steps (equivalent to [[build.steps]])
|
||||
steps = [
|
||||
{ name = "string", in = "glob", out = "path", cmd = "string" },
|
||||
]
|
||||
|
||||
[[build.post_steps]]
|
||||
name = "string"
|
||||
in = "glob"
|
||||
out = "path or glob"
|
||||
cmd = "string"
|
||||
name = "string" # required
|
||||
in = "glob" # required (or "target/...")
|
||||
out = "path" # required
|
||||
cmd = "string" # required
|
||||
|
||||
# Inline array form (equivalent to [[build.steps]] / [[build.post_steps]])
|
||||
steps = [
|
||||
{ name = "string", in = "glob", out = "path or glob", cmd = "string" },
|
||||
]
|
||||
# Inline post_steps (equivalent to [[build.post_steps]])
|
||||
post_steps = [
|
||||
{ name = "string", in = "glob", out = "path or glob", cmd = "string" },
|
||||
{ name = "string", in = "glob", out = "path", cmd = "string" },
|
||||
]
|
||||
|
||||
# Profile overrides (inherit all from [build])
|
||||
[build.debug]
|
||||
cflags = ["string", "..."] # optional
|
||||
ldflags = ["string", "..."] # optional
|
||||
@@ -56,36 +73,62 @@ ldflags = ["string", "..."] # optional
|
||||
cflags = ["string", "..."] # optional
|
||||
ldflags = ["string", "..."] # optional
|
||||
|
||||
# Target-specific overrides
|
||||
[build.linux]
|
||||
compiler = "string"
|
||||
cflags = ["string", "..."]
|
||||
|
||||
[build.windows]
|
||||
compiler = "string"
|
||||
|
||||
# Target + profile override
|
||||
[build.linux.release]
|
||||
cflags = ["string", "..."]
|
||||
|
||||
[toolchain]
|
||||
cc = "string" # optional
|
||||
cxx = "string" # optional
|
||||
as = "string" # optional
|
||||
ar = "string" # optional
|
||||
ld = "string" # optional
|
||||
uic = "string" # optional
|
||||
moc = "string" # optional
|
||||
rcc = "string" # optional
|
||||
cc = "string" # optional (C compiler override)
|
||||
cxx = "string" # optional (C++ compiler override)
|
||||
as = "string" # optional (assembler override)
|
||||
ar = "string" # optional (archiver override)
|
||||
ld = "string" # optional (linker override)
|
||||
uic = "string" # optional (Qt uic)
|
||||
moc = "string" # optional (Qt moc)
|
||||
rcc = "string" # optional (Qt rcc)
|
||||
|
||||
[run]
|
||||
cmd = "string" # optional
|
||||
cmd = "string" # required for `dcr run`
|
||||
|
||||
# Debug run variant
|
||||
[run.debug]
|
||||
cmd = "string"
|
||||
|
||||
[workspace]
|
||||
name = { path = "string", deps = ["string", "..."] }
|
||||
[workspace.member_name]
|
||||
path = "string" # required
|
||||
deps = ["string", "..."] # optional
|
||||
|
||||
[dependencies]
|
||||
name = {
|
||||
path = "string", # required for each dependency
|
||||
include = ["string", "..."], # optional
|
||||
lib = ["string", "..."], # optional
|
||||
libs = ["string", "..."], # optional
|
||||
}
|
||||
[dependencies.dep_name]
|
||||
path = "string" # local path
|
||||
# or
|
||||
version = "string" # registry version
|
||||
# or
|
||||
git = "string" # git URL
|
||||
branch = "string" # optional (git branch)
|
||||
tag = "string" # optional (git tag)
|
||||
rev = "string" # optional (git commit)
|
||||
# common fields:
|
||||
include = ["string", "..."] # optional (include dirs)
|
||||
lib = ["string", "..."] # optional (lib dirs)
|
||||
libs = ["string", "..."] # optional (link libs)
|
||||
features = ["string", "..."] # optional (registry features)
|
||||
```
|
||||
|
||||
## Validation rules
|
||||
|
||||
- `[package]`, `[build]`, `[dependencies]` must exist.
|
||||
- Required string fields must be non-empty.
|
||||
- `build.kind` must be `bin`, `staticlib`, or `sharedlib`.
|
||||
- `build.kind` must be `bin`, `staticlib`, `sharedlib`, `efi`, or `elf`.
|
||||
- Dependency fields `include/lib/libs` must be string arrays when provided.
|
||||
- `build.exclude`/`build.include` must be string arrays when provided.
|
||||
- `build.roots` must be a string array when provided.
|
||||
|
||||
@@ -230,6 +230,12 @@ fn get_list_with_profile_and_target(
|
||||
// Custom from target/profile
|
||||
if let Some(val) = get_config_value_raw(config, "build", field, profile, target) {
|
||||
if let Some(_arr) = val.as_array() {
|
||||
let is_base = inherit
|
||||
&& config
|
||||
.get(&format!("build.{field}"))
|
||||
.map(|v| v == &val)
|
||||
.unwrap_or(false);
|
||||
if !is_base {
|
||||
let custom = parse_string_array(&val, &format!("build.{field}"))?;
|
||||
if inherit {
|
||||
out.extend(custom);
|
||||
@@ -237,6 +243,7 @@ fn get_list_with_profile_and_target(
|
||||
out = custom;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if inherit {
|
||||
// Legacy append
|
||||
if let Some(table) = profile_table(config, profile)
|
||||
@@ -513,6 +520,8 @@ fn build_project_at(
|
||||
let build_language = get_language_with_profile(&config, profile)?;
|
||||
let build_standard =
|
||||
get_string_with_profile_and_target(&config, "standard", profile, build_target);
|
||||
let build_cxx_standard =
|
||||
get_string_with_profile_and_target(&config, "cxx_standard", profile, build_target);
|
||||
let build_kind = get_string_with_profile_and_target(&config, "kind", profile, build_target);
|
||||
let build_platform =
|
||||
get_string_with_profile_and_target(&config, "platform", profile, build_target);
|
||||
@@ -716,12 +725,29 @@ fn build_project_at(
|
||||
}
|
||||
|
||||
let target_dir_binding = normalize_target(build_target.unwrap_or(""), profile);
|
||||
let version_info = parse_version_info(&project_version);
|
||||
let substitute = |s: &str| -> String {
|
||||
s.replace("{profile}", profile)
|
||||
.replace("{name}", &project_name)
|
||||
.replace("{version}", &version_info.full)
|
||||
.replace("{version_major}", &version_info.major)
|
||||
.replace("{version_minor}", &version_info.minor)
|
||||
.replace("{version_patch}", &version_info.patch)
|
||||
.replace("{version_suffix}", &version_info.suffix)
|
||||
.replace("{version_suffix_dash}", &version_info.suffix_dash)
|
||||
};
|
||||
let resolved_cflags: Vec<String> = resolved_cflags.iter().map(|f| substitute(f)).collect();
|
||||
let modified_ldflags: Vec<String> =
|
||||
resolved_ldflags.iter().map(|f| substitute(f)).collect();
|
||||
let merged_include_dirs: Vec<String> =
|
||||
merged_include_dirs.iter().map(|f| substitute(f)).collect();
|
||||
let ctx = BuildContext {
|
||||
profile,
|
||||
project_name: &project_name,
|
||||
compiler: &resolved_compiler,
|
||||
language: &build_language,
|
||||
standard: &build_standard,
|
||||
cxx_standard: &build_cxx_standard,
|
||||
target: build_target,
|
||||
target_dir: target_dir_binding.as_deref(),
|
||||
kind: normalize_kind(&build_kind),
|
||||
@@ -740,7 +766,7 @@ fn build_project_at(
|
||||
lib_dirs: &resolved.lib_dirs,
|
||||
libs: &resolved.libs,
|
||||
cflags: &resolved_cflags,
|
||||
ldflags: &resolved_ldflags,
|
||||
ldflags: &modified_ldflags,
|
||||
output_filename: if output_filename.is_empty() {
|
||||
None
|
||||
} else {
|
||||
@@ -763,7 +789,6 @@ fn build_project_at(
|
||||
let tool_execs = resolve_toolchain_execs(&tc_uic, &tc_moc, &tc_rcc, &pkg_configs);
|
||||
let step_flags =
|
||||
build_step_flags(&resolved_cflags, &resolved.include_dirs, &resolved_compiler);
|
||||
let version_info = parse_version_info(&project_version);
|
||||
let step_vars = StepVars {
|
||||
profile,
|
||||
version: &version_info.full,
|
||||
|
||||
@@ -171,6 +171,7 @@ pub struct ProjectInfo {
|
||||
pub profile: String,
|
||||
pub language: String,
|
||||
pub standard: String,
|
||||
pub cxx_standard: String,
|
||||
pub compiler: String,
|
||||
pub kind: String,
|
||||
pub sources: Vec<String>,
|
||||
@@ -228,6 +229,7 @@ fn collect_project_info_inner(root: &Path, profile: &str) -> Result<ProjectInfo,
|
||||
let version = get_config_str(&config, "package.version");
|
||||
let language = get_language_with_profile_or_default(&config, profile);
|
||||
let standard = get_string_with_profile(&config, "standard", profile);
|
||||
let cxx_standard = get_string_with_profile(&config, "cxx_standard", profile);
|
||||
let compiler_s = get_string_with_profile(&config, "compiler", profile);
|
||||
let kind = get_string_with_profile(&config, "kind", profile);
|
||||
let build_target = get_string_with_profile(&config, "target", profile);
|
||||
@@ -339,6 +341,7 @@ fn collect_project_info_inner(root: &Path, profile: &str) -> Result<ProjectInfo,
|
||||
compiler: &resolved_compiler,
|
||||
language: &language,
|
||||
standard: &standard,
|
||||
cxx_standard: &cxx_standard,
|
||||
target: Some(build_target.as_str()),
|
||||
target_dir: target_dir_binding.as_deref(),
|
||||
kind: normalize_kind(&kind),
|
||||
@@ -381,6 +384,7 @@ fn collect_project_info_inner(root: &Path, profile: &str) -> Result<ProjectInfo,
|
||||
profile: profile.to_string(),
|
||||
language,
|
||||
standard,
|
||||
cxx_standard,
|
||||
compiler: resolved_compiler,
|
||||
kind: normalize_kind(&kind).to_string(),
|
||||
sources: abs_sources,
|
||||
@@ -471,6 +475,10 @@ fn project_info_to_json(info: &ProjectInfo) -> String {
|
||||
" \"standard\": {},\n",
|
||||
json_str(&info.standard)
|
||||
));
|
||||
out.push_str(&format!(
|
||||
" \"cxx_standard\": {},\n",
|
||||
json_str(&info.cxx_standard)
|
||||
));
|
||||
out.push_str(&format!(
|
||||
" \"compiler\": {},\n",
|
||||
json_str(&info.compiler)
|
||||
@@ -575,6 +583,13 @@ fn build_compile_command(info: &ProjectInfo, source: &str, profile: &str) -> Vec
|
||||
};
|
||||
cmd.push(compiler.to_string());
|
||||
cmd.push("-c".to_string());
|
||||
|
||||
// ASM x flag — must be before source file
|
||||
if let Some(flag) = asm_lang_flag(source) {
|
||||
cmd.push("-x".to_string());
|
||||
cmd.push(flag.to_string());
|
||||
}
|
||||
|
||||
cmd.push(source.to_string());
|
||||
|
||||
// Object path (for -o, approximate — not critical for IntelliSense)
|
||||
@@ -595,16 +610,19 @@ fn build_compile_command(info: &ProjectInfo, source: &str, profile: &str) -> Vec
|
||||
cmd.push("-fPIC".to_string());
|
||||
}
|
||||
|
||||
// ASM x flag
|
||||
if let Some(flag) = asm_lang_flag(source) {
|
||||
cmd.push("-x".to_string());
|
||||
cmd.push(flag.to_string());
|
||||
}
|
||||
|
||||
// -std=
|
||||
if !info.standard.is_empty() && info.language.to_lowercase() != "asm" {
|
||||
let ext = Path::new(source)
|
||||
.extension()
|
||||
.and_then(|e| e.to_str())
|
||||
.unwrap_or("");
|
||||
let is_cpp = matches!(ext, "cpp" | "cxx" | "cc");
|
||||
if is_cpp && !info.cxx_standard.is_empty() {
|
||||
cmd.push(format!("-std={}", info.cxx_standard));
|
||||
} else if !is_cpp {
|
||||
cmd.push(format!("-std={}", info.standard));
|
||||
}
|
||||
}
|
||||
|
||||
// Default profile flags (mirrors unix_cc.rs defaults)
|
||||
match profile {
|
||||
|
||||
@@ -262,7 +262,22 @@ pub fn object_path(obj_dir: &Path, source: &str, obj_ext: &str) -> String {
|
||||
stripped.to_path_buf()
|
||||
};
|
||||
let mut out = obj_dir.join(rel);
|
||||
out.set_extension(obj_ext.trim_start_matches('.'));
|
||||
let ext = out
|
||||
.extension()
|
||||
.and_then(|e| e.to_str())
|
||||
.unwrap_or("")
|
||||
.to_string();
|
||||
let obj_ext = obj_ext.trim_start_matches('.');
|
||||
if !ext.is_empty() {
|
||||
let stem = out
|
||||
.file_stem()
|
||||
.and_then(|s| s.to_str())
|
||||
.unwrap_or("")
|
||||
.to_string();
|
||||
out.set_file_name(format!("{stem}.{ext}.{obj_ext}"));
|
||||
} else {
|
||||
out.set_extension(obj_ext);
|
||||
}
|
||||
out.to_string_lossy().to_string()
|
||||
}
|
||||
|
||||
@@ -379,28 +394,28 @@ mod tests {
|
||||
fn object_path_basic() {
|
||||
let obj_dir = Path::new("target/debug/obj");
|
||||
let result = object_path(obj_dir, "./src/main.c", "o");
|
||||
assert_eq!(result, "target/debug/obj/main.o");
|
||||
assert_eq!(result, "target/debug/obj/main.c.o");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn object_path_nested() {
|
||||
let obj_dir = Path::new("target/debug/obj");
|
||||
let result = object_path(obj_dir, "./src/core/utils.c", "o");
|
||||
assert_eq!(result, "target/debug/obj/core/utils.o");
|
||||
assert_eq!(result, "target/debug/obj/core/utils.c.o");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn object_path_no_prefix() {
|
||||
let obj_dir = Path::new("target/debug/obj");
|
||||
let result = object_path(obj_dir, "src/main.c", "o");
|
||||
assert_eq!(result, "target/debug/obj/main.o");
|
||||
assert_eq!(result, "target/debug/obj/main.c.o");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn object_path_msvc_ext() {
|
||||
let obj_dir = Path::new("target/debug/obj");
|
||||
let result = object_path(obj_dir, "./src/main.c", "obj");
|
||||
assert_eq!(result, "target/debug/obj/main.obj");
|
||||
assert_eq!(result, "target/debug/obj/main.c.obj");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -25,7 +25,7 @@ use std::time::Instant;
|
||||
|
||||
pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
||||
let lang = ctx.language.to_lowercase();
|
||||
if lang.split('+').any(|p| p.trim() != "asm") {
|
||||
if lang.split(',').any(|p| p.trim() != "asm") {
|
||||
return Err("GAS backend requires build.language = \"asm\"".to_string());
|
||||
}
|
||||
let assembler = if ctx.compiler.is_empty() {
|
||||
|
||||
@@ -27,7 +27,7 @@ pub struct BuildContext<'a> {
|
||||
pub compiler: &'a str,
|
||||
pub language: &'a str,
|
||||
pub standard: &'a str,
|
||||
#[allow(dead_code)]
|
||||
pub cxx_standard: &'a str,
|
||||
pub target: Option<&'a str>,
|
||||
pub target_dir: Option<&'a str>,
|
||||
pub kind: &'a str,
|
||||
|
||||
@@ -144,8 +144,8 @@ pub(crate) fn collect_sources(ctx: &BuildContext) -> Result<Vec<String>, String>
|
||||
|
||||
fn source_extensions(language: &str) -> Vec<&str> {
|
||||
let mut out = Vec::new();
|
||||
for part in language.split('+') {
|
||||
let lang = part.trim().to_lowercase();
|
||||
for part in language.split(',').map(|s| s.trim()) {
|
||||
let lang = part.to_lowercase();
|
||||
match lang.as_str() {
|
||||
"c" => out.extend(["c"]),
|
||||
"c++" | "cpp" | "cxx" => out.extend(["cpp", "cxx", "cc"]),
|
||||
@@ -243,18 +243,26 @@ fn build_object(
|
||||
let mut cmd = Command::new(compiler);
|
||||
cmd.arg("/nologo");
|
||||
|
||||
match ctx.language.to_lowercase().as_str() {
|
||||
"c" => {
|
||||
let ext = Path::new(source)
|
||||
.extension()
|
||||
.and_then(|e| e.to_str())
|
||||
.unwrap_or("");
|
||||
let is_cpp = matches!(ext, "cpp" | "cxx" | "cc");
|
||||
if is_cpp {
|
||||
cmd.arg("/TP");
|
||||
} else {
|
||||
cmd.arg("/TC");
|
||||
}
|
||||
"c++" | "cpp" | "cxx" => {
|
||||
cmd.arg("/TP");
|
||||
}
|
||||
_ => return Err("Unsupported language".to_string()),
|
||||
}
|
||||
|
||||
if !ctx.standard.is_empty() && ctx.language.to_lowercase() != "asm" {
|
||||
let std_flag = msvc_standard_flag(ctx.language, ctx.standard)?;
|
||||
let std_val = if is_cpp && !ctx.cxx_standard.is_empty() {
|
||||
ctx.cxx_standard
|
||||
} else if !is_cpp && !ctx.standard.is_empty() {
|
||||
ctx.standard
|
||||
} else {
|
||||
""
|
||||
};
|
||||
if !std_val.is_empty() {
|
||||
let std_flag = msvc_standard_flag(if is_cpp { "c++" } else { "c" }, std_val)?;
|
||||
cmd.arg(std_flag);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ use std::time::Instant;
|
||||
|
||||
pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
||||
let lang = ctx.language.to_lowercase();
|
||||
if lang.split('+').any(|p| p.trim() != "asm") {
|
||||
if lang.split(',').any(|p| p.trim() != "asm") {
|
||||
return Err("NASM backend requires build.language = \"asm\"".to_string());
|
||||
}
|
||||
let assembler = if ctx.compiler.is_empty() {
|
||||
|
||||
@@ -133,8 +133,8 @@ pub(crate) fn collect_sources(ctx: &BuildContext) -> Result<Vec<String>, String>
|
||||
|
||||
fn source_extensions(language: &str) -> Vec<&str> {
|
||||
let mut out = Vec::new();
|
||||
for part in language.split('+') {
|
||||
let lang = part.trim().to_lowercase();
|
||||
for part in language.split(',').map(|s| s.trim()) {
|
||||
let lang = part.to_lowercase();
|
||||
match lang.as_str() {
|
||||
"c" => out.extend(["c"]),
|
||||
"c++" | "cpp" | "cxx" => out.extend(["cpp", "cxx", "cc"]),
|
||||
@@ -210,16 +210,18 @@ fn build_object(
|
||||
}
|
||||
|
||||
let mut cmd = Command::new(compiler);
|
||||
cmd.arg("-c").arg(source).arg("-o").arg(obj_path);
|
||||
|
||||
if ctx.kind == "sharedlib" {
|
||||
cmd.arg("-fPIC");
|
||||
}
|
||||
cmd.arg("-c");
|
||||
|
||||
if let Some(flag) = asm_lang_flag(source) {
|
||||
cmd.arg("-x").arg(flag);
|
||||
}
|
||||
|
||||
cmd.arg(source).arg("-o").arg(obj_path);
|
||||
|
||||
if ctx.kind == "sharedlib" {
|
||||
cmd.arg("-fPIC");
|
||||
}
|
||||
|
||||
if let Some(platform) = ctx.platform
|
||||
&& !platform.trim().is_empty()
|
||||
{
|
||||
@@ -227,8 +229,17 @@ fn build_object(
|
||||
}
|
||||
|
||||
if !ctx.standard.is_empty() && ctx.language.to_lowercase() != "asm" {
|
||||
let ext = Path::new(source)
|
||||
.extension()
|
||||
.and_then(|e| e.to_str())
|
||||
.unwrap_or("");
|
||||
let is_cpp = matches!(ext, "cpp" | "cxx" | "cc");
|
||||
if is_cpp && !ctx.cxx_standard.is_empty() {
|
||||
cmd.arg(format!("-std={}", ctx.cxx_standard));
|
||||
} else if !is_cpp {
|
||||
cmd.arg(format!("-std={}", ctx.standard));
|
||||
}
|
||||
}
|
||||
|
||||
let use_dcr_defaults = ctx.cflags.is_empty() && !is_bare_metal_target(ctx.target);
|
||||
if use_dcr_defaults {
|
||||
|
||||
@@ -100,6 +100,8 @@ pub struct PackageConfig {
|
||||
pub struct BuildConfig {
|
||||
pub language: LanguageConfig,
|
||||
pub standard: String,
|
||||
#[serde(default)]
|
||||
pub cxx_standard: Option<String>,
|
||||
pub compiler: String,
|
||||
#[serde(default)]
|
||||
pub kind: Option<String>,
|
||||
|
||||
@@ -190,7 +190,7 @@ pub fn parse_language_value(value: &toml::Value, key: &str) -> Result<String, St
|
||||
if parts.is_empty() {
|
||||
return Err(format!("{key} is empty"));
|
||||
}
|
||||
Ok(parts.join("+"))
|
||||
Ok(parts.join(","))
|
||||
}
|
||||
|
||||
pub fn resolve_compiler(
|
||||
@@ -258,7 +258,7 @@ fn toolchain_override_compiler(
|
||||
|
||||
pub fn primary_language(language: &str) -> String {
|
||||
let parts: Vec<String> = language
|
||||
.split('+')
|
||||
.split(',')
|
||||
.map(|p| p.trim().to_lowercase())
|
||||
.filter(|p| !p.is_empty())
|
||||
.collect();
|
||||
|
||||
Reference in New Issue
Block a user