mirror of
https://github.com/dexoron/dcr.git
synced 2026-10-21 22:29:32 +00:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 97db82e4f8 | |||
| 3d596d7280 | |||
| 5cb4473bd1 | |||
| 33f955d4e3 | |||
| 838be72dd2 | |||
| 39fad31c4f | |||
| c8ef38a99b | |||
| 8e0c535cfd | |||
| de88c160ad | |||
| 4daca925a0 | |||
| 595b7ac105 | |||
|
|
11ae3762d0 | ||
|
|
4c439317e3 | ||
| 0aa3f87f99 |
43
.github/workflows/release.yml
vendored
43
.github/workflows/release.yml
vendored
@@ -41,10 +41,6 @@ on:
|
|||||||
description: "Publish to crates.io"
|
description: "Publish to crates.io"
|
||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
default: false
|
||||||
run_snap:
|
|
||||||
description: "Publish to Snap Store (dcrup)"
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
run_brew:
|
run_brew:
|
||||||
description: "Update Homebrew tap"
|
description: "Update Homebrew tap"
|
||||||
type: boolean
|
type: boolean
|
||||||
@@ -1248,34 +1244,11 @@ jobs:
|
|||||||
git commit -m "dcr ${{ needs.setup.outputs.version }}"
|
git commit -m "dcr ${{ needs.setup.outputs.version }}"
|
||||||
git push
|
git push
|
||||||
|
|
||||||
# ── Snap Store (dcrup) ─────────────────────────────────────────────────────
|
# ── Snap Store (dcrup) — temporarily disabled ────────────────────────────
|
||||||
publish-snap:
|
# Classic confinement was denied for this project (too new).
|
||||||
if: |
|
# Snap artifact is still built and attached to the release via
|
||||||
(github.event_name == 'push' && needs.setup.outputs.prerelease == 'false') ||
|
# publish-dexoron-packages. Users can install manually:
|
||||||
(github.event_name == 'workflow_dispatch' && github.event.inputs.run_snap == 'true')
|
# sudo snap install --classic --dangerous dcrup_*.snap
|
||||||
needs: [setup, build-linux-packages]
|
#
|
||||||
runs-on: ubuntu-latest
|
# publish-snap:
|
||||||
steps:
|
# if: ...
|
||||||
- name: Download Built Packages
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: snap-package
|
|
||||||
path: dist-packages
|
|
||||||
|
|
||||||
- name: Resolve Snap File
|
|
||||||
id: snap-file
|
|
||||||
run: |
|
|
||||||
snap_file=$(ls dist-packages/*.snap 2>/dev/null | head -1)
|
|
||||||
if [ -z "$snap_file" ]; then
|
|
||||||
echo "No .snap file found in dist-packages/"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "snap=$snap_file" >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
- name: Publish to Snap Store
|
|
||||||
uses: snapcore/action-publish@v1
|
|
||||||
env:
|
|
||||||
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_STORE_LOGIN }}
|
|
||||||
with:
|
|
||||||
snap: ${{ steps.snap-file.outputs.snap }}
|
|
||||||
release: stable
|
|
||||||
71
CHANGELOG.md
71
CHANGELOG.md
@@ -1,5 +1,63 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [0.7.3] - 2026-06-13 "Оптимизация Зависимостей и VCS / Dependency Optimization & VCS"
|
||||||
|
|
||||||
|
### RU
|
||||||
|
|
||||||
|
**Добавлено:**
|
||||||
|
|
||||||
|
- **Параметр `--vcs` для команд `new` и `init`** — добавлена возможность явно указывать систему контроля версий через `--vcs <git|none>`.
|
||||||
|
- **Интеграция Git-информации в `--version`** — утилита теперь выводит текущий короткий хэш коммита и суффикс `-dirty`, если в репозитории DCR есть незакоммиченные изменения.
|
||||||
|
- **Автоматическая генерация `.gitignore`** — при инициализации Git-репозитория автоматически создается файл `.gitignore` с исключением директории `/target`.
|
||||||
|
- **Защита от вложенных репозиториев** — автоматическая инициализация Git теперь пропускается, если `dcr new` или `dcr init` запускаются внутри уже существующего Git-репозитория.
|
||||||
|
- **Тесты для верификации VCS** — добавлены интеграционные тесты `new_vcs_options_work` и `init_vcs_options_work` для проверки логики инициализации репозиториев.
|
||||||
|
|
||||||
|
**Изменено:**
|
||||||
|
|
||||||
|
- **Полный отказ от библиотеки `git2`** — работа с Git (клонирование зависимостей, инициализация VCS, проверка статуса) переведена на вызовы системного `git` через `Command`. Это облегчает сборку проекта и убирает необходимость в фичах `vendored` и `openssl`.
|
||||||
|
- **Миграция с `reqwest` на `ureq`** — в модуле обновления (`flag_update.rs`) тяжелый `reqwest` заменен на легковесный синхронный клиент `ureq 2.10`, что уменьшило оверхед и размер итогового бинарника.
|
||||||
|
|
||||||
|
### EN
|
||||||
|
|
||||||
|
**Added:**
|
||||||
|
|
||||||
|
- **`--vcs` option for `new` and `init` commands** — introduced explicit version control system selection via `--vcs <git|none>`.
|
||||||
|
- **Git metadata integration in `--version`** — the CLI now appends the current short commit hash and a `-dirty` suffix if there are uncommitted changes in the DCR repository.
|
||||||
|
- **Automatic `.gitignore` generation** — Git repository initialization now automatically writes a `.gitignore` file excluding the `/target` directory.
|
||||||
|
- **Nested repository prevention** — automatic Git initialization is now skipped if `dcr new` or `dcr init` is executed inside an already existing Git repository.
|
||||||
|
- **VCS verification tests** — added `new_vcs_options_work` and `init_vcs_options_work` integration tests to ensure reliable repository behavior.
|
||||||
|
|
||||||
|
**Changed:**
|
||||||
|
|
||||||
|
- **Complete removal of `git2` dependency** — all Git actions (dependency fetching, VCS setup, status checks) are now delegated to the system `git` executable via `Command`. This simplifies compilation and drops the need for `vendored` and `openssl` features.
|
||||||
|
- **Migrated from `reqwest` to `ureq`** — replaced the heavy `reqwest` crate in `flag_update.rs` with the lightweight synchronous `ureq 2.10` client, reducing overhead and the final binary size.
|
||||||
|
|
||||||
|
## [0.7.2] - 2026-06-07 "Исправление Таргетов и Линковки / Target & Linker Fixes"
|
||||||
|
|
||||||
|
### RU
|
||||||
|
|
||||||
|
**Добавлено:**
|
||||||
|
- Учтён `target_env` в `default_target_triple()` — Linux (gnu/musl) и Windows (msvc/gnu) теперь используют правильное окружение, а не хардкод.
|
||||||
|
|
||||||
|
**Исправлено:**
|
||||||
|
- **Сборка на macOS Apple Silicon падала с `_main` undefined** — `--target=` теперь передаётся и в ldflags, не только в cflags. `default_target_triple()` на macOS использует `std::env::consts::ARCH` вместо хардкода `x86_64`.
|
||||||
|
- **Бейджи GitHub Stars и GPL-3.0 не рендерились в README** — блок бейджей переведён на чистый HTML.
|
||||||
|
|
||||||
|
**Изменено:**
|
||||||
|
- Вычисление таргета по умолчанию вынесено в общую функцию `default_target_triple()` — устранено дублирование в build, run, clean.
|
||||||
|
|
||||||
|
### EN
|
||||||
|
|
||||||
|
**Added:**
|
||||||
|
- `default_target_triple()` now respects `target_env` — Linux (gnu/musl) and Windows (msvc/gnu) use the correct environment instead of hardcoded values.
|
||||||
|
|
||||||
|
**Fixed:**
|
||||||
|
- **macOS Apple Silicon builds failed with `_main` undefined** — `--target=` is now passed to ldflags, not just cflags. `default_target_triple()` uses `std::env::consts::ARCH` on macOS instead of hardcoded `x86_64`.
|
||||||
|
- **GitHub Stars and GPL-3.0 badges not rendering in README** — badge block converted to pure HTML.
|
||||||
|
|
||||||
|
**Changed:**
|
||||||
|
- Default target resolution extracted into shared `default_target_triple()` — removed duplication across build, run, clean.
|
||||||
|
|
||||||
## [0.7.1] - 2026-06-02 "Мультиархитектурное расширение / The Multi-Arch Expansion"
|
## [0.7.1] - 2026-06-02 "Мультиархитектурное расширение / The Multi-Arch Expansion"
|
||||||
|
|
||||||
### RU
|
### RU
|
||||||
@@ -12,7 +70,7 @@
|
|||||||
- Оптимизация сборки: `opt-level = "z"`, LTO, `codegen-units = 1`, `panic = "abort"`, `strip = true`.
|
- Оптимизация сборки: `opt-level = "z"`, LTO, `codegen-units = 1`, `panic = "abort"`, `strip = true`.
|
||||||
- Поддержка новых архитектур (i686, armv7, riscv64gc, aarch64) в рабочих процессах и установочных скриптах.
|
- Поддержка новых архитектур (i686, armv7, riscv64gc, aarch64) в рабочих процессах и установочных скриптах.
|
||||||
- Сборка пакетов `deb` и `rpm` для всех 5 архитектур Linux через `cargo-zigbuild`.
|
- Сборка пакетов `deb` и `rpm` для всех 5 архитектур Linux через `cargo-zigbuild`.
|
||||||
- Пакеты AUR (`dcr-dev`, `dcr-dev-bin`) и публикация Snap в CI.
|
- Пакеты AUR (`dcr-dev`, `dcr-dev-bin`) и сборка Snap в CI.
|
||||||
|
|
||||||
**Изменено:**
|
**Изменено:**
|
||||||
- Функция `validate_package_name` стала публичной.
|
- Функция `validate_package_name` стала публичной.
|
||||||
@@ -20,6 +78,15 @@
|
|||||||
- Инструкция в README изменена с `| bash` на `| sh` для POSIX.
|
- Инструкция в README изменена с `| bash` на `| sh` для POSIX.
|
||||||
- Отключена архитектура Windows 32-bit (GNU) из-за проблем со сборкой.
|
- Отключена архитектура Windows 32-bit (GNU) из-за проблем со сборкой.
|
||||||
|
|
||||||
|
**Исправлено:**
|
||||||
|
|
||||||
|
- **`dcr new <invalid-name>` создаёт директорию, затем падает** — валидация теперь происходит до операций с файлами.
|
||||||
|
- **`dcr init` создаёт dcr.toml, затем падает при невалидном имени директории** — то же исправление.
|
||||||
|
- **Man-страницы отсутствовали после установки** — теперь устанавливаются через скрипты установки, пакетные менеджеры и Homebrew.
|
||||||
|
- **Ложное срабатывание на букву диска в Windows** — в парсинге файлов зависимостей.
|
||||||
|
- **CRLF `\r\n` ломает парсер зависимостей** — теперь обрабатываются смешанные окончания строк.
|
||||||
|
- **Новая строка после обратного слеша не поглощалась** — в обработке escape-последовательностей `parse_d_file`.
|
||||||
|
|
||||||
### EN
|
### EN
|
||||||
|
|
||||||
**Added:**
|
**Added:**
|
||||||
@@ -30,7 +97,7 @@
|
|||||||
- Release profile optimizations (`opt-level = "z"`, LTO, `codegen-units = 1`, `panic = "abort"`, `strip = true`).
|
- Release profile optimizations (`opt-level = "z"`, LTO, `codegen-units = 1`, `panic = "abort"`, `strip = true`).
|
||||||
- Linux i686, armv7, riscv64gc, aarch64 architecture support in release workflows and installation scripts.
|
- Linux i686, armv7, riscv64gc, aarch64 architecture support in release workflows and installation scripts.
|
||||||
- Multi-arch `deb` and `rpm` package builds for all 5 Linux architectures via `cargo-zigbuild`.
|
- Multi-arch `deb` and `rpm` package builds for all 5 Linux architectures via `cargo-zigbuild`.
|
||||||
- AUR packages (`dcr-dev`, `dcr-dev-bin`) and automated Snapcraft publishing in CI.
|
- AUR packages (`dcr-dev`, `dcr-dev-bin`) and Snap build in CI.
|
||||||
|
|
||||||
**Changed:**
|
**Changed:**
|
||||||
- `validate_package_name` made public and callable from CLI commands directly.
|
- `validate_package_name` made public and callable from CLI commands directly.
|
||||||
|
|||||||
1037
Cargo.lock
generated
1037
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
19
Cargo.toml
19
Cargo.toml
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "dcr"
|
name = "dcr"
|
||||||
version = "0.7.1"
|
version = "0.7.3"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
description = "DCR is a utility for managing C/C++ projects in a Cargo-like style."
|
description = "DCR is a utility for managing C/C++ projects in a Cargo-like style."
|
||||||
@@ -61,7 +61,7 @@ assets = [
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
glob = "0.3"
|
glob = "0.3"
|
||||||
reqwest = { version = "0.12", default-features = false, features = ["blocking", "json", "rustls-tls"] }
|
ureq = { version = "2.10", default-features = false, features = ["json", "native-certs"] }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
self-replace = "1"
|
self-replace = "1"
|
||||||
sha2 = "0.10"
|
sha2 = "0.10"
|
||||||
@@ -69,21 +69,6 @@ toml = "1.0.3"
|
|||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
ctrlc = "3"
|
ctrlc = "3"
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["vendored"]
|
|
||||||
vendored = [
|
|
||||||
"git2/vendored-libgit2",
|
|
||||||
"git2/vendored-openssl",
|
|
||||||
"openssl/vendored",
|
|
||||||
]
|
|
||||||
|
|
||||||
[target.'cfg(not(windows))'.dependencies]
|
|
||||||
git2 = "0.20.4"
|
|
||||||
openssl = "0.10"
|
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
|
||||||
git2 = "0.20.4"
|
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
opt-level = "z"
|
opt-level = "z"
|
||||||
lto = "thin"
|
lto = "thin"
|
||||||
|
|||||||
38
README.md
38
README.md
@@ -1,22 +1,21 @@
|
|||||||
<div align="center">
|
<h1 align="center">DCR — Dexoron Cargo Realization</h1>
|
||||||
|
|
||||||
# DCR — Dexoron Cargo Realization
|
<p align="center"><strong>A Cargo-style build tool for C/C++ projects</strong></p>
|
||||||
|
|
||||||
**A Cargo-style build tool for C/C++ projects**
|
<p align="center">
|
||||||
|
<a href="https://github.com/dexoron/dcr/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/dexoron/dcr/actions/workflows/ci.yml/badge.svg"></a>
|
||||||
|
<a href="https://github.com/dexoron/dcr/releases/latest"><img alt="GitHub Release" src="https://img.shields.io/github/v/release/dexoron/dcr"></a>
|
||||||
|
<a href="https://github.com/dexoron/dcr/releases"><img alt="Platform" src="https://img.shields.io/badge/platform-linux%20%7C%20macos%20%7C%20windows-lightgrey"></a>
|
||||||
|
<br>
|
||||||
|
<a href="https://aur.archlinux.org/packages/dcr"><img alt="AUR" src="https://img.shields.io/aur/version/dcr"></a>
|
||||||
|
<a href="https://crates.io/crates/dcr"><img alt="Crates.io" src="https://img.shields.io/crates/v/dcr"></a>
|
||||||
|
<a href="https://github.com/dexoron/homebrew-dexoron"><img alt="Homebrew" src="https://img.shields.io/badge/homebrew-dexoron%2Fdexoron-orange"></a>
|
||||||
|
<br>
|
||||||
|
<a href="https://github.com/dexoron/dcr/stargazers"><img alt="GitHub Stars" src="https://img.shields.io/github/stars/dexoron/dcr?style=flat"></a>
|
||||||
|
<a href="LICENSE"><img alt="License: GPL-3.0" src="https://img.shields.io/badge/License-GPL--3.0-blue.svg"></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
[](https://github.com/dexoron/dcr/actions/workflows/ci.yml)
|
<!-- Snap badge removed: classic confinement was denied for this project. -->
|
||||||
[](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)
|
|
||||||
[](https://snapcraft.io/dcrup)
|
|
||||||
<br/>
|
|
||||||
[](https://github.com/dexoron/dcr/stargazers)
|
|
||||||
[](LICENSE)
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -38,7 +37,7 @@
|
|||||||
<td><b>Linux (Dexoron Packages)</b></td>
|
<td><b>Linux (Dexoron Packages)</b></td>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
See https://dcr.dexoron.su for settings youre packages manager
|
See https://pkg.dexoron.su for settings youre packages manager
|
||||||
|
|
||||||
**Arch Linux:**
|
**Arch Linux:**
|
||||||
|
|
||||||
@@ -127,8 +126,11 @@ curl -fsSL https://dcr.dexoron.su/install_bsd.sh | sh
|
|||||||
<td><b>Snap (Linux)</b></td>
|
<td><b>Snap (Linux)</b></td>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
|
> Snap Store publishing temporarily disabled — see [#51671](https://forum.snapcraft.io/t/classic-confinement-request-for-dcrup/51671).
|
||||||
|
> Download the `.snap` from [GitHub Releases](https://github.com/dexoron/dcr/releases/latest) and install manually:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
sudo snap install dcrup
|
sudo snap install --classic --dangerous dcrup_*.snap
|
||||||
```
|
```
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
33
build.rs
33
build.rs
@@ -18,4 +18,37 @@
|
|||||||
fn main() {
|
fn main() {
|
||||||
let target = std::env::var("TARGET").unwrap_or_else(|_| "unknown-target".to_string());
|
let target = std::env::var("TARGET").unwrap_or_else(|_| "unknown-target".to_string());
|
||||||
println!("cargo:rustc-env=DCR_TARGET={target}");
|
println!("cargo:rustc-env=DCR_TARGET={target}");
|
||||||
|
|
||||||
|
// Let's try to retrieve the commit hash for the version
|
||||||
|
let commit = std::process::Command::new("git")
|
||||||
|
.args(["rev-parse", "--short", "HEAD"])
|
||||||
|
.output()
|
||||||
|
.ok()
|
||||||
|
.and_then(|o| {
|
||||||
|
if o.status.success() {
|
||||||
|
String::from_utf8(o.stdout)
|
||||||
|
.ok()
|
||||||
|
.map(|s| s.trim().to_string())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check if there are any uncommitted changes in the repository
|
||||||
|
let is_dirty = std::process::Command::new("git")
|
||||||
|
.args(["status", "--porcelain"])
|
||||||
|
.output()
|
||||||
|
.ok()
|
||||||
|
.map(|o| o.status.success() && !o.stdout.is_empty())
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
if let Some(commit_hash) = commit {
|
||||||
|
let dirty_suffix = if is_dirty { "-dirty" } else { "" };
|
||||||
|
println!(
|
||||||
|
"cargo:rustc-env=DCR_GIT_INFO= (git {}{})",
|
||||||
|
commit_hash, dirty_suffix
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
println!("cargo:rustc-env=DCR_GIT_INFO=");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ sidebar_label: Installation
|
|||||||
|
|
||||||
## Linux (Dexoron Packages)
|
## Linux (Dexoron Packages)
|
||||||
|
|
||||||
Configure your package manager at https://dcr.dexoron.su and install:
|
Configure your package manager at https://pkg.dexoron.su and install:
|
||||||
|
|
||||||
**Arch Linux**
|
**Arch Linux**
|
||||||
```sh
|
```sh
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ use crate::core::config::Config;
|
|||||||
use crate::core::deps::{register, resolve_deps};
|
use crate::core::deps::{register, resolve_deps};
|
||||||
use crate::core::workspace::parse_workspace;
|
use crate::core::workspace::parse_workspace;
|
||||||
use crate::utils::build::{
|
use crate::utils::build::{
|
||||||
get_bool_with_profile, get_config_opt, get_config_str, get_language_with_profile,
|
default_target_triple, get_bool_with_profile, get_config_opt, get_config_str,
|
||||||
normalize_target_os, parse_version_info, profile_table, resolve_compiler,
|
get_language_with_profile, normalize_target_os, parse_version_info, prepend_clang_target_flag,
|
||||||
resolve_pkg_config_flags, resolve_tool,
|
profile_table, resolve_compiler, resolve_pkg_config_flags, resolve_tool,
|
||||||
};
|
};
|
||||||
use crate::utils::fs::{check_dir, find_project_root, with_dir};
|
use crate::utils::fs::{check_dir, find_project_root, with_dir};
|
||||||
use crate::utils::log::error;
|
use crate::utils::log::error;
|
||||||
@@ -100,27 +100,7 @@ pub fn build(args: &[String]) -> i32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if flags.target.is_none() {
|
if flags.target.is_none() {
|
||||||
let default_target = if cfg!(target_os = "linux") {
|
flags.target = Some(default_target_triple());
|
||||||
format!("{}-unknown-linux-gnu", std::env::consts::ARCH)
|
|
||||||
} 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 if cfg!(any(
|
|
||||||
target_os = "freebsd",
|
|
||||||
target_os = "openbsd",
|
|
||||||
target_os = "netbsd",
|
|
||||||
target_os = "dragonfly"
|
|
||||||
)) {
|
|
||||||
format!(
|
|
||||||
"{}-unknown-{}",
|
|
||||||
std::env::consts::ARCH,
|
|
||||||
std::env::consts::OS
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
"unknown".to_string()
|
|
||||||
};
|
|
||||||
flags.target = Some(default_target);
|
|
||||||
}
|
}
|
||||||
if flags.clean {
|
if flags.clean {
|
||||||
let mut clean_args = Vec::new();
|
let mut clean_args = Vec::new();
|
||||||
@@ -718,19 +698,13 @@ fn build_project_at(
|
|||||||
tc_cxx.as_deref(),
|
tc_cxx.as_deref(),
|
||||||
tc_as.as_deref(),
|
tc_as.as_deref(),
|
||||||
);
|
);
|
||||||
if let Some(t) = build_target
|
|
||||||
&& !t.trim().is_empty()
|
|
||||||
&& resolved_compiler.to_lowercase().contains("clang")
|
|
||||||
{
|
|
||||||
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 resolved_linker = resolve_tool("DCR_LD", tc_ld.as_deref());
|
let resolved_linker = resolve_tool("DCR_LD", tc_ld.as_deref());
|
||||||
|
prepend_clang_target_flag(&mut build_cflags, build_target, &resolved_compiler);
|
||||||
|
prepend_clang_target_flag(
|
||||||
|
&mut build_ldflags,
|
||||||
|
build_target,
|
||||||
|
resolved_linker.as_deref().unwrap_or(&resolved_compiler),
|
||||||
|
);
|
||||||
let resolved_archiver = resolve_tool("DCR_AR", tc_ar.as_deref());
|
let resolved_archiver = resolve_tool("DCR_AR", tc_ar.as_deref());
|
||||||
|
|
||||||
let out_dir_config = get_build_string_with_profile(&config, "out_dir", profile);
|
let out_dir_config = get_build_string_with_profile(&config, "out_dir", profile);
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
use crate::config::flags;
|
use crate::config::flags;
|
||||||
use crate::core::config::Config;
|
use crate::core::config::Config;
|
||||||
use crate::core::workspace::parse_workspace;
|
use crate::core::workspace::parse_workspace;
|
||||||
use crate::utils::build::parse_version_info;
|
use crate::utils::build::{default_target_triple, parse_version_info};
|
||||||
use crate::utils::fs::{check_dir, find_project_root, with_dir};
|
use crate::utils::fs::{check_dir, find_project_root, with_dir};
|
||||||
use crate::utils::log::{error, warn};
|
use crate::utils::log::{error, warn};
|
||||||
use crate::utils::text::{BOLD_CYAN, BOLD_GREEN, colored, printc};
|
use crate::utils::text::{BOLD_CYAN, BOLD_GREEN, colored, printc};
|
||||||
@@ -136,28 +136,7 @@ fn clean_from_root(root: &Path, flags: &CleanFlags) -> Result<(), String> {
|
|||||||
.map(|s| s.trim().to_string())
|
.map(|s| s.trim().to_string())
|
||||||
.filter(|s| !s.is_empty())
|
.filter(|s| !s.is_empty())
|
||||||
})
|
})
|
||||||
.or_else(|| {
|
.or_else(|| Some(default_target_triple()));
|
||||||
Some(if cfg!(target_os = "linux") {
|
|
||||||
format!("{}-unknown-linux-gnu", std::env::consts::ARCH)
|
|
||||||
} 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 if cfg!(any(
|
|
||||||
target_os = "freebsd",
|
|
||||||
target_os = "openbsd",
|
|
||||||
target_os = "netbsd",
|
|
||||||
target_os = "dragonfly"
|
|
||||||
)) {
|
|
||||||
format!(
|
|
||||||
"{}-unknown-{}",
|
|
||||||
std::env::consts::ARCH,
|
|
||||||
std::env::consts::OS
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
"unknown".to_string()
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
if flags.all
|
if flags.all
|
||||||
&& let Some(workspace) = parse_workspace(
|
&& let Some(workspace) = parse_workspace(
|
||||||
|
|||||||
@@ -17,9 +17,9 @@
|
|||||||
|
|
||||||
use crate::utils::log::{error, warn};
|
use crate::utils::log::{error, warn};
|
||||||
use crate::utils::text::{BOLD_CYAN, BOLD_GREEN, printc};
|
use crate::utils::text::{BOLD_CYAN, BOLD_GREEN, printc};
|
||||||
use reqwest::blocking::Client;
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
use std::io::Read;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
@@ -72,15 +72,8 @@ pub fn flag_update(args: &[String]) -> i32 {
|
|||||||
|
|
||||||
let current_version = env!("CARGO_PKG_VERSION");
|
let current_version = env!("CARGO_PKG_VERSION");
|
||||||
let target = env!("DCR_TARGET");
|
let target = env!("DCR_TARGET");
|
||||||
let client = match Client::builder().user_agent("dcr-updater").build() {
|
|
||||||
Ok(client) => client,
|
|
||||||
Err(_) => {
|
|
||||||
error("Failed to initialize HTTP client");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let release = match fetch_latest_release(&client) {
|
let release = match fetch_latest_release() {
|
||||||
Ok(release) => release,
|
Ok(release) => release,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error(&format!("Failed to check for updates: {err}"));
|
error(&format!("Failed to check for updates: {err}"));
|
||||||
@@ -104,7 +97,7 @@ pub fn flag_update(args: &[String]) -> i32 {
|
|||||||
return 1;
|
return 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
let bytes = match download_asset(&client, &asset.browser_download_url) {
|
let bytes = match download_asset(&asset.browser_download_url) {
|
||||||
Ok(bytes) => bytes,
|
Ok(bytes) => bytes,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error(&format!("Failed to download update: {err}"));
|
error(&format!("Failed to download update: {err}"));
|
||||||
@@ -131,35 +124,35 @@ pub fn flag_update(args: &[String]) -> i32 {
|
|||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch_latest_release(client: &Client) -> Result<Release, String> {
|
fn fetch_latest_release() -> Result<Release, String> {
|
||||||
let response = client
|
let response = ureq::get(LATEST_RELEASE_URL)
|
||||||
.get(LATEST_RELEASE_URL)
|
.set("User-Agent", "dcr-updater")
|
||||||
.send()
|
.call()
|
||||||
.map_err(|_| "GitHub API request failed".to_string())?;
|
.map_err(|e| match e {
|
||||||
|
ureq::Error::Status(code, _) => format!("GitHub API returned status {code}"),
|
||||||
if !response.status().is_success() {
|
ureq::Error::Transport(e) => format!("GitHub API request failed: {e}"),
|
||||||
return Err(format!("GitHub API returned status {}", response.status()));
|
})?;
|
||||||
}
|
|
||||||
|
|
||||||
response
|
response
|
||||||
.json::<Release>()
|
.into_json::<Release>()
|
||||||
.map_err(|_| "GitHub API response has an unexpected format".to_string())
|
.map_err(|_| "GitHub API response has an unexpected format".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn download_asset(client: &Client, url: &str) -> Result<Vec<u8>, String> {
|
fn download_asset(url: &str) -> Result<Vec<u8>, String> {
|
||||||
let response = client
|
let response = ureq::get(url)
|
||||||
.get(url)
|
.set("User-Agent", "dcr-updater")
|
||||||
.send()
|
.call()
|
||||||
.map_err(|_| "Download request failed".to_string())?;
|
.map_err(|e| match e {
|
||||||
|
ureq::Error::Status(code, _) => format!("Download returned status {code}"),
|
||||||
|
ureq::Error::Transport(e) => format!("Download request failed: {e}"),
|
||||||
|
})?;
|
||||||
|
|
||||||
if !response.status().is_success() {
|
let mut reader = response.into_reader();
|
||||||
return Err(format!("Download returned status {}", response.status()));
|
let mut data = Vec::new();
|
||||||
}
|
reader
|
||||||
|
.read_to_end(&mut data)
|
||||||
response
|
.map_err(|_| "Failed to read downloaded data".to_string())?;
|
||||||
.bytes()
|
Ok(data)
|
||||||
.map(|bytes| bytes.to_vec())
|
|
||||||
.map_err(|_| "Failed to read downloaded data".to_string())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn asset_candidates(target: &str) -> Vec<String> {
|
fn asset_candidates(target: &str) -> Vec<String> {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
use crate::config::FILE_MAIN_C;
|
use crate::config::FILE_MAIN_C;
|
||||||
use crate::core::config::{Config, validate_package_name};
|
use crate::core::config::{Config, validate_package_name};
|
||||||
|
use crate::core::vcs::VcsKind;
|
||||||
use crate::utils::fs::check_dir;
|
use crate::utils::fs::check_dir;
|
||||||
use crate::utils::log::{error, warn};
|
use crate::utils::log::{error, warn};
|
||||||
use crate::utils::text::{BOLD_CYAN, BOLD_GREEN, colored, printc};
|
use crate::utils::text::{BOLD_CYAN, BOLD_GREEN, colored, printc};
|
||||||
@@ -27,7 +28,7 @@ use toml::Value;
|
|||||||
pub fn init(args: &[String]) -> i32 {
|
pub fn init(args: &[String]) -> i32 {
|
||||||
if args.first().is_some_and(|a| a == "--help") {
|
if args.first().is_some_and(|a| a == "--help") {
|
||||||
printc("USAGE:", BOLD_GREEN);
|
printc("USAGE:", BOLD_GREEN);
|
||||||
printc(" dcr init", BOLD_CYAN);
|
printc(" dcr init [--vcs <git|none>]", BOLD_CYAN);
|
||||||
println!();
|
println!();
|
||||||
printc("DESCRIPTION:", BOLD_GREEN);
|
printc("DESCRIPTION:", BOLD_GREEN);
|
||||||
println!(" Initializes the current directory as a DCR project.");
|
println!(" Initializes the current directory as a DCR project.");
|
||||||
@@ -35,7 +36,25 @@ pub fn init(args: &[String]) -> i32 {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !args.is_empty() {
|
let mut vcs_str = None;
|
||||||
|
let mut clean_args = Vec::new();
|
||||||
|
let mut iter = args.iter();
|
||||||
|
while let Some(arg) = iter.next() {
|
||||||
|
if arg == "--vcs" {
|
||||||
|
if let Some(val) = iter.next() {
|
||||||
|
vcs_str = Some(val.clone());
|
||||||
|
} else {
|
||||||
|
error("--vcs requires a value");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else if let Some(stripped) = arg.strip_prefix("--vcs=") {
|
||||||
|
vcs_str = Some(stripped.to_string());
|
||||||
|
} else {
|
||||||
|
clean_args.push(arg.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !clean_args.is_empty() {
|
||||||
warn("Command does not support additional arguments");
|
warn("Command does not support additional arguments");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -106,6 +125,40 @@ pub fn init(args: &[String]) -> i32 {
|
|||||||
colored("src/main.c", BOLD_CYAN)
|
colored("src/main.c", BOLD_CYAN)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// settings VCS
|
||||||
|
let mut vcs_kind = VcsKind::Git;
|
||||||
|
if let Some(ref vcs_val) = vcs_str {
|
||||||
|
match VcsKind::parse(vcs_val) {
|
||||||
|
Ok(kind) => vcs_kind = kind,
|
||||||
|
Err(e) => {
|
||||||
|
error(&e);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let Ok(cwd_path) = std::env::current_dir()
|
||||||
|
&& crate::core::vcs::find_existing_vcs(&cwd_path).is_some()
|
||||||
|
{
|
||||||
|
vcs_kind = VcsKind::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if vcs_kind == VcsKind::Git {
|
||||||
|
if crate::utils::git::is_git_available() {
|
||||||
|
let project_path = std::path::Path::new(".");
|
||||||
|
if let Err(e) = crate::core::vcs::init_vcs(vcs_kind, project_path) {
|
||||||
|
warn(&format!("Failed to initialize git repository: {}", e));
|
||||||
|
} else {
|
||||||
|
println!(
|
||||||
|
" {} Initialized git repository",
|
||||||
|
colored("✔", BOLD_GREEN)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
warn(
|
||||||
|
"Git is not installed or not found in PATH. Skipping Git repository initialization.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"Project `{}` successfully created\n",
|
"Project `{}` successfully created\n",
|
||||||
colored(project_name.as_str(), BOLD_GREEN)
|
colored(project_name.as_str(), BOLD_GREEN)
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
use crate::config::FILE_MAIN_C;
|
use crate::config::FILE_MAIN_C;
|
||||||
use crate::core::config::{Config, validate_package_name};
|
use crate::core::config::{Config, validate_package_name};
|
||||||
|
use crate::core::vcs::VcsKind;
|
||||||
use crate::utils::fs::check_dir;
|
use crate::utils::fs::check_dir;
|
||||||
use crate::utils::log::{error, warn};
|
use crate::utils::log::{error, warn};
|
||||||
use crate::utils::text::{BOLD_CYAN, BOLD_GREEN, colored, printc};
|
use crate::utils::text::{BOLD_CYAN, BOLD_GREEN, colored, printc};
|
||||||
@@ -27,7 +28,7 @@ use toml::Value;
|
|||||||
pub fn new(args: &[String]) -> i32 {
|
pub fn new(args: &[String]) -> i32 {
|
||||||
if args.first().is_some_and(|a| a == "--help") {
|
if args.first().is_some_and(|a| a == "--help") {
|
||||||
printc("USAGE:", BOLD_GREEN);
|
printc("USAGE:", BOLD_GREEN);
|
||||||
printc(" dcr new <name>", BOLD_CYAN);
|
printc(" dcr new <name> [--vcs <git|none>]", BOLD_CYAN);
|
||||||
println!();
|
println!();
|
||||||
printc("DESCRIPTION:", BOLD_GREEN);
|
printc("DESCRIPTION:", BOLD_GREEN);
|
||||||
println!(" Creates a new C/C++ project with the given name.");
|
println!(" Creates a new C/C++ project with the given name.");
|
||||||
@@ -37,16 +38,34 @@ pub fn new(args: &[String]) -> i32 {
|
|||||||
|
|
||||||
let items = check_dir(None).unwrap_or_default();
|
let items = check_dir(None).unwrap_or_default();
|
||||||
|
|
||||||
if args.is_empty() {
|
let mut vcs_str = None;
|
||||||
|
let mut clean_args = Vec::new();
|
||||||
|
let mut iter = args.iter();
|
||||||
|
while let Some(arg) = iter.next() {
|
||||||
|
if arg == "--vcs" {
|
||||||
|
if let Some(val) = iter.next() {
|
||||||
|
vcs_str = Some(val.clone());
|
||||||
|
} else {
|
||||||
|
error("--vcs requires a value");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else if let Some(stripped) = arg.strip_prefix("--vcs=") {
|
||||||
|
vcs_str = Some(stripped.to_string());
|
||||||
|
} else {
|
||||||
|
clean_args.push(arg.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if clean_args.is_empty() {
|
||||||
error("Project name not specified");
|
error("Project name not specified");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if args.len() > 1 {
|
if clean_args.len() > 1 {
|
||||||
warn("Command does not support additional arguments");
|
warn("Command does not support additional arguments");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
let project_name = &args[0];
|
let project_name = &clean_args[0];
|
||||||
println!(
|
println!(
|
||||||
"Creating a Project `{}`...",
|
"Creating a Project `{}`...",
|
||||||
colored(project_name, BOLD_CYAN)
|
colored(project_name, BOLD_CYAN)
|
||||||
@@ -127,6 +146,40 @@ pub fn new(args: &[String]) -> i32 {
|
|||||||
colored("src/main.c", BOLD_CYAN)
|
colored("src/main.c", BOLD_CYAN)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// settings VCS
|
||||||
|
let mut vcs_kind = VcsKind::Git;
|
||||||
|
if let Some(ref vcs_val) = vcs_str {
|
||||||
|
match VcsKind::parse(vcs_val) {
|
||||||
|
Ok(kind) => vcs_kind = kind,
|
||||||
|
Err(e) => {
|
||||||
|
error(&e);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let Ok(cwd) = std::env::current_dir()
|
||||||
|
&& crate::core::vcs::find_existing_vcs(&cwd).is_some()
|
||||||
|
{
|
||||||
|
vcs_kind = VcsKind::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if vcs_kind == VcsKind::Git {
|
||||||
|
if crate::utils::git::is_git_available() {
|
||||||
|
let project_path = std::path::Path::new(project_name);
|
||||||
|
if let Err(e) = crate::core::vcs::init_vcs(vcs_kind, project_path) {
|
||||||
|
warn(&format!("Failed to initialize git repository: {}", e));
|
||||||
|
} else {
|
||||||
|
println!(
|
||||||
|
" {} Initialized git repository",
|
||||||
|
colored("✔", BOLD_GREEN)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
warn(
|
||||||
|
"Git is not installed or not found in PATH. Skipping Git repository initialization.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"Project `{}` successfully created\n",
|
"Project `{}` successfully created\n",
|
||||||
colored(project_name, BOLD_GREEN)
|
colored(project_name, BOLD_GREEN)
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ use crate::cli::build::build;
|
|||||||
use crate::cli::flags::parse_build_run_flags;
|
use crate::cli::flags::parse_build_run_flags;
|
||||||
use crate::core::config::Config;
|
use crate::core::config::Config;
|
||||||
use crate::core::runner::run_binary;
|
use crate::core::runner::run_binary;
|
||||||
use crate::utils::build::{normalize_target_os, parse_version_info};
|
use crate::utils::build::{default_target_triple, normalize_target_os, parse_version_info};
|
||||||
use crate::utils::fs::find_project_root;
|
use crate::utils::fs::find_project_root;
|
||||||
use crate::utils::fs::with_dir;
|
use crate::utils::fs::with_dir;
|
||||||
use crate::utils::log::error;
|
use crate::utils::log::error;
|
||||||
@@ -178,27 +178,7 @@ fn run_project(
|
|||||||
// If no target specified, use default host target for target-specific config
|
// If no target specified, use default host target for target-specific config
|
||||||
let mut target = flags.target.clone();
|
let mut target = flags.target.clone();
|
||||||
if target.is_none() {
|
if target.is_none() {
|
||||||
let default_target = if cfg!(target_os = "linux") {
|
target = Some(default_target_triple());
|
||||||
format!("{}-unknown-linux-gnu", std::env::consts::ARCH)
|
|
||||||
} 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 if cfg!(any(
|
|
||||||
target_os = "freebsd",
|
|
||||||
target_os = "openbsd",
|
|
||||||
target_os = "netbsd",
|
|
||||||
target_os = "dragonfly"
|
|
||||||
)) {
|
|
||||||
format!(
|
|
||||||
"{}-unknown-{}",
|
|
||||||
std::env::consts::ARCH,
|
|
||||||
std::env::consts::OS
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
"unknown".to_string()
|
|
||||||
};
|
|
||||||
target = Some(default_target);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let build_kind = config
|
let build_kind = config
|
||||||
|
|||||||
@@ -29,43 +29,5 @@ pub fn fetch_git_dep(
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut builder = git2::build::RepoBuilder::new();
|
crate::utils::git::git_clone_and_checkout(url, target_dir, branch, tag, rev)
|
||||||
if let Some(b) = branch {
|
|
||||||
builder.branch(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
let repo = builder
|
|
||||||
.clone(url, target_dir)
|
|
||||||
.map_err(|e| format!("failed to clone {}: {}", url, e))?;
|
|
||||||
|
|
||||||
if let Some(t) = tag {
|
|
||||||
checkout_ref(&repo, &format!("refs/tags/{}", t))?;
|
|
||||||
} else if let Some(r) = rev {
|
|
||||||
checkout_ref(&repo, r)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn checkout_ref(repo: &git2::Repository, reference_str: &str) -> Result<(), String> {
|
|
||||||
let (object, reference) = repo
|
|
||||||
.revparse_ext(reference_str)
|
|
||||||
.map_err(|e| format!("failed to find ref {}: {}", reference_str, e))?;
|
|
||||||
|
|
||||||
repo.checkout_tree(&object, None).map_err(|e| {
|
|
||||||
let ref_name = reference.as_ref().and_then(|r| r.name()).unwrap_or("HEAD");
|
|
||||||
format!("failed to checkout {}: {}", ref_name, e)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
match reference {
|
|
||||||
Some(gref) => {
|
|
||||||
let ref_name = gref.name().unwrap_or("HEAD");
|
|
||||||
repo.set_head(ref_name)
|
|
||||||
}
|
|
||||||
None => repo.set_head_detached(object.id()),
|
|
||||||
}
|
|
||||||
.map_err(|e| format!("failed to set HEAD: {}", e))?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,4 +20,5 @@ pub mod config;
|
|||||||
pub mod deps;
|
pub mod deps;
|
||||||
pub mod registry;
|
pub mod registry;
|
||||||
pub mod runner;
|
pub mod runner;
|
||||||
|
pub mod vcs;
|
||||||
pub mod workspace;
|
pub mod workspace;
|
||||||
|
|||||||
32
src/core/vcs/git.rs
Normal file
32
src/core/vcs/git.rs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
// DCR — Cargo-like C/C++ project manager.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2026 Dexoron (Bezotechestvo Vladimir) <main@dexoron.su>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use std::fs;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
// creating git init and .gitignore
|
||||||
|
pub fn init(path: &Path) -> Result<(), String> {
|
||||||
|
crate::utils::git::git_init(path)?;
|
||||||
|
|
||||||
|
let gitignore_path = path.join(".gitignore");
|
||||||
|
if !gitignore_path.exists() {
|
||||||
|
fs::write(&gitignore_path, "/target\n")
|
||||||
|
.map_err(|e| format!("failed to create .gitignore: {}", e))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
61
src/core/vcs/mod.rs
Normal file
61
src/core/vcs/mod.rs
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
// DCR — Cargo-like C/C++ project manager.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2026 Dexoron (Bezotechestvo Vladimir) <main@dexoron.su>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
pub mod git;
|
||||||
|
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum VcsKind {
|
||||||
|
Git,
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VcsKind {
|
||||||
|
pub fn parse(s: &str) -> Result<Self, String> {
|
||||||
|
match s.to_lowercase().as_str() {
|
||||||
|
"git" => Ok(Self::Git),
|
||||||
|
"none" => Ok(Self::None),
|
||||||
|
_ => Err(format!("Unknown VCS: {}", s)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_vcs(kind: VcsKind, path: &Path) -> Result<(), String> {
|
||||||
|
match kind {
|
||||||
|
VcsKind::Git => git::init(path),
|
||||||
|
VcsKind::None => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// look for git repo in parents to avoid nested repos
|
||||||
|
pub fn find_existing_vcs(path: &Path) -> Option<VcsKind> {
|
||||||
|
let mut current = if path.is_file() {
|
||||||
|
path.parent()
|
||||||
|
} else {
|
||||||
|
Some(path)
|
||||||
|
};
|
||||||
|
|
||||||
|
while let Some(dir) = current {
|
||||||
|
if dir.join(".git").exists() {
|
||||||
|
return Some(VcsKind::Git);
|
||||||
|
}
|
||||||
|
current = dir.parent();
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
@@ -44,7 +44,12 @@ fn main() {
|
|||||||
"clean" => cli::clean::clean(rest),
|
"clean" => cli::clean::clean(rest),
|
||||||
"gen" => cli::r#gen::r#gen(rest),
|
"gen" => cli::r#gen::r#gen(rest),
|
||||||
"--version" => {
|
"--version" => {
|
||||||
println!("dcr {} ({})", env!("CARGO_PKG_VERSION"), env!("DCR_TARGET"));
|
println!(
|
||||||
|
"dcr {}{} ({})",
|
||||||
|
env!("CARGO_PKG_VERSION"),
|
||||||
|
env!("DCR_GIT_INFO"),
|
||||||
|
env!("DCR_TARGET")
|
||||||
|
);
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
"--help" => cli::help::help(),
|
"--help" => cli::help::help(),
|
||||||
|
|||||||
@@ -72,6 +72,53 @@ pub fn normalize_target_os(target: &str) -> &str {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn default_target_triple() -> String {
|
||||||
|
let arch = std::env::consts::ARCH;
|
||||||
|
if cfg!(target_os = "linux") {
|
||||||
|
let env = if cfg!(target_env = "musl") {
|
||||||
|
"musl"
|
||||||
|
} else {
|
||||||
|
"gnu"
|
||||||
|
};
|
||||||
|
format!("{arch}-unknown-linux-{env}")
|
||||||
|
} else if cfg!(target_os = "macos") {
|
||||||
|
format!("{arch}-apple-darwin")
|
||||||
|
} else if cfg!(target_os = "windows") {
|
||||||
|
let env = if cfg!(target_env = "gnu") {
|
||||||
|
"gnu"
|
||||||
|
} else {
|
||||||
|
"msvc"
|
||||||
|
};
|
||||||
|
format!("{arch}-pc-windows-{env}")
|
||||||
|
} else if cfg!(any(
|
||||||
|
target_os = "freebsd",
|
||||||
|
target_os = "openbsd",
|
||||||
|
target_os = "netbsd",
|
||||||
|
target_os = "dragonfly"
|
||||||
|
)) {
|
||||||
|
format!("{arch}-unknown-{}", std::env::consts::OS)
|
||||||
|
} else {
|
||||||
|
"unknown".to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prepend_clang_target_flag(flags: &mut Vec<String>, target: Option<&str>, tool: &str) {
|
||||||
|
let Some(target) = target.map(str::trim).filter(|t| !t.is_empty()) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if !tool.to_lowercase().contains("clang") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let target_flag = format!("--target={target}");
|
||||||
|
if !flags
|
||||||
|
.iter()
|
||||||
|
.any(|f| f == &target_flag || f.starts_with("--target="))
|
||||||
|
{
|
||||||
|
flags.insert(0, target_flag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_config_str(config: &Config, key: &str) -> String {
|
pub fn get_config_str(config: &Config, key: &str) -> String {
|
||||||
config
|
config
|
||||||
.get(key)
|
.get(key)
|
||||||
@@ -380,6 +427,61 @@ mod tests {
|
|||||||
assert_eq!(normalize_target_os("unknown"), "unknown");
|
assert_eq!(normalize_target_os("unknown"), "unknown");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn default_target_triple_uses_host_arch() {
|
||||||
|
let target = default_target_triple();
|
||||||
|
if cfg!(target_os = "linux") {
|
||||||
|
assert_eq!(
|
||||||
|
target,
|
||||||
|
format!("{}-unknown-linux-gnu", std::env::consts::ARCH)
|
||||||
|
);
|
||||||
|
} else if cfg!(target_os = "macos") {
|
||||||
|
assert_eq!(target, format!("{}-apple-darwin", std::env::consts::ARCH));
|
||||||
|
} else if cfg!(target_os = "windows") {
|
||||||
|
assert_eq!(
|
||||||
|
target,
|
||||||
|
format!("{}-pc-windows-msvc", std::env::consts::ARCH)
|
||||||
|
);
|
||||||
|
} else if cfg!(any(
|
||||||
|
target_os = "freebsd",
|
||||||
|
target_os = "openbsd",
|
||||||
|
target_os = "netbsd",
|
||||||
|
target_os = "dragonfly"
|
||||||
|
)) {
|
||||||
|
assert_eq!(
|
||||||
|
target,
|
||||||
|
format!(
|
||||||
|
"{}-unknown-{}",
|
||||||
|
std::env::consts::ARCH,
|
||||||
|
std::env::consts::OS
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
assert_eq!(target, "unknown");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn prepend_clang_target_flag_adds_once_for_clang() {
|
||||||
|
let mut flags = vec!["-O2".to_string()];
|
||||||
|
prepend_clang_target_flag(&mut flags, Some("aarch64-apple-darwin"), "clang");
|
||||||
|
prepend_clang_target_flag(&mut flags, Some("aarch64-apple-darwin"), "clang");
|
||||||
|
assert_eq!(
|
||||||
|
flags,
|
||||||
|
vec![
|
||||||
|
"--target=aarch64-apple-darwin".to_string(),
|
||||||
|
"-O2".to_string()
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn prepend_clang_target_flag_ignores_non_clang_tools() {
|
||||||
|
let mut flags = vec!["-O2".to_string()];
|
||||||
|
prepend_clang_target_flag(&mut flags, Some("aarch64-apple-darwin"), "gcc");
|
||||||
|
assert_eq!(flags, vec!["-O2".to_string()]);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_version_parts() {
|
fn parse_version_parts() {
|
||||||
let info = parse_version_info("1.2.3-beta");
|
let info = parse_version_info("1.2.3-beta");
|
||||||
|
|||||||
112
src/utils/git.rs
Normal file
112
src/utils/git.rs
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
// DCR — Cargo-like C/C++ project manager.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2026 Dexoron (Bezotechestvo Vladimir) <main@dexoron.su>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use std::path::Path;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
// checking installed git on system
|
||||||
|
pub fn is_git_available() -> bool {
|
||||||
|
Command::new("git")
|
||||||
|
.arg("--version")
|
||||||
|
.output()
|
||||||
|
.map(|o| o.status.success())
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// warning of git not in system
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn warn_if_git_missing() {
|
||||||
|
if !is_git_available() {
|
||||||
|
crate::utils::log::warn(
|
||||||
|
"Git is not installed or not found in PATH. Some features (like dependency cloning and VCS initialization) may fail.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// init repo in folder
|
||||||
|
pub fn git_init(path: &Path) -> Result<(), String> {
|
||||||
|
if !is_git_available() {
|
||||||
|
return Err("git executable not found in PATH".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
let status = Command::new("git")
|
||||||
|
.arg("init")
|
||||||
|
.current_dir(path)
|
||||||
|
.status()
|
||||||
|
.map_err(|e| format!("failed to execute git init: {}", e))?;
|
||||||
|
|
||||||
|
if !status.success() {
|
||||||
|
return Err("git init failed".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone the dependency and switch to the branch/tag/commit
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn git_clone_and_checkout(
|
||||||
|
url: &str,
|
||||||
|
target_dir: &Path,
|
||||||
|
branch: Option<&str>,
|
||||||
|
tag: Option<&str>,
|
||||||
|
rev: Option<&str>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
if !is_git_available() {
|
||||||
|
return Err("git executable not found in PATH".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut args = vec!["clone"];
|
||||||
|
|
||||||
|
if let Some(b) = branch {
|
||||||
|
args.push("--branch");
|
||||||
|
args.push(b);
|
||||||
|
args.push("--single-branch");
|
||||||
|
} else if let Some(t) = tag {
|
||||||
|
args.push("--branch");
|
||||||
|
args.push(t);
|
||||||
|
args.push("--single-branch");
|
||||||
|
}
|
||||||
|
|
||||||
|
args.push(url);
|
||||||
|
|
||||||
|
let target_dir_str = target_dir.to_string_lossy().to_string();
|
||||||
|
args.push(&target_dir_str);
|
||||||
|
|
||||||
|
let status = Command::new("git")
|
||||||
|
.args(&args)
|
||||||
|
.status()
|
||||||
|
.map_err(|e| format!("failed to execute git clone: {}", e))?;
|
||||||
|
|
||||||
|
if !status.success() {
|
||||||
|
return Err(format!("git clone of {} failed", url));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(r) = rev {
|
||||||
|
let checkout_status = Command::new("git")
|
||||||
|
.arg("checkout")
|
||||||
|
.arg(r)
|
||||||
|
.current_dir(target_dir)
|
||||||
|
.status()
|
||||||
|
.map_err(|e| format!("failed to execute git checkout: {}", e))?;
|
||||||
|
|
||||||
|
if !checkout_status.success() {
|
||||||
|
return Err(format!("git checkout to revision {} failed", r));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -17,5 +17,6 @@
|
|||||||
|
|
||||||
pub mod build;
|
pub mod build;
|
||||||
pub mod fs;
|
pub mod fs;
|
||||||
|
pub mod git;
|
||||||
pub mod log;
|
pub mod log;
|
||||||
pub mod text;
|
pub mod text;
|
||||||
|
|||||||
@@ -99,6 +99,60 @@ fn new_creates_project_layout() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_git_in_path() -> bool {
|
||||||
|
std::process::Command::new("git")
|
||||||
|
.arg("--version")
|
||||||
|
.output()
|
||||||
|
.map(|o| o.status.success())
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn new_vcs_options_work() {
|
||||||
|
// 1. Test --vcs none
|
||||||
|
let dir = unique_sandbox_dir("new_vcs_none");
|
||||||
|
let out = run_dcr(&["new", "hello_none", "--vcs", "none"], &dir);
|
||||||
|
assert!(out.status.success(), "dcr new --vcs none should succeed");
|
||||||
|
let project_dir = dir.join("hello_none");
|
||||||
|
assert!(project_dir.is_dir());
|
||||||
|
assert!(!project_dir.join(".git").exists());
|
||||||
|
assert!(!project_dir.join(".gitignore").exists());
|
||||||
|
|
||||||
|
// 2. Test --vcs git
|
||||||
|
if is_git_in_path() {
|
||||||
|
let dir = unique_sandbox_dir("new_vcs_git");
|
||||||
|
let out = run_dcr(&["new", "hello_git", "--vcs", "git"], &dir);
|
||||||
|
assert!(out.status.success(), "dcr new --vcs git should succeed");
|
||||||
|
let project_dir = dir.join("hello_git");
|
||||||
|
assert!(project_dir.is_dir());
|
||||||
|
assert!(project_dir.join(".git").is_dir());
|
||||||
|
assert!(project_dir.join(".gitignore").is_file());
|
||||||
|
let gitignore = std::fs::read_to_string(project_dir.join(".gitignore")).unwrap();
|
||||||
|
assert!(gitignore.contains("/target"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn init_vcs_options_work() {
|
||||||
|
// 1. Test --vcs none
|
||||||
|
let dir = unique_sandbox_dir("init_vcs_none");
|
||||||
|
let out = run_dcr(&["init", "--vcs", "none"], &dir);
|
||||||
|
assert!(out.status.success(), "dcr init --vcs none should succeed");
|
||||||
|
assert!(!dir.join(".git").exists());
|
||||||
|
assert!(!dir.join(".gitignore").exists());
|
||||||
|
|
||||||
|
// 2. Test --vcs git
|
||||||
|
if is_git_in_path() {
|
||||||
|
let dir = unique_sandbox_dir("init_vcs_git");
|
||||||
|
let out = run_dcr(&["init", "--vcs", "git"], &dir);
|
||||||
|
assert!(out.status.success(), "dcr init --vcs git should succeed");
|
||||||
|
assert!(dir.join(".git").is_dir());
|
||||||
|
assert!(dir.join(".gitignore").is_file());
|
||||||
|
let gitignore = std::fs::read_to_string(dir.join(".gitignore")).unwrap();
|
||||||
|
assert!(gitignore.contains("/target"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn init_and_clean_remove_target() {
|
fn init_and_clean_remove_target() {
|
||||||
let dir = unique_sandbox_dir("init");
|
let dir = unique_sandbox_dir("init");
|
||||||
|
|||||||
Reference in New Issue
Block a user