mirror of
https://github.com/dexoron/dcr.git
synced 2026-10-21 19:49:36 +00:00
Merge branch 'master' of https://github.com/dexoron/dcr into dev
This commit is contained in:
300
.github/workflows/release.yml
vendored
300
.github/workflows/release.yml
vendored
@@ -7,32 +7,65 @@ on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: "Tag to build and upload assets for (e.g. v0.6.5)"
|
||||
description: "Tag to build (e.g. v0.6.7)"
|
||||
required: true
|
||||
run_linux:
|
||||
description: "Build Linux"
|
||||
type: boolean
|
||||
default: true
|
||||
run_macos:
|
||||
description: "Build macOS"
|
||||
type: boolean
|
||||
default: true
|
||||
run_windows:
|
||||
description: "Build Windows"
|
||||
type: boolean
|
||||
default: true
|
||||
run_publish:
|
||||
description: "Publish to crates.io"
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
# ── Resolve Tag ────────────────────────────────────────────────────────────
|
||||
setup:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
tag: ${{ steps.tag.outputs.value }}
|
||||
version: ${{ steps.tag.outputs.version }}
|
||||
prerelease: ${{ steps.tag.outputs.prerelease }}
|
||||
steps:
|
||||
- name: Resolve tag
|
||||
id: tag
|
||||
run: |
|
||||
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
||||
TAG="${{ github.event.inputs.tag }}"
|
||||
else
|
||||
TAG="${GITHUB_REF_NAME}"
|
||||
fi
|
||||
VERSION="${TAG#v}"
|
||||
[[ "$TAG" == *"-"* ]] && PRERELEASE="true" || PRERELEASE="false"
|
||||
echo "value=${TAG}" >> "$GITHUB_OUTPUT"
|
||||
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||
echo "prerelease=${PRERELEASE}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
# ── Release Notes ──────────────────────────────────────────────────────────
|
||||
notes:
|
||||
needs: setup
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
# При ручном запуске чекаутим нужный тег
|
||||
ref: ${{ github.event.inputs.tag || github.ref }}
|
||||
ref: ${{ needs.setup.outputs.tag }}
|
||||
|
||||
- name: Prepare Release Notes
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# При ручном запуске берём версию из инпута, иначе из тега
|
||||
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
||||
tag="${{ github.event.inputs.tag }}"
|
||||
tag="${tag#v}"
|
||||
else
|
||||
tag="${GITHUB_REF_NAME#v}"
|
||||
fi
|
||||
tag="${{ needs.setup.outputs.version }}"
|
||||
notes_file=".github/release-notes.md"
|
||||
if grep -q "^## \\[${tag}\\]" CHANGELOG.md; then
|
||||
awk -v ver="$tag" '
|
||||
@@ -51,60 +84,23 @@ jobs:
|
||||
name: release-notes
|
||||
path: .github/release-notes.md
|
||||
|
||||
build:
|
||||
# ── Linux ──────────────────────────────────────────────────────────────────
|
||||
build-linux:
|
||||
if: ${{ github.event_name == 'push' || github.event.inputs.run_linux == 'true' }}
|
||||
needs: [setup, notes]
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
# ── Linux GNU ──────────────────────────────────────────────────────
|
||||
- os: ubuntu-latest
|
||||
target: x86_64-unknown-linux-gnu
|
||||
|
||||
- os: ubuntu-latest
|
||||
target: aarch64-unknown-linux-gnu
|
||||
apt: gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
|
||||
env_cc: CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc
|
||||
env_linker: CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc
|
||||
|
||||
# ── Linux Musl ─────────────────────────────────────────────────────
|
||||
- os: ubuntu-latest
|
||||
target: x86_64-unknown-linux-musl
|
||||
apt: musl-tools
|
||||
env_cc: CC_x86_64_unknown_linux_musl=musl-gcc
|
||||
env_linker: CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER=musl-gcc
|
||||
|
||||
- os: ubuntu-latest
|
||||
target: aarch64-unknown-linux-musl
|
||||
musl_cross: aarch64-linux-musl-cross
|
||||
env_cc: CC_aarch64_unknown_linux_musl=aarch64-linux-musl-gcc
|
||||
env_linker: CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=aarch64-linux-musl-gcc
|
||||
|
||||
# ── macOS ──────────────────────────────────────────────────────────
|
||||
- os: macos-15-intel
|
||||
target: x86_64-apple-darwin
|
||||
|
||||
- os: macos-latest
|
||||
target: aarch64-apple-darwin
|
||||
|
||||
# ── Windows MSVC ───────────────────────────────────────────────────
|
||||
- os: windows-latest
|
||||
target: x86_64-pc-windows-msvc
|
||||
|
||||
- os: windows-latest
|
||||
target: aarch64-pc-windows-msvc
|
||||
|
||||
# ── Windows GNU ────────────────────────────────────────────────────
|
||||
- os: windows-latest
|
||||
target: x86_64-pc-windows-gnu
|
||||
|
||||
# gnullvm исключён: LLVM MinGW не предустановлен на GitHub Actions
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: notes
|
||||
- target: x86_64-unknown-linux-gnu
|
||||
- target: aarch64-unknown-linux-gnu
|
||||
- target: x86_64-unknown-linux-musl
|
||||
- target: aarch64-unknown-linux-musl
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.inputs.tag || github.ref }}
|
||||
ref: ${{ needs.setup.outputs.tag }}
|
||||
|
||||
- name: Download Release Notes
|
||||
uses: actions/download-artifact@v4
|
||||
@@ -112,34 +108,10 @@ jobs:
|
||||
name: release-notes
|
||||
path: .github
|
||||
|
||||
- name: Install apt packages
|
||||
if: runner.os == 'Linux' && matrix.apt != ''
|
||||
- name: Install cargo-zigbuild
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu musl-tools
|
||||
case "${{ matrix.target }}" in
|
||||
aarch64-unknown-linux-gnu)
|
||||
echo "CC_aarch64-unknown-linux-gnu=aarch64-linux-gnu-gcc" >> $GITHUB_ENV
|
||||
echo "AR_aarch64-unknown-linux-gnu=aarch64-linux-gnu-ar" >> $GITHUB_ENV
|
||||
;;
|
||||
x86_64-unknown-linux-musl)
|
||||
echo "CC_x86_64-unknown-linux-musl=x86_64-linux-musl-gcc" >> $GITHUB_ENV
|
||||
;;
|
||||
aarch64-unknown-linux-musl)
|
||||
curl -L https://musl.cc/aarch64-linux-musl-cross.tgz -o /tmp/musl-aarch64.tgz
|
||||
sudo tar xzf /tmp/musl-aarch64.tgz -C /opt
|
||||
echo "/opt/aarch64-linux-musl-cross/bin" >> $GITHUB_PATH
|
||||
echo "CC_aarch64-unknown-linux-musl=aarch64-linux-musl-gcc" >> $GITHUB_ENV
|
||||
echo "AR_aarch64-unknown-linux-musl=aarch64-linux-musl-ar" >> $GITHUB_ENV
|
||||
;;
|
||||
esac
|
||||
|
||||
- name: Setup LLVM + MinGW (gnullvm)
|
||||
if: contains(matrix.target, 'gnullvm')
|
||||
shell: pwsh
|
||||
run: |
|
||||
choco install llvm mingw -y --no-progress
|
||||
echo "C:\Program Files\LLVM\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||
pip install cargo-zigbuild
|
||||
echo "$HOME/.local/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Setup Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
@@ -147,56 +119,132 @@ jobs:
|
||||
targets: ${{ matrix.target }}
|
||||
|
||||
- name: Build
|
||||
shell: bash
|
||||
env:
|
||||
MATRIX_ENV_CC: ${{ matrix.env_cc }}
|
||||
MATRIX_ENV_LINKER: ${{ matrix.env_linker }}
|
||||
run: |
|
||||
if [[ -n "${MATRIX_ENV_CC:-}" ]]; then
|
||||
export "${MATRIX_ENV_CC?}"
|
||||
fi
|
||||
if [[ -n "${MATRIX_ENV_LINKER:-}" ]]; then
|
||||
export "${MATRIX_ENV_LINKER?}"
|
||||
fi
|
||||
cargo build --release --target ${{ matrix.target }}
|
||||
|
||||
- name: Derive version string
|
||||
shell: bash
|
||||
run: |
|
||||
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
||||
RAW_TAG="${{ github.event.inputs.tag }}"
|
||||
if [ "${{ matrix.target }}" != "x86_64-unknown-linux-gnu" ]; then
|
||||
cargo zigbuild --release --target ${{ matrix.target }}
|
||||
else
|
||||
RAW_TAG="${GITHUB_REF_NAME}"
|
||||
cargo build --release --target ${{ matrix.target }}
|
||||
fi
|
||||
echo "RELEASE_TAG=${RAW_TAG}" >> "$GITHUB_ENV"
|
||||
echo "RELEASE_VERSION=${RAW_TAG#v}" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Derive asset name (Unix)
|
||||
if: runner.os != 'Windows'
|
||||
shell: bash
|
||||
- name: Prepare Artifact
|
||||
run: |
|
||||
echo "ASSET_NAME=dcr-${{ matrix.target }}-${RELEASE_VERSION}" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Derive asset name (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
shell: pwsh
|
||||
run: |
|
||||
"ASSET_NAME=dcr-${{ matrix.target }}-$($env:RELEASE_VERSION).exe" >> $env:GITHUB_ENV
|
||||
|
||||
- name: Prepare Artifact (Unix)
|
||||
if: runner.os != 'Windows'
|
||||
shell: bash
|
||||
run: cp target/${{ matrix.target }}/release/dcr "$ASSET_NAME"
|
||||
|
||||
- name: Prepare Artifact (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
shell: pwsh
|
||||
run: Copy-Item "target/${{ matrix.target }}/release/dcr.exe" "$env:ASSET_NAME"
|
||||
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: ${{ env.RELEASE_TAG }}
|
||||
tag_name: ${{ needs.setup.outputs.tag }}
|
||||
files: ${{ env.ASSET_NAME }}
|
||||
body_path: .github/release-notes.md
|
||||
prerelease: ${{ contains(env.RELEASE_TAG, '-') }}
|
||||
prerelease: ${{ needs.setup.outputs.prerelease == 'true' }}
|
||||
|
||||
# ── macOS ──────────────────────────────────────────────────────────────────
|
||||
build-macos:
|
||||
if: ${{ github.event_name == 'push' || github.event.inputs.run_macos == 'true' }}
|
||||
needs: [setup, notes]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: macos-15-intel
|
||||
target: x86_64-apple-darwin
|
||||
- os: macos-latest
|
||||
target: aarch64-apple-darwin
|
||||
runs-on: ${{ matrix.os }}
|
||||
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: Setup Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
|
||||
- name: Build
|
||||
run: cargo build --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' }}
|
||||
needs: [setup, notes]
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- target: x86_64-pc-windows-msvc
|
||||
- target: aarch64-pc-windows-msvc
|
||||
- target: x86_64-pc-windows-gnu
|
||||
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: Setup Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
|
||||
- name: Build
|
||||
run: cargo build --release --target ${{ matrix.target }}
|
||||
|
||||
- name: Prepare Artifact
|
||||
shell: pwsh
|
||||
run: |
|
||||
$asset = "dcr-${{ matrix.target }}-${{ needs.setup.outputs.version }}.exe"
|
||||
Copy-Item "target/${{ matrix.target }}/release/dcr.exe" "$asset"
|
||||
"ASSET_NAME=$asset" >> $env: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' }}
|
||||
|
||||
# ── crates.io ──────────────────────────────────────────────────────────────
|
||||
publish-crates:
|
||||
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]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ needs.setup.outputs.tag }}
|
||||
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Publish to crates.io
|
||||
run: cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -2,4 +2,6 @@ target/
|
||||
sandbox/**
|
||||
.idea/
|
||||
.vscode/
|
||||
.env
|
||||
.env
|
||||
|
||||
personal/
|
||||
77
AGENT.md
77
AGENT.md
@@ -1,77 +0,0 @@
|
||||
# AGENT.md
|
||||
|
||||
## Project Overview
|
||||
|
||||
**DCR (Dexoron Cargo Realization)** — CLI tool for managing C/C++/ASM projects in a Cargo-style.
|
||||
|
||||
- **Implementation Language**: Rust (edition 2024)
|
||||
- **License**: MIT
|
||||
- **Repository**: [github.com/dexoron/dcr](https://github.com/dexoron/dcr)
|
||||
|
||||
## Architecture
|
||||
|
||||
### Source Tree (`src/`)
|
||||
|
||||
```
|
||||
src/
|
||||
├── main.rs # Entry point, CLI command routing
|
||||
├── config.rs # Global constants
|
||||
├── cli/ # CLI command implementation
|
||||
│ ├── new.rs # dcr new <name>
|
||||
│ ├── init.rs # dcr init
|
||||
│ ├── setup.rs # dcr setup (registry initialization)
|
||||
│ ├── add.rs # dcr add <name>
|
||||
│ ├── build.rs # dcr build
|
||||
│ ├── run.rs # dcr run
|
||||
│ ├── clean.rs # dcr clean
|
||||
│ ├── help.rs # dcr --help
|
||||
│ └── flag_update.rs # dcr --update
|
||||
├── core/ # Business logic
|
||||
│ ├── config.rs # dcr.toml parsing
|
||||
│ ├── registry.rs # Package index management (RegistryManager)
|
||||
│ ├── deps/ # Dependency resolution (path, git)
|
||||
│ ├── runner.rs # Binary execution
|
||||
│ ├── workspace.rs # [workspace] support
|
||||
│ └── builder/ # Compiler backends (gcc, clang, msvc, nasm, gas)
|
||||
├── platform/ # Platform-specific logic
|
||||
└── utils/ # Helper utilities
|
||||
```
|
||||
|
||||
### Key Concepts
|
||||
|
||||
- **`~/.dcr/`**: Centralized storage for registries and configurations.
|
||||
- **`config.toml`**: File in `~/.dcr/` defining available package indices and their priorities.
|
||||
- **`dcr.toml`**: Local project configuration.
|
||||
- **Registry System**: `RegistryManager` handles package discovery across multiple indices based on priority.
|
||||
|
||||
## Supported Platforms
|
||||
|
||||
| Platform | Target triple |
|
||||
|---|---|
|
||||
| Linux | `x86_64-unknown-linux-gnu` |
|
||||
| macOS Intel | `x86_64-apple-darwin` |
|
||||
| macOS ARM | `aarch64-apple-darwin` |
|
||||
| Windows | `x86_64-pc-windows-msvc` |
|
||||
|
||||
## Dependencies (Cargo)
|
||||
|
||||
| Crate | Purpose |
|
||||
|---|---|
|
||||
| `reqwest` | HTTP requests |
|
||||
| `serde` | Serialization |
|
||||
| `toml` | Configuration parsing |
|
||||
| `git2` | Git dependency handling |
|
||||
|
||||
## Development
|
||||
|
||||
### Build & Run
|
||||
```bash
|
||||
cargo build --release
|
||||
./target/release/dcr setup
|
||||
```
|
||||
|
||||
## Code Conventions
|
||||
- Rust stable edition 2024.
|
||||
- `cargo fmt` and `cargo clippy -- -D warnings`.
|
||||
- CLI output in English.
|
||||
- Use `utils::log` for messaging.
|
||||
56
AGENTS.md
Normal file
56
AGENTS.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# AGENTS.md
|
||||
|
||||
## Quick start
|
||||
|
||||
```bash
|
||||
cargo build --release && ./target/release/dcr setup
|
||||
```
|
||||
|
||||
## Pre-PR (CI) gate
|
||||
|
||||
```bash
|
||||
cargo fmt --all --check
|
||||
cargo clippy --all-targets --all-features -- -D warnings
|
||||
cargo check --all-targets --all-features
|
||||
cargo test
|
||||
```
|
||||
|
||||
CI (`.github/workflows/ci.yml`) runs fmt, clippy, check — **not** tests.
|
||||
|
||||
## Tests
|
||||
|
||||
- Integration tests in `tests/` build the dcr binary and run it in temp dirs under `sandbox/cli-tests/` (gitignored).
|
||||
- Some tests **skip** if no C compiler (`gcc`/`clang`/`cc`) is found at runtime.
|
||||
- Compiler can be overridden via `DCR_COMPILER` or `DCR_CC` env vars.
|
||||
- Tests use `$CARGO_BIN_EXE_dcr` when available, else `target/debug/dcr`.
|
||||
|
||||
## Architecture
|
||||
|
||||
- **Single Rust package** (edition 2024), not a workspace. Only one binary: `dcr`.
|
||||
- **Entrypoint**: `src/main.rs` — matches `args[1]` against command names.
|
||||
- **Compiler backends** in `src/core/builder/`: `unix_cc` (gcc/clang), `msvc` (cl/clang-cl), `gas`, `nasm`. Dispatched by matching compiler name string.
|
||||
- **Config**: `dcr.toml` (per-project), `~/.dcr/config.toml` (registry), `~/.dcr/index.json` (index). Defaults: `language=c`, `standard=c11`, `compiler=clang`, `kind=bin`.
|
||||
- **`build.rs`** embeds `TARGET` as `DCR_TARGET` env var (shown in `--version`).
|
||||
- **CLI output** must be in English. Use `utils::log` for messaging.
|
||||
|
||||
## Key commands
|
||||
|
||||
| Command | Notes |
|
||||
|---|---|
|
||||
| `dcr new <name>` | Creates project dir |
|
||||
| `dcr init` | Init from CWD (must be empty) |
|
||||
| `dcr build [--debug\|--release] [--target <triple>]` | Default profile: `--debug` |
|
||||
| `dcr run [--debug\|--release]` | Only for `kind = "bin"` |
|
||||
| `dcr clean [--debug\|--release] [--all]` | `--all` for workspace roots |
|
||||
| `dcr tree` | Dependency tree viewer |
|
||||
| `dcr test [--init]` | Runs/test project tests |
|
||||
| `dcr gen <vscode\|clion\|compile-commands>` | IDE integration |
|
||||
| `dcr --update` | Self-update from GitHub Releases |
|
||||
|
||||
## Cross-compilation
|
||||
|
||||
`--target` accepts short names (`linux`, `macos`, `windows`) or full triples. In CI, Linux cross-builds use `cargo-zigbuild`. When `build.target` is set in `dcr.toml`, DCR auto-injects `--target=<value>` into cflags (clang). Bare-metal targets (containing `none`/`-elf`/`eabi`/`baremetal`) skip DCR's default compiler flags.
|
||||
|
||||
## Dependencies (Cargo)
|
||||
|
||||
reqwest (blocking+json+rustls-tls), serde, toml, git2, openssl (vendored), self-replace, sha2, ctrlc.
|
||||
47
CHANGELOG.md
47
CHANGELOG.md
@@ -1,5 +1,47 @@
|
||||
# Changelog
|
||||
|
||||
## [0.6.7] - 2026-05-26
|
||||
|
||||
Added:
|
||||
|
||||
- `build.kind = "efi"` — UEFI PE32+ executable support. Links with `-shared -nostdlib -Wl,-dll -Wl,--subsystem,10`, output via `efi_path()` with `.efi` extension, rejected by `dcr run`.
|
||||
- `build.kind = "elf"` — bare-metal ELF executable support. Uses `elf_path()` output path, rejected by `dcr run`, no extra linker flags.
|
||||
- `build.filename` and `build.extension` in `dcr.toml` — complete control over the final artifact name without relying on `package.name`.
|
||||
Example:
|
||||
```toml
|
||||
[build]
|
||||
filename = "KERNEL"
|
||||
extension = "EFI"
|
||||
```
|
||||
Produces `KERNEL.EFI` (works for `bin`, `staticlib`, and `sharedlib`).
|
||||
- Automatic injection of `--target=<build.target>` into compiler flags when `build.target` is set in `dcr.toml`. This greatly simplifies clang-based cross-compilation (especially for bare-metal targets like `aarch64-none-elf`).
|
||||
- Bare-metal targets (containing `none`, `-elf`, `eabi`, `baremetal`) no longer receive DCR's internal default flags (`-g`, `-Wall`, `-Wextra`, `-fno-omit-frame-pointer`, `-DDCR_DEBUG`, etc.). Prevents unwanted sections (`.comment`, debug info, etc.) that break custom linker scripts when `inherit = true`.
|
||||
- `build.ldscript` in `dcr.toml` — linker script path passed as `-T <path>` to the linker. Essential for bare-metal/embedded/freestanding targets.
|
||||
- `dcr build --verbose` — prints compiler/linker command lines (also works with `DCR_DEBUG` env var).
|
||||
- `dcr add <name>` without a source argument — if a registry is configured, DCR looks up the package by name and adds it as a version dependency.
|
||||
|
||||
Fixed:
|
||||
|
||||
- `build.target` declared in `dcr.toml` is now correctly used as the default target even when `--target` is not passed on the command line (previously the host target was always forced for config resolution).
|
||||
- `dcr run` with `--target` or `build.target` now correctly finds the binary in the arch-specific target directory.
|
||||
- `dcr run` checks profile-specific `build.{profile}.kind` before rejecting library builds (previously only checked `build.kind`).
|
||||
- `dcr clean` now reads `build.target` from `dcr.toml` when no `--target` flag is given.
|
||||
- Linux: target directory is now consistent — `target/<arch>-unknown-linux-gnu/<profile>/` (no stale `target/<profile>/`).
|
||||
- `dcr.lock` is now populated with all resolved dependencies (registry, path, git) instead of being always empty.
|
||||
- Git dependencies are now properly recognized and recorded in `dcr.lock`.
|
||||
- `is_registry_dep` correctly identifies version-based registry dependency tables (`{ version = "..." }`) and rejects tables with unknown keys.
|
||||
- `dcr test` accepts `--debug`/`--release` flags (defaults to `debug` profile instead of always `release`).
|
||||
- Build fingerprint cache (`build_cache_path`) now includes `target_dir` — different targets no longer share a single cache entry.
|
||||
- `object_path` no longer hardcodes the `src/` prefix — works correctly with custom `build.roots` (e.g. `roots = ["lib"]`).
|
||||
- `dcr update` now generates `.exe` candidates for all Windows targets, not just `x86_64-pc-windows-msvc`.
|
||||
- Compiler existence is verified before starting the build (clear error if compiler is not found in PATH).
|
||||
- Consolidated duplicate `OUTPUT_MUTEX` definitions — MSVC backend now uses the shared mutex from `common.rs`.
|
||||
- Registry cache root directory is created if missing (prevents cryptic errors when `~/.dcr/` does not exist).
|
||||
|
||||
Changed:
|
||||
|
||||
- `openssl` dependency is now conditional (`cfg(not(windows))`) — no longer pulled in on Windows, fixing Windows CI builds that lack Perl `Locale::Maketext::Simple`. On Linux (including musl cross-compilation) the vendored OpenSSL build is unchanged.
|
||||
|
||||
## [0.6.6] - 2026-05-18
|
||||
|
||||
Fixed:
|
||||
@@ -29,8 +71,9 @@ between `cli/build.rs`, `cli/run.rs`, `cli/clean.rs`, `cli/gen.rs`
|
||||
`package_root_from_registry_info`, `registry_include_dir`, `registry_lib_dir`,
|
||||
`path_from_string_dep`
|
||||
- Unit tests for `register.rs` (`is_registry_dep`, `package_root_from_registry_info`),
|
||||
`deps/mod.rs` (`path_dep_path`, `push_default_lib_dirs`),
|
||||
`utils/build.rs` (`normalize_target_os`, `parse_version_info`)
|
||||
`deps/mod.rs` (`path_dep_path`, `push_default_lib_dirs`),
|
||||
`utils/build.rs` (`normalize_target_os`, `parse_version_info`)
|
||||
- `dcr tree` command — visual dependency tree viewer (similar to `cargo tree`)
|
||||
|
||||
Removed:
|
||||
|
||||
|
||||
53
Cargo.lock
generated
53
Cargo.lock
generated
@@ -112,11 +112,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "dcr"
|
||||
version = "0.6.6"
|
||||
version = "0.6.7"
|
||||
dependencies = [
|
||||
"ctrlc",
|
||||
"git2",
|
||||
"glob",
|
||||
"openssl",
|
||||
"reqwest",
|
||||
"self-replace",
|
||||
"serde",
|
||||
@@ -192,6 +193,21 @@ version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
||||
dependencies = [
|
||||
"foreign-types-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types-shared"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.2"
|
||||
@@ -730,12 +746,46 @@ version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf0b434746ee2832f4f0baf10137e1cabb18cbe6912c69e2e33263c45250f542"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
"openssl-macros",
|
||||
"openssl-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-src"
|
||||
version = "300.6.0+3.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8e8cbfd3a4a8c8f089147fd7aaa33cf8c7450c4d09f8f80698a0cf093abeff4"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.115"
|
||||
@@ -744,6 +794,7 @@ checksum = "158fe5b292746440aa6e7a7e690e55aeb72d41505e2804c23c6973ad0e9c9781"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"openssl-src",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
[package]
|
||||
name = "dcr"
|
||||
version = "0.6.6"
|
||||
version = "0.6.7"
|
||||
edition = "2024"
|
||||
build = "build.rs"
|
||||
description = "DCR is a utility for managing C/C++ projects in a Cargo-like style."
|
||||
license = "MIT"
|
||||
repository = "https://github.com/dexoron/dcr"
|
||||
|
||||
[dependencies]
|
||||
glob = "0.3"
|
||||
@@ -14,3 +17,6 @@ toml = "1.0.3"
|
||||
git2 = "0.20.4"
|
||||
serde_json = "1"
|
||||
ctrlc = "3"
|
||||
|
||||
[target.'cfg(not(windows))'.dependencies]
|
||||
openssl = { version = "0.10", features = ["vendored"] }
|
||||
|
||||
@@ -16,7 +16,7 @@ The current implementation is written in Rust.
|
||||
- Clean build artifacts
|
||||
- Generate IDE integration files (VS Code, CLion) and compilation databases
|
||||
- Generate a minimal C project template
|
||||
- Build static and shared libraries
|
||||
- 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
|
||||
@@ -154,7 +154,9 @@ You can also set `build.target` to override the output directory (profile-indepe
|
||||
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`).
|
||||
`dcr run` is only for `build.kind = "bin"` and will fail for libraries.
|
||||
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.
|
||||
@@ -203,7 +205,7 @@ type = "app" # or "lib", "none"
|
||||
language = "c"
|
||||
standard = "c11"
|
||||
compiler = "clang"
|
||||
kind = "bin" # "bin", "staticlib", "sharedlib"
|
||||
kind = "bin" # "bin", "staticlib", "sharedlib", "efi", "elf"
|
||||
# Optional platform hint
|
||||
# platform = "x86_64"
|
||||
# Optional custom flags
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
* [dcr clean](commands/dcr-clean.md)
|
||||
* [dcr gen](commands/dcr-gen.md)
|
||||
* [dcr test](commands/dcr-test.md)
|
||||
* [dcr tree](commands/dcr-tree.md)
|
||||
|
||||
## Dependencies & Build
|
||||
|
||||
|
||||
@@ -5,11 +5,15 @@ Add a dependency to `dcr.toml`.
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
dcr add <name> <source> [options]
|
||||
dcr add <name> [source] [options]
|
||||
```
|
||||
|
||||
If `<source>` is omitted and a registry is configured, DCR will look up the
|
||||
package by name in the registry and add it as a version dependency.
|
||||
|
||||
## Sources
|
||||
|
||||
- *omitted*: Look up in the configured registry.
|
||||
- `path:./path/to/lib`: Local directory.
|
||||
- `github:user/repo`: GitHub repository.
|
||||
- `gitlab:user/repo`: GitLab repository.
|
||||
@@ -29,4 +33,5 @@ dcr add my_lib path:./libs/my_lib
|
||||
dcr add cool_lib github:user/cool_lib
|
||||
dcr add shared_lib gitlab:org/shared --branch develop
|
||||
dcr add custom_lib git:git.internal.com/r/lib --tag v1.0.0
|
||||
dcr add mylib # Looks up in registry
|
||||
```
|
||||
|
||||
@@ -25,6 +25,8 @@ dcr build --debug --clean
|
||||
## Config values used
|
||||
|
||||
- `package.name`
|
||||
- `build.filename`
|
||||
- `build.extension`
|
||||
- `build.compiler`
|
||||
- `build.language`
|
||||
- `build.standard`
|
||||
@@ -33,6 +35,7 @@ dcr build --debug --clean
|
||||
- `build.platform`
|
||||
- `build.cflags`
|
||||
- `build.ldflags`
|
||||
- `build.ldscript`
|
||||
- `build.debug` / `build.release`
|
||||
- `build.exclude`
|
||||
- `build.include`
|
||||
@@ -56,11 +59,16 @@ dcr build --debug --clean
|
||||
- Profile flag (`--debug` / `--release`) can appear in any argument position (duplicates are rejected).
|
||||
- Unknown profile flags return an error.
|
||||
- `--target <triple>` builds for specified target (short names: `linux`, `macos`, `windows`).
|
||||
- `--verbose` prints the compiler/linker command lines to stderr (also works via `DCR_DEBUG` env var).
|
||||
- If no `--target`, builds for `build.targets` or native.
|
||||
- Incremental rebuild checks source/object mtime and tracked header dependencies.
|
||||
- `--force` skips build cache checks and recompiles.
|
||||
- `--clean` removes `target/<profile>` and `build.clean` paths before building.
|
||||
- `build.filename` + `build.extension` allow full control over the final artifact name (e.g. `KERNEL.BIN` instead of using `package.name`). When `build.kind = "efi"`, the output is a PE32+ executable with `.efi` extension.
|
||||
- `build.kind = "efi"` links with `-shared -nostdlib -Wl,-dll -Wl,--subsystem,10` for UEFI PE32+ format. Use `build.filename` and `build.extension` for full control over the artifact name.
|
||||
- `build.kind = "elf"` produces a bare-metal ELF executable. Linking flags are user-controlled via `build.ldflags` (no PIE/CRT defaults are added).
|
||||
- Default GCC/Clang profile flags: `debug` -> `-O0 -g -Wall -Wextra -fno-omit-frame-pointer -DDCR_DEBUG`, `release` -> `-O3 -DNDEBUG`.
|
||||
These defaults are **not applied** for bare-metal targets (e.g. `aarch64-none-elf`, targets containing `none` / `-elf` / `eabi`).
|
||||
- For `language = "asm"` with `compiler = "as"`/`"gas"`, use `.s` files (no preprocessing). For `.S`, use `gcc` or `clang`.
|
||||
- In workspace root, `dcr build` builds all members in dependency order.
|
||||
- `build.exclude` removes paths from source/header collection; `build.include` re-allows matching paths and has priority over `exclude`.
|
||||
|
||||
@@ -15,7 +15,7 @@ dcr clean --release --all
|
||||
## Behavior
|
||||
|
||||
- Without arguments: removes entire `target/`.
|
||||
- With profile: removes only `target/<profile>/`.
|
||||
- With profile: removes only the profile-specific directory. Respects `build.target` from config: if `build.target = "aarch64-unknown-linux-gnu"`, cleans `target/aarch64-unknown-linux-gnu/<profile>/`.
|
||||
- With `--all`: also cleans all workspace members.
|
||||
- Additionally removes paths from `build.clean` if configured.
|
||||
- `build.clean` supports `{profile}` and version placeholders.
|
||||
|
||||
@@ -23,7 +23,7 @@ dcr run --debug --clean
|
||||
|
||||
## Restrictions
|
||||
|
||||
- If `build.kind = "staticlib"` or `build.kind = "sharedlib"`, `run` exits with error.
|
||||
- If `build.kind` (or `build.{profile}.kind`) is `"staticlib"` or `"sharedlib"`, `run` exits with error.
|
||||
- The profile is selected from the first argument.
|
||||
- `--target <triple>` runs the binary for specified target (short names: `linux`, `macos`, `windows`).
|
||||
- `--force` skips build cache checks and recompiles.
|
||||
|
||||
@@ -6,6 +6,7 @@ Runs the project's test suite.
|
||||
|
||||
```sh
|
||||
dcr test
|
||||
dcr test --debug
|
||||
dcr test --release
|
||||
dcr test --target linux
|
||||
dcr test --init
|
||||
@@ -39,6 +40,6 @@ DCR uses a simple test framework:
|
||||
|
||||
- Test binary is linked with the main project (for lib projects) or standalone.
|
||||
- `--init` creates `tests/dcr_test.h` and `tests/test.c` if missing.
|
||||
- Tests run in release mode by default for performance.
|
||||
- Tests run in debug mode by default. Use `--release` for release mode.
|
||||
- Custom test logic can be added by editing `tests/test.c`.
|
||||
- Fails if no `tests/test.c` exists (run `dcr test --init` first).
|
||||
22
docs/commands/dcr-tree.md
Normal file
22
docs/commands/dcr-tree.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# dcr tree
|
||||
|
||||
Displays a visual dependency tree for the current project (similar to `cargo tree`).
|
||||
|
||||
## Usage
|
||||
|
||||
```sh
|
||||
dcr tree
|
||||
dcr tree --invert
|
||||
dcr tree -p some-package
|
||||
```
|
||||
|
||||
## What it shows
|
||||
|
||||
- All direct and transitive dependencies
|
||||
- Version of each dependency
|
||||
- Whether a dependency is a registry, git, or path dependency
|
||||
|
||||
## Notes
|
||||
|
||||
- Useful for debugging dependency issues and understanding the build graph.
|
||||
- Output format is inspired by `cargo tree`.
|
||||
@@ -8,6 +8,8 @@ language = "c"
|
||||
standard = "c11"
|
||||
compiler = "clang"
|
||||
kind = "bin"
|
||||
# filename = "KERNEL"
|
||||
# extension = "BIN"
|
||||
# optional:
|
||||
# inherit = true # Inherit from base (default true)
|
||||
# targets = ["linux", "macos"] # Multiple targets to build
|
||||
@@ -31,13 +33,17 @@ kind = "bin"
|
||||
Mixed languages can be specified as an array, for example `["c", "c++", "asm"]`.
|
||||
- `standard` (string, required): language standard passed to compiler.
|
||||
- `compiler` (string, required): compiler command (for example `clang`, `gcc`, `cl`).
|
||||
- `kind` (string, required): `bin`, `staticlib`, or `sharedlib`.
|
||||
- `kind` (string, required): `bin`, `staticlib`, `sharedlib`, `efi`, or `elf`.
|
||||
- `filename` (string, optional): custom output filename (without extension). Overrides `package.name` for the final artifact.
|
||||
- `extension` (string, optional): custom file extension without the leading dot (e.g. `BIN`, `efi`, `EFI`). Combined with `filename`.
|
||||
- `inherit` (bool, optional): inherit settings from base `[build]` (default `true`). If `false`, only use target/profile specific settings.
|
||||
- `targets` (string array, optional): list of targets to build simultaneously. Supports short names (`linux`, `macos`, `windows`).
|
||||
- `target` (string, optional): custom output directory for final artifact.
|
||||
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.
|
||||
- `ldflags` (string array, optional): extra link flags.
|
||||
- `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`.
|
||||
- `include` directory entries (non-glob) are also passed to the compiler as `-I` include paths.
|
||||
@@ -145,4 +151,4 @@ rcc = "rcc"
|
||||
|
||||
- Compiler backend is selected by compiler string (`gcc`, `clang`, `cl`, `clang-cl`).
|
||||
- Empty or unknown compiler value falls back to clang-like build path.
|
||||
- `run` is not allowed for library kinds (`staticlib`, `sharedlib`).
|
||||
- `run` is not allowed for library kinds (`staticlib`, `sharedlib`), `efi`, or `elf`.
|
||||
|
||||
@@ -178,6 +178,8 @@ compiler = "aarch64-linux-gnu-gcc"' >> dcr.toml
|
||||
dcr build --target aarch64-linux-gnu --release
|
||||
```
|
||||
|
||||
**Note:** If you set `target = "aarch64-linux-gnu"` (or any value) directly under `[build]`, DCR will automatically add `--target=...` to cflags when using clang. You usually don't need to repeat it manually.
|
||||
|
||||
### Windows Build on Linux
|
||||
|
||||
```bash
|
||||
|
||||
@@ -49,7 +49,7 @@ dcr clean --release --all
|
||||
|
||||
## Notes
|
||||
|
||||
- `run` is unavailable for `build.kind = "staticlib"` and `build.kind = "sharedlib"`.
|
||||
- `run` is unavailable for `build.kind = "staticlib"`, `build.kind = "sharedlib"`, `build.kind = "efi"`, and `build.kind = "elf"`.
|
||||
- Build profile flag (`--debug` / `--release`) can be passed in any argument position.
|
||||
- Unknown profile flags produce an error.
|
||||
- In workspace root, `clean --all` cleans all member projects.
|
||||
|
||||
@@ -7,9 +7,11 @@ dcr new <name>
|
||||
dcr init
|
||||
dcr setup
|
||||
dcr add <name> <source>(optional)
|
||||
dcr build [--debug|--release]
|
||||
dcr build [--debug|--release] [--target <triple>]
|
||||
dcr run [--debug|--release]
|
||||
dcr clean [--debug|--release] [--all]
|
||||
dcr tree
|
||||
dcr test
|
||||
dcr gen <subcommand>
|
||||
dcr --help
|
||||
dcr --version
|
||||
@@ -18,11 +20,13 @@ dcr --update
|
||||
|
||||
## Notes on argument parsing
|
||||
|
||||
- `build`/`run` parse flags in any order (`--debug|--release`, `--force`, `--clean`) and reject duplicates.
|
||||
- `build`/`run` parse flags in any order (`--debug|--release`, `--force`, `--clean`, `--verbose`) and reject duplicates.
|
||||
- `new` requires exactly one argument.
|
||||
- `init` and `--update` do not accept extra arguments.
|
||||
- `clean` accepts `--debug|--release` and optional `--all`.
|
||||
- `gen` supports `project-info`, `compile-commands`, `vscode`, `clion`.
|
||||
- `tree` shows a dependency tree (similar to `cargo tree`).
|
||||
- `test` / `tests` runs the project's tests.
|
||||
|
||||
## Exit behavior overview
|
||||
|
||||
|
||||
@@ -12,10 +12,13 @@ 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
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::core::config::Config;
|
||||
use crate::core::deps::register;
|
||||
use crate::utils::fs::find_project_root;
|
||||
use crate::utils::log::error;
|
||||
use crate::utils::text::{BOLD_GREEN, colored};
|
||||
@@ -12,6 +13,7 @@ pub struct AddArgs {
|
||||
pub branch: Option<String>,
|
||||
pub tag: Option<String>,
|
||||
pub rev: Option<String>,
|
||||
pub version_from_registry: Option<String>,
|
||||
}
|
||||
|
||||
pub fn add(args: &[String]) -> i32 {
|
||||
@@ -71,6 +73,8 @@ pub fn add(args: &[String]) -> i32 {
|
||||
let mut table = Map::new();
|
||||
table.insert("path".to_string(), Value::String(path));
|
||||
Value::Table(table)
|
||||
} else if let Some(version) = add_args.version_from_registry {
|
||||
Value::String(version)
|
||||
} else {
|
||||
error("Dependency source (path or git) must be provided");
|
||||
return 1;
|
||||
@@ -124,8 +128,38 @@ fn parse_add_args(args: &[String]) -> Result<AddArgs, i32> {
|
||||
}
|
||||
source_spec = s.clone();
|
||||
} else {
|
||||
error("Dependency source is required");
|
||||
return Err(1);
|
||||
// No source provided — try registry auto-lookup
|
||||
match register::resolve_package_from_registry(&name) {
|
||||
Ok(info) => {
|
||||
let version = info
|
||||
.get("latest_version")
|
||||
.or_else(|| info.get("version"))
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or_else(|| {
|
||||
(
|
||||
"Registry entry for `".to_string() + &name + "` has no version field",
|
||||
1,
|
||||
)
|
||||
})
|
||||
.map_err(|(msg, code)| {
|
||||
error(&msg);
|
||||
code
|
||||
})?;
|
||||
return Ok(AddArgs {
|
||||
name,
|
||||
path: None,
|
||||
git: None,
|
||||
branch: None,
|
||||
tag: None,
|
||||
rev: None,
|
||||
version_from_registry: Some(version.to_string()),
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
error(&format!("Cannot add `{}` without source: {}", name, e));
|
||||
return Err(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while let Some(arg) = iter.next() {
|
||||
@@ -197,5 +231,6 @@ fn parse_add_args(args: &[String]) -> Result<AddArgs, i32> {
|
||||
branch,
|
||||
tag,
|
||||
rev,
|
||||
version_from_registry: None,
|
||||
})
|
||||
}
|
||||
|
||||
112
src/cli/build.rs
112
src/cli/build.rs
@@ -50,7 +50,15 @@ pub fn build(args: &[String]) -> i32 {
|
||||
Err(code) => return code,
|
||||
};
|
||||
|
||||
// If no target specified, use default host target for target-specific config
|
||||
if flags.target.is_none() {
|
||||
let config_path = root.join("dcr.toml");
|
||||
if let Ok(config) = Config::open(config_path.to_str().unwrap()) {
|
||||
let bt = get_build_string_with_profile(&config, "target", "debug");
|
||||
if !bt.is_empty() {
|
||||
flags.target = Some(bt);
|
||||
}
|
||||
}
|
||||
}
|
||||
if flags.target.is_none() {
|
||||
let default_target = if cfg!(target_os = "linux") {
|
||||
"x86_64-unknown-linux-gnu"
|
||||
@@ -69,7 +77,13 @@ pub fn build(args: &[String]) -> i32 {
|
||||
let _ = clean(&clean_args);
|
||||
}
|
||||
match with_dir(&root, || {
|
||||
build_from_root(&root, &flags.profile, flags.target.as_deref(), flags.force)
|
||||
build_from_root(
|
||||
&root,
|
||||
&flags.profile,
|
||||
flags.target.as_deref(),
|
||||
flags.force,
|
||||
flags.verbose,
|
||||
)
|
||||
}) {
|
||||
Ok(()) => 0,
|
||||
Err(msg) => {
|
||||
@@ -341,9 +355,15 @@ fn ensure_target_dirs(items: &[String], profile: &str, target_dir: Option<String
|
||||
if let Some(dir) = &target_dir {
|
||||
let _ = fs::create_dir_all(dir);
|
||||
} else {
|
||||
let default_dir = if cfg!(target_os = "linux") {
|
||||
let arch = std::env::consts::ARCH;
|
||||
format!("{arch}-unknown-linux-gnu/{profile}")
|
||||
} else {
|
||||
profile.to_string()
|
||||
};
|
||||
let target_items = check_dir(Some("target")).unwrap_or_default();
|
||||
if !target_items.contains(&profile.to_string()) {
|
||||
let _ = fs::create_dir(format!("./target/{profile}"));
|
||||
if !target_items.contains(&default_dir) {
|
||||
let _ = fs::create_dir_all(format!("./target/{default_dir}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -369,6 +389,7 @@ fn build_from_root(
|
||||
profile: &str,
|
||||
target: Option<&str>,
|
||||
force: bool,
|
||||
verbose: bool,
|
||||
) -> Result<(), String> {
|
||||
let config = Config::open("./dcr.toml").map_err(|err| err.to_string())?;
|
||||
let project_name = get_config_str(&config, "package.name");
|
||||
@@ -410,12 +431,19 @@ fn build_from_root(
|
||||
);
|
||||
}
|
||||
if let Some(workspace) = parse_workspace(&config, profile, build_target.as_deref(), root)? {
|
||||
build_workspace(&workspace, profile, build_target.as_deref(), force)?;
|
||||
build_workspace(&workspace, profile, build_target.as_deref(), force, verbose)?;
|
||||
let excludes: Vec<std::path::PathBuf> =
|
||||
workspace.members.iter().map(|m| m.path.clone()).collect();
|
||||
build_project_at(root, profile, build_target.as_deref(), &excludes, force)?;
|
||||
build_project_at(
|
||||
root,
|
||||
profile,
|
||||
build_target.as_deref(),
|
||||
&excludes,
|
||||
force,
|
||||
verbose,
|
||||
)?;
|
||||
} else {
|
||||
build_project_at(root, profile, build_target.as_deref(), &[], force)?;
|
||||
build_project_at(root, profile, build_target.as_deref(), &[], force, verbose)?;
|
||||
}
|
||||
}
|
||||
let elapsed = ((start_time.elapsed().as_secs_f64() * 100.0).trunc()) / 100.0;
|
||||
@@ -432,9 +460,10 @@ fn build_workspace(
|
||||
profile: &str,
|
||||
target: Option<&str>,
|
||||
force: bool,
|
||||
verbose: bool,
|
||||
) -> Result<(), String> {
|
||||
for member in &workspace.members {
|
||||
build_project_at(&member.path, profile, target, &[], force)?;
|
||||
build_project_at(&member.path, profile, target, &[], force, verbose)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -445,6 +474,7 @@ fn build_project_at(
|
||||
target: Option<&str>,
|
||||
exclude_dirs: &[std::path::PathBuf],
|
||||
force: bool,
|
||||
verbose: bool,
|
||||
) -> Result<(), String> {
|
||||
with_dir(project_root, || {
|
||||
check_interrupted()?;
|
||||
@@ -490,10 +520,32 @@ fn build_project_at(
|
||||
.or_else(|| get_config_opt(&config, "toolchain.moc"));
|
||||
let tc_rcc = get_config_value(&config, "toolchain", "rcc", profile, build_target)
|
||||
.or_else(|| get_config_opt(&config, "toolchain.rcc"));
|
||||
let build_cflags =
|
||||
let mut build_cflags =
|
||||
get_list_with_profile_and_target(&config, "cflags", profile, build_target)?;
|
||||
let build_ldflags =
|
||||
if let Some(t) = build_target
|
||||
&& !t.trim().is_empty()
|
||||
{
|
||||
let target_flag = format!("--target={}", t.trim());
|
||||
if !build_cflags
|
||||
.iter()
|
||||
.any(|f| f == &target_flag || f.starts_with("--target="))
|
||||
{
|
||||
build_cflags.insert(0, target_flag);
|
||||
}
|
||||
}
|
||||
let mut build_ldflags =
|
||||
get_list_with_profile_and_target(&config, "ldflags", profile, build_target)?;
|
||||
let build_ldscript =
|
||||
get_string_with_profile_and_target(&config, "ldscript", profile, build_target);
|
||||
if !build_ldscript.is_empty() {
|
||||
build_ldflags.push(format!("-T{}", build_ldscript));
|
||||
}
|
||||
|
||||
// Новые поля: filename и extension
|
||||
let output_filename =
|
||||
get_string_with_profile_and_target(&config, "filename", profile, build_target);
|
||||
let output_extension =
|
||||
get_string_with_profile_and_target(&config, "extension", profile, build_target);
|
||||
let build_excludes =
|
||||
get_list_with_profile_and_target(&config, "exclude", profile, build_target)?;
|
||||
let build_includes =
|
||||
@@ -557,7 +609,7 @@ fn build_project_at(
|
||||
dep_root.display()
|
||||
));
|
||||
}
|
||||
build_project_at(&dep_root, profile, build_target, &[], force)?;
|
||||
build_project_at(&dep_root, profile, build_target, &[], force, verbose)?;
|
||||
print!(
|
||||
"\r{:100}\r {} {} v{}",
|
||||
"",
|
||||
@@ -672,6 +724,17 @@ fn build_project_at(
|
||||
libs: &resolved.libs,
|
||||
cflags: &resolved_cflags,
|
||||
ldflags: &resolved_ldflags,
|
||||
output_filename: if output_filename.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(output_filename.as_str())
|
||||
},
|
||||
output_extension: if output_extension.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(output_extension.as_str())
|
||||
},
|
||||
verbose,
|
||||
};
|
||||
if std::env::var("DCR_DEBUG").is_ok() {
|
||||
eprintln!("[dcr] debug: compiler={}", ctx.compiler);
|
||||
@@ -1260,18 +1323,35 @@ fn write_build_fingerprint(ctx: &BuildContext, fingerprint: &str) -> Result<(),
|
||||
}
|
||||
|
||||
fn build_cache_path(profile: &str, target_dir: Option<&str>) -> std::path::PathBuf {
|
||||
let _ = target_dir;
|
||||
Path::new("./target").join(profile).join(".dcr-build.hash")
|
||||
match target_dir {
|
||||
Some(dir) => Path::new(dir).join(".dcr-build.hash"),
|
||||
None => Path::new("./target").join(profile).join(".dcr-build.hash"),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_output_path(ctx: &BuildContext) -> String {
|
||||
let name = ctx.output_filename.unwrap_or(ctx.project_name);
|
||||
let ext = ctx.output_extension.unwrap_or("");
|
||||
|
||||
let final_name = if ext.is_empty() {
|
||||
name.to_string()
|
||||
} else {
|
||||
format!("{}.{}", name, ext)
|
||||
};
|
||||
|
||||
if ctx.kind == "staticlib" {
|
||||
return crate::platform::lib_path(ctx.profile, ctx.project_name, ctx.target_dir);
|
||||
return crate::platform::lib_path(ctx.profile, &final_name, ctx.target_dir);
|
||||
}
|
||||
if ctx.kind == "sharedlib" {
|
||||
return crate::platform::shared_lib_path(ctx.profile, ctx.project_name, ctx.target_dir);
|
||||
return crate::platform::shared_lib_path(ctx.profile, &final_name, ctx.target_dir);
|
||||
}
|
||||
crate::platform::bin_path(ctx.profile, ctx.project_name, ctx.target_dir)
|
||||
if ctx.kind == "efi" {
|
||||
return crate::platform::efi_path(ctx.profile, &final_name, ctx.target_dir);
|
||||
}
|
||||
if ctx.kind == "elf" {
|
||||
return crate::platform::elf_path(ctx.profile, &final_name, ctx.target_dir);
|
||||
}
|
||||
crate::platform::bin_path(ctx.profile, &final_name, ctx.target_dir)
|
||||
}
|
||||
|
||||
fn collect_header_files(
|
||||
|
||||
@@ -91,17 +91,27 @@ fn parse_clean_flags(args: &[String]) -> Result<CleanFlags, String> {
|
||||
fn clean_from_root(root: &Path, flags: &CleanFlags) -> Result<(), String> {
|
||||
let config = Config::open("./dcr.toml").map_err(|err| err.to_string())?;
|
||||
|
||||
let target = flags.target.clone().or_else(|| {
|
||||
Some(if cfg!(target_os = "linux") {
|
||||
"x86_64-unknown-linux-gnu".to_string()
|
||||
} else if cfg!(target_os = "macos") {
|
||||
"x86_64-apple-darwin".to_string()
|
||||
} else if cfg!(target_os = "windows") {
|
||||
"x86_64-pc-windows-msvc".to_string()
|
||||
} else {
|
||||
"unknown".to_string()
|
||||
let target = flags
|
||||
.target
|
||||
.clone()
|
||||
.or_else(|| {
|
||||
config
|
||||
.get("build.target")
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty())
|
||||
})
|
||||
});
|
||||
.or_else(|| {
|
||||
Some(if cfg!(target_os = "linux") {
|
||||
"x86_64-unknown-linux-gnu".to_string()
|
||||
} else if cfg!(target_os = "macos") {
|
||||
"x86_64-apple-darwin".to_string()
|
||||
} else if cfg!(target_os = "windows") {
|
||||
"x86_64-pc-windows-msvc".to_string()
|
||||
} else {
|
||||
"unknown".to_string()
|
||||
})
|
||||
});
|
||||
|
||||
if flags.all
|
||||
&& let Some(workspace) = parse_workspace(
|
||||
|
||||
@@ -138,8 +138,8 @@ fn download_asset(client: &Client, url: &str) -> Result<Vec<u8>, String> {
|
||||
fn asset_candidates(target: &str) -> Vec<String> {
|
||||
let mut names = vec![format!("dcr-{target}")];
|
||||
|
||||
if target == "x86_64-pc-windows-msvc" {
|
||||
names.push("dcr-x86_64-pc-windows-msvc.exe".to_string());
|
||||
if target.contains("-windows-") || target.ends_with("-windows") {
|
||||
names.push(format!("dcr-{target}.exe"));
|
||||
}
|
||||
|
||||
names
|
||||
|
||||
@@ -6,6 +6,7 @@ pub struct BuildRunFlags {
|
||||
pub target: Option<String>,
|
||||
pub force: bool,
|
||||
pub clean: bool,
|
||||
pub verbose: bool,
|
||||
}
|
||||
|
||||
pub fn parse_build_run_flags(args: &[String]) -> Result<BuildRunFlags, i32> {
|
||||
@@ -13,6 +14,7 @@ pub fn parse_build_run_flags(args: &[String]) -> Result<BuildRunFlags, i32> {
|
||||
let mut target = None;
|
||||
let mut force = false;
|
||||
let mut clean = false;
|
||||
let mut verbose = false;
|
||||
let mut iter = args.iter();
|
||||
|
||||
while let Some(arg) = iter.next() {
|
||||
@@ -29,6 +31,10 @@ pub fn parse_build_run_flags(args: &[String]) -> Result<BuildRunFlags, i32> {
|
||||
clean = true;
|
||||
continue;
|
||||
}
|
||||
if candidate == "verbose" {
|
||||
verbose = true;
|
||||
continue;
|
||||
}
|
||||
if candidate == "target" {
|
||||
if let Some(t) = iter.next() {
|
||||
target = Some(t.clone());
|
||||
@@ -55,5 +61,6 @@ pub fn parse_build_run_flags(args: &[String]) -> Result<BuildRunFlags, i32> {
|
||||
target,
|
||||
force,
|
||||
clean,
|
||||
verbose,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -337,6 +337,9 @@ fn collect_project_info_inner(root: &Path, profile: &str) -> Result<ProjectInfo,
|
||||
libs: &resolved.libs,
|
||||
cflags: &resolved_cflags,
|
||||
ldflags: &resolved_ldflags,
|
||||
output_filename: None,
|
||||
output_extension: None,
|
||||
verbose: false,
|
||||
};
|
||||
|
||||
let sources = collect_sources(&ctx).map_err(|e| format!("Failed to collect sources: {e}"))?;
|
||||
|
||||
@@ -69,15 +69,6 @@ pub fn run(args: &[String]) -> i32 {
|
||||
.get("package.name")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("");
|
||||
let build_kind = config
|
||||
.get("build.kind")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("");
|
||||
let target_dir = config
|
||||
.get("build.target")
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|s| s.trim())
|
||||
.filter(|s| !s.is_empty());
|
||||
|
||||
let mut flags = match parse_build_run_flags(args) {
|
||||
Ok(v) => v,
|
||||
@@ -98,6 +89,12 @@ pub fn run(args: &[String]) -> i32 {
|
||||
flags.target = Some(default_target.to_string());
|
||||
}
|
||||
|
||||
let build_kind = config
|
||||
.get(&format!("build.{}.kind", flags.profile))
|
||||
.and_then(|v| v.as_str())
|
||||
.or_else(|| config.get("build.kind").and_then(|v| v.as_str()))
|
||||
.unwrap_or("");
|
||||
|
||||
let normalized_target_dir = flags
|
||||
.target
|
||||
.as_ref()
|
||||
@@ -110,7 +107,9 @@ pub fn run(args: &[String]) -> i32 {
|
||||
let run_cmd = get_run_cmd(&config, &flags.profile, flags.target.as_deref(), version);
|
||||
|
||||
let kind = build_kind.trim();
|
||||
if run_cmd.is_none() && (kind == "staticlib" || kind == "sharedlib") {
|
||||
if run_cmd.is_none()
|
||||
&& (kind == "staticlib" || kind == "sharedlib" || kind == "efi" || kind == "elf")
|
||||
{
|
||||
error("Cannot run library build");
|
||||
return 1;
|
||||
}
|
||||
@@ -128,13 +127,21 @@ pub fn run(args: &[String]) -> i32 {
|
||||
}
|
||||
println!("\n {} {}", colored("Running", BOLD_GREEN), bin_path);
|
||||
println!("--------------------------------");
|
||||
return run_binary(project_name, &flags.profile, target_dir);
|
||||
return run_binary(
|
||||
project_name,
|
||||
&flags.profile,
|
||||
normalized_target_dir.as_deref(),
|
||||
);
|
||||
}
|
||||
|
||||
let fallback_code = if let Some(cmd) = run_cmd {
|
||||
run_shell(&cmd)
|
||||
} else {
|
||||
run_binary(project_name, &flags.profile, target_dir)
|
||||
run_binary(
|
||||
project_name,
|
||||
&flags.profile,
|
||||
normalized_target_dir.as_deref(),
|
||||
)
|
||||
};
|
||||
if fallback_code != 1 {
|
||||
return fallback_code;
|
||||
|
||||
@@ -16,10 +16,11 @@ const BOLD_BLUE: &str = "\x1b[1m\x1b[94m";
|
||||
|
||||
pub fn test(args: &[String]) -> i32 {
|
||||
let mut init_header = false;
|
||||
let mut profile = "debug";
|
||||
for arg in args {
|
||||
if arg == "--help" {
|
||||
println!("USAGE:");
|
||||
println!(" dcr test [--init]");
|
||||
println!(" dcr test [--init] [--debug | --release]");
|
||||
println!();
|
||||
println!("ALIASES:");
|
||||
println!(" dcr tests");
|
||||
@@ -29,12 +30,18 @@ pub fn test(args: &[String]) -> i32 {
|
||||
println!();
|
||||
println!("OPTIONS:");
|
||||
println!(" --init Create tests/dcr_test.h in current project");
|
||||
println!(" --debug Build and run tests with debug profile (default)");
|
||||
println!(" --release Build and run tests with release profile");
|
||||
return 0;
|
||||
}
|
||||
if arg == "--init" {
|
||||
init_header = true;
|
||||
continue;
|
||||
}
|
||||
if arg == "--debug" || arg == "--release" {
|
||||
profile = arg.trim_start_matches("--");
|
||||
continue;
|
||||
}
|
||||
error("Unknown argument");
|
||||
return 1;
|
||||
}
|
||||
@@ -68,7 +75,7 @@ pub fn test(args: &[String]) -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
match with_dir(&root, run_testsuite) {
|
||||
match with_dir(&root, || run_testsuite(profile)) {
|
||||
Ok(code) => code,
|
||||
Err(msg) => {
|
||||
error(&msg);
|
||||
@@ -77,13 +84,12 @@ pub fn test(args: &[String]) -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
fn run_testsuite() -> Result<i32, String> {
|
||||
if build::build(&[String::from("--release")]) != 0 {
|
||||
fn run_testsuite(profile: &str) -> Result<i32, String> {
|
||||
if build::build(&[format!("--{profile}")]) != 0 {
|
||||
return Ok(1);
|
||||
}
|
||||
|
||||
let config = Config::open("./dcr.toml").map_err(|_| "Failed to read dcr.toml".to_string())?;
|
||||
let profile = "release";
|
||||
let name = get_config_str(&config, "package.name");
|
||||
let language = get_language_with_profile(&config, profile)?;
|
||||
let compiler_cfg = get_string_with_profile(&config, "compiler", profile);
|
||||
|
||||
@@ -5,6 +5,10 @@ use std::sync::{Mutex, OnceLock};
|
||||
|
||||
static OUTPUT_MUTEX: OnceLock<Mutex<()>> = OnceLock::new();
|
||||
|
||||
pub fn get_output_lock() -> &'static Mutex<()> {
|
||||
OUTPUT_MUTEX.get_or_init(|| Mutex::new(()))
|
||||
}
|
||||
|
||||
pub fn collect_sources(
|
||||
roots: &[PathBuf],
|
||||
extensions: &[&str],
|
||||
@@ -230,10 +234,16 @@ pub fn normalize_source_path(path: &Path) -> String {
|
||||
|
||||
pub fn object_path(obj_dir: &Path, source: &str, obj_ext: &str) -> String {
|
||||
let src_path = Path::new(source);
|
||||
let rel = src_path
|
||||
.strip_prefix("./src")
|
||||
.or_else(|_| src_path.strip_prefix("src"))
|
||||
.unwrap_or(src_path);
|
||||
let stripped = src_path.strip_prefix("./").unwrap_or(src_path);
|
||||
let rel = stripped
|
||||
.components()
|
||||
.skip(1)
|
||||
.collect::<std::path::PathBuf>();
|
||||
let rel = if rel.components().next().is_some() {
|
||||
rel
|
||||
} else {
|
||||
stripped.to_path_buf()
|
||||
};
|
||||
let mut out = obj_dir.join(rel);
|
||||
out.set_extension(obj_ext.trim_start_matches('.'));
|
||||
out.to_string_lossy().to_string()
|
||||
|
||||
@@ -18,7 +18,10 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
||||
};
|
||||
let start_time = Instant::now();
|
||||
let sources = collect_sources(ctx)?;
|
||||
let obj_dir = Path::new("./target").join(ctx.profile).join("obj");
|
||||
let obj_dir = match ctx.target_dir {
|
||||
Some(dir) => Path::new(dir).join("obj"),
|
||||
None => Path::new("./target").join(ctx.profile).join("obj"),
|
||||
};
|
||||
let objects = build_objects(assembler, &sources, &obj_dir, ctx, "o")?;
|
||||
|
||||
if ctx.kind == "staticlib" {
|
||||
@@ -37,7 +40,7 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
||||
for obj in &objects {
|
||||
cmd.arg(obj);
|
||||
}
|
||||
if std::env::var("DCR_DEBUG").is_ok() {
|
||||
if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() {
|
||||
eprintln!("[dcr] {:?}", cmd);
|
||||
}
|
||||
match cmd.status() {
|
||||
@@ -70,14 +73,24 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
||||
for flag in ctx.ldflags {
|
||||
cmd.arg(flag);
|
||||
}
|
||||
let out_path = if ctx.kind == "sharedlib" {
|
||||
platform::shared_lib_path(ctx.profile, ctx.project_name, ctx.target_dir)
|
||||
let name = ctx.output_filename.unwrap_or(ctx.project_name);
|
||||
let ext = ctx.output_extension.unwrap_or("");
|
||||
let final_name = if ext.is_empty() {
|
||||
name.to_string()
|
||||
} else {
|
||||
platform::bin_path(ctx.profile, ctx.project_name, ctx.target_dir)
|
||||
format!("{}.{}", name, ext)
|
||||
};
|
||||
|
||||
let out_path = if ctx.kind == "sharedlib" {
|
||||
platform::shared_lib_path(ctx.profile, &final_name, ctx.target_dir)
|
||||
} else if ctx.kind == "elf" {
|
||||
platform::elf_path(ctx.profile, &final_name, ctx.target_dir)
|
||||
} else {
|
||||
platform::bin_path(ctx.profile, &final_name, ctx.target_dir)
|
||||
};
|
||||
cmd.arg("-o").arg(out_path);
|
||||
|
||||
if std::env::var("DCR_DEBUG").is_ok() {
|
||||
if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() {
|
||||
eprintln!("[dcr] {:?}", cmd);
|
||||
}
|
||||
match cmd.status() {
|
||||
|
||||
@@ -26,10 +26,19 @@ pub struct BuildContext<'a> {
|
||||
pub libs: &'a [String],
|
||||
pub cflags: &'a [String],
|
||||
pub ldflags: &'a [String],
|
||||
pub output_filename: Option<&'a str>,
|
||||
pub output_extension: Option<&'a str>,
|
||||
pub verbose: bool,
|
||||
}
|
||||
|
||||
pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
||||
let compiler = ctx.compiler.to_lowercase();
|
||||
if !check_compiler_exists(ctx.compiler) {
|
||||
return Err(format!(
|
||||
"Compiler not found: {}. Make sure it is installed and available in PATH.",
|
||||
ctx.compiler
|
||||
));
|
||||
}
|
||||
if compiler.contains("clang-cl") {
|
||||
return msvc::build(ctx);
|
||||
}
|
||||
@@ -45,6 +54,14 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
||||
unix_cc::build(ctx)
|
||||
}
|
||||
|
||||
fn check_compiler_exists(compiler: &str) -> bool {
|
||||
let name = if compiler.is_empty() { "cc" } else { compiler };
|
||||
std::process::Command::new(name)
|
||||
.arg("--version")
|
||||
.output()
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
pub fn collect_sources(ctx: &BuildContext) -> Result<Vec<String>, String> {
|
||||
let compiler = ctx.compiler.to_lowercase();
|
||||
if compiler.contains("clang-cl") || compiler == "cl" || compiler.contains("msvc") {
|
||||
|
||||
@@ -18,7 +18,10 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
||||
}
|
||||
let start_time = Instant::now();
|
||||
let sources = collect_sources(ctx)?;
|
||||
let obj_dir = Path::new("./target").join(ctx.profile).join("obj");
|
||||
let obj_dir = match ctx.target_dir {
|
||||
Some(dir) => Path::new(dir).join("obj"),
|
||||
None => Path::new("./target").join(ctx.profile).join("obj"),
|
||||
};
|
||||
let objects = build_objects(compiler, &sources, &obj_dir, ctx, "obj")?;
|
||||
|
||||
if ctx.kind == "staticlib" {
|
||||
@@ -28,7 +31,7 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
||||
for obj in &objects {
|
||||
cmd.arg(obj);
|
||||
}
|
||||
if std::env::var("DCR_DEBUG").is_ok() {
|
||||
if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() {
|
||||
eprintln!("[dcr] {:?}", cmd);
|
||||
}
|
||||
match cmd.status() {
|
||||
@@ -92,12 +95,14 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
||||
}
|
||||
let out_path = if ctx.kind == "sharedlib" {
|
||||
platform::shared_lib_path(ctx.profile, ctx.project_name, ctx.target_dir)
|
||||
} else if ctx.kind == "elf" {
|
||||
platform::elf_path(ctx.profile, ctx.project_name, ctx.target_dir)
|
||||
} else {
|
||||
platform::bin_path(ctx.profile, ctx.project_name, ctx.target_dir)
|
||||
};
|
||||
cmd.arg(format!("/Fe:{out_path}"));
|
||||
|
||||
if std::env::var("DCR_DEBUG").is_ok() {
|
||||
if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() {
|
||||
eprintln!("[dcr] {:?}", cmd);
|
||||
}
|
||||
match cmd.status() {
|
||||
@@ -185,8 +190,6 @@ fn default_flags(profile: &str) -> &'static [&'static str] {
|
||||
}
|
||||
}
|
||||
|
||||
static OUTPUT_MUTEX: std::sync::Mutex<()> = std::sync::Mutex::new(());
|
||||
|
||||
fn build_objects(
|
||||
compiler: &str,
|
||||
sources: &[String],
|
||||
@@ -282,7 +285,7 @@ fn build_object(
|
||||
}
|
||||
}
|
||||
|
||||
let _lock = OUTPUT_MUTEX.lock().unwrap();
|
||||
let _lock = common::get_output_lock().lock().unwrap();
|
||||
|
||||
if !output.status.success() {
|
||||
eprint!("{}", clean_stdout);
|
||||
|
||||
@@ -18,7 +18,10 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
||||
};
|
||||
let start_time = Instant::now();
|
||||
let sources = collect_sources(ctx)?;
|
||||
let obj_dir = Path::new("./target").join(ctx.profile).join("obj");
|
||||
let obj_dir = match ctx.target_dir {
|
||||
Some(dir) => Path::new(dir).join("obj"),
|
||||
None => Path::new("./target").join(ctx.profile).join("obj"),
|
||||
};
|
||||
let objects = build_objects(assembler, &sources, &obj_dir, ctx, "o")?;
|
||||
|
||||
if ctx.kind == "staticlib" {
|
||||
@@ -37,7 +40,7 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
||||
for obj in &objects {
|
||||
cmd.arg(obj);
|
||||
}
|
||||
if std::env::var("DCR_DEBUG").is_ok() {
|
||||
if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() {
|
||||
eprintln!("[dcr] {:?}", cmd);
|
||||
}
|
||||
match cmd.status() {
|
||||
@@ -70,14 +73,24 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
||||
for flag in ctx.ldflags {
|
||||
cmd.arg(flag);
|
||||
}
|
||||
let out_path = if ctx.kind == "sharedlib" {
|
||||
platform::shared_lib_path(ctx.profile, ctx.project_name, ctx.target_dir)
|
||||
let name = ctx.output_filename.unwrap_or(ctx.project_name);
|
||||
let ext = ctx.output_extension.unwrap_or("");
|
||||
let final_name = if ext.is_empty() {
|
||||
name.to_string()
|
||||
} else {
|
||||
platform::bin_path(ctx.profile, ctx.project_name, ctx.target_dir)
|
||||
format!("{}.{}", name, ext)
|
||||
};
|
||||
|
||||
let out_path = if ctx.kind == "sharedlib" {
|
||||
platform::shared_lib_path(ctx.profile, &final_name, ctx.target_dir)
|
||||
} else if ctx.kind == "elf" {
|
||||
platform::elf_path(ctx.profile, &final_name, ctx.target_dir)
|
||||
} else {
|
||||
platform::bin_path(ctx.profile, &final_name, ctx.target_dir)
|
||||
};
|
||||
cmd.arg("-o").arg(out_path);
|
||||
|
||||
if std::env::var("DCR_DEBUG").is_ok() {
|
||||
if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() {
|
||||
eprintln!("[dcr] {:?}", cmd);
|
||||
}
|
||||
match cmd.status() {
|
||||
@@ -147,7 +160,7 @@ fn build_object(
|
||||
cmd.arg(flag);
|
||||
}
|
||||
|
||||
if std::env::var("DCR_DEBUG").is_ok() {
|
||||
if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() {
|
||||
eprintln!("[dcr] {:?}", cmd);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,10 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
||||
ctx.exclude_dirs,
|
||||
ctx.include_paths,
|
||||
)?;
|
||||
let obj_dir = Path::new("./target").join(ctx.profile).join("obj");
|
||||
let obj_dir = match ctx.target_dir {
|
||||
Some(dir) => Path::new(dir).join("obj"),
|
||||
None => Path::new("./target").join(ctx.profile).join("obj"),
|
||||
};
|
||||
let objects = build_objects(compiler, &sources, &obj_dir, ctx, "o")?;
|
||||
|
||||
if ctx.kind == "staticlib" {
|
||||
@@ -30,7 +33,7 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
||||
for obj in &objects {
|
||||
cmd.arg(obj);
|
||||
}
|
||||
if std::env::var("DCR_DEBUG").is_ok() {
|
||||
if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() {
|
||||
eprintln!("[dcr] {:?}", cmd);
|
||||
}
|
||||
match cmd.status() {
|
||||
@@ -51,6 +54,12 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
||||
cmd.arg("-shared");
|
||||
}
|
||||
}
|
||||
if ctx.kind == "efi" {
|
||||
cmd.arg("-shared");
|
||||
cmd.arg("-nostdlib");
|
||||
cmd.arg("-Wl,-dll");
|
||||
cmd.arg("-Wl,--subsystem,10");
|
||||
}
|
||||
for obj in &objects {
|
||||
cmd.arg(obj);
|
||||
}
|
||||
@@ -63,14 +72,26 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
||||
for flag in ctx.ldflags {
|
||||
cmd.arg(flag);
|
||||
}
|
||||
let out_path = if ctx.kind == "sharedlib" {
|
||||
platform::shared_lib_path(ctx.profile, ctx.project_name, ctx.target_dir)
|
||||
let name = ctx.output_filename.unwrap_or(ctx.project_name);
|
||||
let ext = ctx.output_extension.unwrap_or("");
|
||||
let final_name = if ext.is_empty() {
|
||||
name.to_string()
|
||||
} else {
|
||||
platform::bin_path(ctx.profile, ctx.project_name, ctx.target_dir)
|
||||
format!("{}.{}", name, ext)
|
||||
};
|
||||
|
||||
let out_path = if ctx.kind == "sharedlib" {
|
||||
platform::shared_lib_path(ctx.profile, &final_name, ctx.target_dir)
|
||||
} else if ctx.kind == "efi" {
|
||||
platform::efi_path(ctx.profile, &final_name, ctx.target_dir)
|
||||
} else if ctx.kind == "elf" {
|
||||
platform::elf_path(ctx.profile, &final_name, ctx.target_dir)
|
||||
} else {
|
||||
platform::bin_path(ctx.profile, &final_name, ctx.target_dir)
|
||||
};
|
||||
cmd.arg("-o").arg(out_path);
|
||||
|
||||
if std::env::var("DCR_DEBUG").is_ok() {
|
||||
if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() {
|
||||
eprintln!("[dcr] {:?}", cmd);
|
||||
}
|
||||
match cmd.status() {
|
||||
@@ -125,6 +146,19 @@ fn default_flags(profile: &str) -> &'static [&'static str] {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_bare_metal_target(target: Option<&str>) -> bool {
|
||||
if let Some(t) = target {
|
||||
let lower = t.to_lowercase();
|
||||
lower.contains("none")
|
||||
|| lower.contains("-elf")
|
||||
|| lower.contains("eabi")
|
||||
|| lower.contains("baremetal")
|
||||
|| lower.contains("bare-metal")
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn build_objects(
|
||||
compiler: &str,
|
||||
sources: &[String],
|
||||
@@ -179,7 +213,8 @@ fn build_object(
|
||||
cmd.arg(format!("-std={}", ctx.standard));
|
||||
}
|
||||
|
||||
if ctx.cflags.is_empty() {
|
||||
let use_dcr_defaults = ctx.cflags.is_empty() && !is_bare_metal_target(ctx.target);
|
||||
if use_dcr_defaults {
|
||||
for flag in default_flags(ctx.profile) {
|
||||
cmd.arg(flag);
|
||||
}
|
||||
@@ -196,7 +231,7 @@ fn build_object(
|
||||
let d_path = Path::new(obj_path).with_extension("d");
|
||||
cmd.arg("-MMD").arg("-MF").arg(&d_path);
|
||||
|
||||
if std::env::var("DCR_DEBUG").is_ok() {
|
||||
if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() {
|
||||
eprintln!("[dcr] {:?}", cmd);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ const DEFAULT_LANGUAGE: &str = "c";
|
||||
const DEFAULT_STANDARD: &str = "c11";
|
||||
const DEFAULT_COMPILER: &str = "clang";
|
||||
const DEFAULT_KIND: &str = "bin";
|
||||
const VALID_KINDS: &[&str] = &["bin", "staticlib", "sharedlib", "efi", "elf"];
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ConfigError {
|
||||
@@ -242,7 +243,7 @@ impl Config {
|
||||
validate_toolchain(self.typed.toolchain.as_ref())?;
|
||||
if let Some(kind) = &self.typed.build.kind {
|
||||
let kind = kind.trim();
|
||||
if !kind.is_empty() && kind != "bin" && kind != "staticlib" && kind != "sharedlib" {
|
||||
if !kind.is_empty() && !VALID_KINDS.contains(&kind) {
|
||||
return Err(ConfigError::Invalid("build.kind is invalid".into()));
|
||||
}
|
||||
}
|
||||
@@ -410,7 +411,7 @@ impl Config {
|
||||
}
|
||||
if let Some(kind) = table.get("kind").and_then(|v| v.as_str()) {
|
||||
let kind = kind.trim();
|
||||
if !kind.is_empty() && kind != "bin" && kind != "staticlib" && kind != "sharedlib" {
|
||||
if !kind.is_empty() && !VALID_KINDS.contains(&kind) {
|
||||
return Err(ConfigError::Invalid(format!(
|
||||
"build.{profile}.kind is invalid"
|
||||
)));
|
||||
@@ -919,14 +920,16 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn validate_valid_kinds() {
|
||||
for kind in ["bin", "staticlib", "sharedlib"] {
|
||||
let dir = temp_dir(&format!("kind_{kind}"));
|
||||
let content = format!(
|
||||
for &kind in VALID_KINDS {
|
||||
let dir = temp_dir("valid_kind");
|
||||
let toml = format!(
|
||||
"[package]\nname = \"test\"\nversion = \"0.1.0\"\n\n[build]\nlanguage = \"c\"\nstandard = \"c11\"\ncompiler = \"clang\"\nkind = \"{kind}\"\n\n[dependencies]\n"
|
||||
);
|
||||
let path = write_toml_file(&dir, &content);
|
||||
let result = Config::open(&path.to_string_lossy());
|
||||
assert!(result.is_ok(), "kind '{kind}' should be valid");
|
||||
let path = write_toml_file(&dir, &toml);
|
||||
assert!(
|
||||
Config::open(&path.to_string_lossy()).is_ok(),
|
||||
"kind '{kind}' should be valid"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,20 @@ pub mod register;
|
||||
|
||||
use crate::core::config::Config;
|
||||
use crate::core::deps::common::ResolvedDeps;
|
||||
use crate::core::deps::lock::{DepLock, write_lock};
|
||||
use std::path::Path;
|
||||
|
||||
fn dep_version(path: &Path) -> String {
|
||||
Config::open(&path.join("dcr.toml").to_string_lossy())
|
||||
.ok()
|
||||
.and_then(|c| {
|
||||
c.get("package.version")
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|s| s.to_string())
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn resolve_deps(
|
||||
config: &Config,
|
||||
_profile: &str,
|
||||
@@ -26,7 +38,7 @@ pub fn resolve_deps(
|
||||
.to_string();
|
||||
|
||||
let deps_table = config.get("dependencies").and_then(|v| v.as_table());
|
||||
let lock_packages = Vec::new();
|
||||
let mut lock_packages: Vec<DepLock> = Vec::new();
|
||||
|
||||
if let Some(deps) = deps_table {
|
||||
for (name, value) in deps {
|
||||
@@ -45,6 +57,23 @@ pub fn resolve_deps(
|
||||
.to_string(),
|
||||
);
|
||||
resolved.libs.push(name.clone());
|
||||
|
||||
lock_packages.push(DepLock {
|
||||
name: name.clone(),
|
||||
version: pkg_info
|
||||
.get("version")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("")
|
||||
.to_string(),
|
||||
checksum: String::new(),
|
||||
source: format!(
|
||||
"registry+{}",
|
||||
pkg_info
|
||||
.get("registry_url")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("https://dcr-registry.pages.dev")
|
||||
),
|
||||
});
|
||||
} else if let Some(path) = path_dep_path(value) {
|
||||
let dep_root = project_root.join(path);
|
||||
if let Some(table) = value.as_table() {
|
||||
@@ -86,11 +115,25 @@ pub fn resolve_deps(
|
||||
push_default_lib_dirs(&mut resolved.lib_dirs, &dep_root);
|
||||
resolved.libs.push(name.clone());
|
||||
}
|
||||
|
||||
lock_packages.push(DepLock {
|
||||
name: name.clone(),
|
||||
version: dep_version(&dep_root),
|
||||
checksum: String::new(),
|
||||
source: format!("path+{}", dep_root.display()),
|
||||
});
|
||||
} else if let Some(git_info) = git_dep(value) {
|
||||
lock_packages.push(DepLock {
|
||||
name: name.clone(),
|
||||
version: git_info.version.unwrap_or_default(),
|
||||
checksum: String::new(),
|
||||
source: format!("git+{}", git_info.url),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
crate::core::deps::lock::write_lock(
|
||||
write_lock(
|
||||
project_root,
|
||||
&project_name,
|
||||
&project_version,
|
||||
@@ -100,6 +143,24 @@ pub fn resolve_deps(
|
||||
Ok(resolved)
|
||||
}
|
||||
|
||||
struct GitDep<'a> {
|
||||
url: &'a str,
|
||||
version: Option<String>,
|
||||
}
|
||||
|
||||
fn git_dep(value: &toml::Value) -> Option<GitDep<'_>> {
|
||||
if let Some(table) = value.as_table()
|
||||
&& let Some(url) = table.get("git").and_then(|v| v.as_str())
|
||||
{
|
||||
let version = table
|
||||
.get("version")
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|s| s.to_string());
|
||||
return Some(GitDep { url, version });
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn path_dep_path(value: &toml::Value) -> Option<&str> {
|
||||
if let Some(table) = value.as_table() {
|
||||
return table.get("path").and_then(|v| v.as_str());
|
||||
|
||||
@@ -15,9 +15,23 @@ pub struct DcrConfig {
|
||||
pub registry: std::collections::HashMap<String, Registry>,
|
||||
}
|
||||
|
||||
fn home_dir() -> Option<PathBuf> {
|
||||
if let Ok(home) = std::env::var("HOME") {
|
||||
return Some(PathBuf::from(home));
|
||||
}
|
||||
if let Ok(profile) = std::env::var("USERPROFILE") {
|
||||
return Some(PathBuf::from(profile));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn dcr_config_dir() -> Option<PathBuf> {
|
||||
home_dir().map(|h| h.join(".dcr"))
|
||||
}
|
||||
|
||||
pub fn get_registry_config() -> Option<DcrConfig> {
|
||||
let home = std::env::var("HOME").ok()?;
|
||||
let config_path = Path::new(&home).join(".dcr/config.toml");
|
||||
let home = home_dir()?;
|
||||
let config_path = home.join(".dcr/config.toml");
|
||||
if !config_path.exists() {
|
||||
return None;
|
||||
}
|
||||
@@ -30,16 +44,25 @@ pub fn get_index_path() -> PathBuf {
|
||||
std::env::var("DCR_INDEX_PATH")
|
||||
.map(PathBuf::from)
|
||||
.unwrap_or_else(|_| {
|
||||
let home = std::env::var("HOME").unwrap_or_else(|_| ".".to_string());
|
||||
Path::new(&home).join(".dcr").join("index.json")
|
||||
dcr_config_dir()
|
||||
.unwrap_or_else(|| PathBuf::from("."))
|
||||
.join("index.json")
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_registry_cache_root() -> PathBuf {
|
||||
get_index_path()
|
||||
.parent()
|
||||
.map(Path::to_path_buf)
|
||||
.unwrap_or_else(|| PathBuf::from("."))
|
||||
let index_path = get_index_path();
|
||||
if let Some(parent) = index_path.parent() {
|
||||
if parent.exists() {
|
||||
return parent.to_path_buf();
|
||||
}
|
||||
if let Err(e) = std::fs::create_dir_all(parent) {
|
||||
eprintln!("Warning: failed to create registry cache directory: {e}");
|
||||
}
|
||||
parent.to_path_buf()
|
||||
} else {
|
||||
PathBuf::from(".")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn package_root_from_registry_info(pkg_info: &JsonValue) -> Result<PathBuf, String> {
|
||||
@@ -109,12 +132,16 @@ pub fn is_registry_dep(value: &TomlValue) -> bool {
|
||||
return !is_path_like_string(raw) && !is_git_like_string(raw);
|
||||
}
|
||||
if let Some(table) = value.as_table() {
|
||||
return !table.contains_key("git")
|
||||
&& !table.contains_key("path")
|
||||
&& !table.contains_key("url")
|
||||
&& !table.contains_key("version");
|
||||
if table.contains_key("git") || table.contains_key("path") || table.contains_key("url") {
|
||||
return false;
|
||||
}
|
||||
table.contains_key("version")
|
||||
|| table.contains_key("features")
|
||||
|| table.contains_key("optional")
|
||||
|| table.contains_key("registry")
|
||||
} else {
|
||||
false
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn path_from_string_dep(value: &TomlValue) -> Option<&str> {
|
||||
|
||||
@@ -20,10 +20,21 @@ pub struct RegistryManager {
|
||||
pub path: PathBuf,
|
||||
}
|
||||
|
||||
fn home_dir() -> Option<PathBuf> {
|
||||
if let Ok(home) = std::env::var("HOME") {
|
||||
return Some(PathBuf::from(home));
|
||||
}
|
||||
if let Ok(profile) = std::env::var("USERPROFILE") {
|
||||
return Some(PathBuf::from(profile));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
impl RegistryManager {
|
||||
pub fn load() -> Result<Self, Box<dyn std::error::Error>> {
|
||||
let home = std::env::var("HOME")?;
|
||||
let dcr_dir = PathBuf::from(home).join(".dcr");
|
||||
let dcr_dir = home_dir()
|
||||
.ok_or("Cannot determine home directory")?
|
||||
.join(".dcr");
|
||||
let config_path = dcr_dir.join("config.toml");
|
||||
|
||||
if !config_path.exists() {
|
||||
|
||||
@@ -12,14 +12,44 @@ pub fn bin_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
||||
pub fn lib_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
||||
match target_dir {
|
||||
Some(dir) => format!("{}/lib{}.a", dir.trim_end_matches('/'), name),
|
||||
None => format!("./target/{profile}/lib{name}.a"),
|
||||
None => {
|
||||
let arch = std::env::consts::ARCH;
|
||||
let target = format!("{arch}-unknown-linux-gnu");
|
||||
format!("./target/{target}/{profile}/lib{name}.a")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn elf_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
||||
match target_dir {
|
||||
Some(dir) => format!("{}/{}", dir.trim_end_matches('/'), name),
|
||||
None => {
|
||||
let arch = std::env::consts::ARCH;
|
||||
let target = format!("{arch}-unknown-linux-gnu");
|
||||
format!("./target/{target}/{profile}/{name}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn efi_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
||||
match target_dir {
|
||||
Some(dir) => format!("{}/{}.efi", dir.trim_end_matches('/'), name),
|
||||
None => {
|
||||
let arch = std::env::consts::ARCH;
|
||||
let target = format!("{arch}-unknown-linux-gnu");
|
||||
format!("./target/{target}/{profile}/{name}.efi")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shared_lib_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
||||
match target_dir {
|
||||
Some(dir) => format!("{}/lib{}.so", dir.trim_end_matches('/'), name),
|
||||
None => format!("./target/{profile}/lib{name}.so"),
|
||||
None => {
|
||||
let arch = std::env::consts::ARCH;
|
||||
let target = format!("{arch}-unknown-linux-gnu");
|
||||
format!("./target/{target}/{profile}/lib{name}.so")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,10 +83,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn lib_path_default() {
|
||||
assert_eq!(
|
||||
lib_path("debug", "mylib", None),
|
||||
"./target/debug/libmylib.a"
|
||||
);
|
||||
let arch = std::env::consts::ARCH;
|
||||
let expected = format!("./target/{arch}-unknown-linux-gnu/debug/libmylib.a");
|
||||
assert_eq!(lib_path("debug", "mylib", None), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -66,10 +95,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn shared_lib_path_default() {
|
||||
assert_eq!(
|
||||
shared_lib_path("debug", "mylib", None),
|
||||
"./target/debug/libmylib.so"
|
||||
);
|
||||
let arch = std::env::consts::ARCH;
|
||||
let expected = format!("./target/{arch}-unknown-linux-gnu/debug/libmylib.so");
|
||||
assert_eq!(shared_lib_path("debug", "mylib", None), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -12,6 +12,20 @@ pub fn lib_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn elf_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
||||
match target_dir {
|
||||
Some(dir) => format!("{}/{}", dir.trim_end_matches('/'), name),
|
||||
None => format!("./target/{profile}/{name}"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn efi_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
||||
match target_dir {
|
||||
Some(dir) => format!("{}/{}.efi", dir.trim_end_matches('/'), name),
|
||||
None => format!("./target/{profile}/{name}.efi"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shared_lib_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
||||
match target_dir {
|
||||
Some(dir) => format!("{}/lib{}.dylib", dir.trim_end_matches('/'), name),
|
||||
|
||||
@@ -27,6 +27,50 @@ pub fn bin_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn elf_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
linux::elf_path(profile, name, target_dir)
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
return macos::elf_path(profile, name, target_dir);
|
||||
}
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
return windows::elf_path(profile, name, target_dir);
|
||||
}
|
||||
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
|
||||
{
|
||||
match target_dir {
|
||||
Some(dir) => format!("{}/{}", dir.trim_end_matches('/'), name),
|
||||
None => format!("./target/{profile}/{name}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn efi_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
linux::efi_path(profile, name, target_dir)
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
return macos::efi_path(profile, name, target_dir);
|
||||
}
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
return windows::efi_path(profile, name, target_dir);
|
||||
}
|
||||
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
|
||||
{
|
||||
match target_dir {
|
||||
Some(dir) => format!("{}/{}.efi", dir.trim_end_matches('/'), name),
|
||||
None => format!("./target/{profile}/{name}.efi"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lib_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
|
||||
@@ -12,6 +12,20 @@ pub fn lib_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn elf_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
||||
match target_dir {
|
||||
Some(dir) => format!("{}/{}.exe", dir.trim_end_matches('/'), name),
|
||||
None => format!("./target/{profile}/{name}.exe"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn efi_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
||||
match target_dir {
|
||||
Some(dir) => format!("{}/{}.efi", dir.trim_end_matches('/'), name),
|
||||
None => format!("./target/{profile}/{name}.efi"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shared_lib_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
||||
match target_dir {
|
||||
Some(dir) => format!("{}/{}.dll", dir.trim_end_matches('/'), name),
|
||||
|
||||
Reference in New Issue
Block a user