mirror of
https://github.com/dexoron/dcr.git
synced 2026-10-21 18:29:35 +00:00
Compare commits
22 Commits
v0.6.9
...
0587965283
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0587965283 | ||
|
|
478d1ad0c5 | ||
|
|
060b92111f | ||
|
|
86f0566dc2 | ||
|
|
768e2fc0dc | ||
|
|
619493bcbd | ||
|
|
1dac8661b6 | ||
|
|
80a6d574a3 | ||
|
|
daf465368f | ||
|
|
800fe38fba | ||
|
|
a7f9b79b93 | ||
|
|
b03660a9d7 | ||
|
|
2cb02aa102 | ||
|
|
06a8458ca7 | ||
| c2ef5483a0 | |||
| eb24d616c5 | |||
| 4f76f54b63 | |||
| 37b9b855ec | |||
| 8f0f67ad62 | |||
| 49945aa65d | |||
| 83593aa592 | |||
| 2835188da9 |
116
.github/workflows/dexoron-packages-index.yml
vendored
116
.github/workflows/dexoron-packages-index.yml
vendored
@@ -2,6 +2,11 @@ name: Dexoron Packages Index
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: "Версия (тег) для ручного импорта пакетов (например, v0.6.9)"
|
||||
required: true
|
||||
type: string
|
||||
workflow_run:
|
||||
workflows: ["Release"]
|
||||
types:
|
||||
@@ -13,7 +18,7 @@ permissions:
|
||||
jobs:
|
||||
update-packages-index:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
|
||||
if: ${{ github.event_name == 'workflow_dispatch' || github.event_workflow_run.conclusion == 'success' }}
|
||||
|
||||
steps:
|
||||
- name: Checkout Central Packages Repo
|
||||
@@ -23,105 +28,82 @@ jobs:
|
||||
token: ${{ secrets.PACKAGES_REPO_TOKEN }}
|
||||
path: 'central-packages'
|
||||
|
||||
# --- СКАЧИВАНИЕ АРТЕФАКТОВ ИЗ РЕЛИЗА ---
|
||||
- name: Import GPG Key
|
||||
uses: crazy-max/ghaction-import-gpg@v6
|
||||
with:
|
||||
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
|
||||
- name: Download Arch package artifact
|
||||
# --- Загрузка артефактов (сохранена прежняя логика) ---
|
||||
- name: Download Artifacts
|
||||
if: ${{ github.event_name == 'workflow_run' }}
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: arch-package
|
||||
path: central-packages/archlinux/x86_64/
|
||||
github-token: ${{ secrets.PACKAGES_REPO_TOKEN }}
|
||||
path: central-packages/
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
continue-on-error: true
|
||||
|
||||
- name: Download Debian package artifact
|
||||
if: ${{ github.event_name == 'workflow_run' }}
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: debian-package
|
||||
path: central-packages/debian/
|
||||
github-token: ${{ secrets.PACKAGES_REPO_TOKEN }}
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
continue-on-error: true
|
||||
- name: Download Packages from Release Assets
|
||||
if: ${{ github.event_name == 'workflow_dispatch' }}
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
TAG: ${{ inputs.tag }}
|
||||
run: |
|
||||
gh release download "$TAG" --repo dexoron/dcr --pattern "*.pkg.tar.zst" --dir central-packages/archlinux/x86_64/ --clobber
|
||||
gh release download "$TAG" --repo dexoron/dcr --pattern "*.deb" --dir central-packages/debian/ --clobber
|
||||
gh release download "$TAG" --repo dexoron/dcr --pattern "*.rpm" --dir central-packages/fedora/x86_64/ --clobber
|
||||
|
||||
- name: Download RPM package artifact
|
||||
if: ${{ github.event_name == 'workflow_run' }}
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: rpm-package
|
||||
path: central-packages/rpm/x86_64/
|
||||
github-token: ${{ secrets.PACKAGES_REPO_TOKEN }}
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
continue-on-error: true
|
||||
# --- ИНДЕКСАЦИЯ И ПОДПИСЬ ---
|
||||
|
||||
# --- ИНДЕКСАЦИЯ REPO ---
|
||||
|
||||
- name: Update Pacman Database (Inside Arch Container)
|
||||
- name: Update Pacman Database
|
||||
run: |
|
||||
docker run --rm \
|
||||
-v ${{ github.workspace }}/central-packages:/packages \
|
||||
-v ~/.gnupg:/root/.gnupg \
|
||||
archlinux:latest \
|
||||
bash -c '
|
||||
cd /packages/archlinux/x86_64 && \
|
||||
rm -f dexoron.db dexoron.files && \
|
||||
shopt -s nullglob
|
||||
packages=(*.pkg.tar.zst)
|
||||
if [ ${#packages[@]} -gt 0 ]; then
|
||||
echo "Found ${#packages[@]} Arch packages. Updating database..."
|
||||
repo-add dexoron.db.tar.gz "${packages[@]}" && \
|
||||
packages=(*.pkg.tar.zst) && \
|
||||
repo-add --sign dexoron.db.tar.gz "${packages[@]}" && \
|
||||
ln -sf dexoron.db.tar.gz dexoron.db && \
|
||||
ln -sf dexoron.files.tar.gz dexoron.files
|
||||
else
|
||||
echo "No .pkg.tar.zst files found. Skipping."
|
||||
fi
|
||||
'
|
||||
- name: Fix GPG permissions after Docker
|
||||
run: sudo chown -R runner:runner ~/.gnupg
|
||||
|
||||
- name: Update Debian APT Repository Index
|
||||
run: |
|
||||
sudo apt-get update && sudo apt-get install -y dpkg-dev
|
||||
cd central-packages/debian
|
||||
|
||||
shopt -s nullglob
|
||||
deb_packages=(*.deb)
|
||||
|
||||
if [ ${#deb_packages[@]} -gt 0 ]; then
|
||||
echo "Found ${#deb_packages[@]} Debian packages. Generating Packages.gz..."
|
||||
# Генерируем индекс для плоского (flat) репозитория
|
||||
dpkg-scanpackages . /dev/null | gzip -9c > Packages.gz
|
||||
else
|
||||
echo "No .deb files found. Skipping Debian index update."
|
||||
fi
|
||||
|
||||
echo "Origin: Dexoron Packages" > Release
|
||||
echo "Label: Dexoron Packages" >> Release
|
||||
echo "Suite: stable" >> Release
|
||||
echo "Codename: stable" >> Release
|
||||
echo "Architectures: amd64" >> Release
|
||||
echo "Components: main" >> Release
|
||||
echo "Description: Dexoron binary package repository" >> Release
|
||||
echo "Date: $(date -uR)" >> Release
|
||||
echo "SHA256:" >> Release
|
||||
echo " $(sha256sum Packages.gz | awk '{print $1" "$2}')" >> Release
|
||||
|
||||
gpg --batch --yes --pinentry-mode loopback --local-user 74322D39483E9C9E8C86C0AA0770A5C2A2A82DAC --detach-sign --armor --output Release.gpg Release
|
||||
gpg --batch --yes --pinentry-mode loopback --local-user 74322D39483E9C9E8C86C0AA0770A5C2A2A82DAC --clearsign --output InRelease Release
|
||||
|
||||
- name: Update Fedora DNF Repository Index
|
||||
run: |
|
||||
sudo apt-get update && sudo apt-get install -y createrepo-c
|
||||
cd central-packages/rpm/x86_64
|
||||
|
||||
shopt -s nullglob
|
||||
rpm_packages=(*.rpm)
|
||||
|
||||
if [ ${#rpm_packages[@]} -gt 0 ]; then
|
||||
echo "Found ${#rpm_packages[@]} RPM packages. Generating repodata..."
|
||||
# Пересоздаем метаданные для dnf
|
||||
cd central-packages/fedora/x86_64
|
||||
createrepo_c .
|
||||
else
|
||||
echo "No .rpm files found. Skipping Fedora index update."
|
||||
fi
|
||||
|
||||
# --- ПУШ ИЗМЕНЕНИЙ ---
|
||||
gpg --batch --yes --detach-sign --armor --output repodata/repomd.xml.asc repodata/repomd.xml
|
||||
|
||||
- name: Push to dexoron/packages
|
||||
run: |
|
||||
cd central-packages
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
# Проверяем изменения во всех трех целевых папках
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
git add archlinux/ debian/ rpm/
|
||||
git commit -m "chore: update repositories index (arch, debian, rpm)"
|
||||
git add .
|
||||
if ! git diff --cached --quiet; then
|
||||
git commit -m "chore: update and sign repositories"
|
||||
git push
|
||||
else
|
||||
echo "No changes detected in repository databases."
|
||||
fi
|
||||
76
.github/workflows/release.yml
vendored
76
.github/workflows/release.yml
vendored
@@ -100,10 +100,28 @@ jobs:
|
||||
name: release-notes
|
||||
path: .github/release-notes.md
|
||||
|
||||
# ── Create Release (prevent race conditions) ───────────────────────────────
|
||||
create-release:
|
||||
needs: [setup, notes]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download Release Notes
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: release-notes
|
||||
path: .github
|
||||
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: ${{ needs.setup.outputs.tag }}
|
||||
body_path: .github/release-notes.md
|
||||
prerelease: ${{ needs.setup.outputs.prerelease == 'true' }}
|
||||
|
||||
# ── Linux Binaries Matrix ──────────────────────────────────────────────────
|
||||
build-linux:
|
||||
if: ${{ github.event_name == 'push' || github.event.inputs.run_linux == 'true' }}
|
||||
needs: [setup, notes]
|
||||
needs: [setup, notes, create-release]
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -129,6 +147,12 @@ jobs:
|
||||
pip install cargo-zigbuild
|
||||
echo "$HOME/.local/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Setup Zig
|
||||
if: matrix.target != 'x86_64-unknown-linux-gnu'
|
||||
uses: mlugg/setup-zig@v1
|
||||
with:
|
||||
version: 0.13.0
|
||||
|
||||
- name: Setup Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -222,14 +246,15 @@ jobs:
|
||||
- name: Build Arch Package inside Container
|
||||
if: matrix.package_type == 'arch'
|
||||
run: |
|
||||
PKGVER='${{ needs.setup.outputs.version }}'
|
||||
PKGVER_ORIG='${{ needs.setup.outputs.version }}'
|
||||
PKGVER_CLEAN="${PKGVER_ORIG//-/.}"
|
||||
TAG='${{ needs.setup.outputs.tag }}'
|
||||
SRC_URL="https://github.com/dexoron/dcr/archive/refs/tags/${TAG}.tar.gz"
|
||||
SHA256="$(curl -fsSL "$SRC_URL" | sha256sum | awk '{print $1}')"
|
||||
mkdir -p arch-build
|
||||
cat > arch-build/PKGBUILD <<PKG
|
||||
pkgname=dcr
|
||||
pkgver=${PKGVER}
|
||||
pkgver=${PKGVER_CLEAN}
|
||||
pkgrel=1
|
||||
pkgdesc="Cargo-like utility to manage C/C++ projects"
|
||||
arch=('x86_64')
|
||||
@@ -238,14 +263,14 @@ jobs:
|
||||
depends=('gcc-libs')
|
||||
makedepends=('cargo')
|
||||
options=('!lto')
|
||||
source=("\$pkgname-\$pkgver.tar.gz::\$url/archive/refs/tags/v\$pkgver.tar.gz")
|
||||
source=("\$pkgname-\$pkgver.tar.gz::\$url/archive/refs/tags/${TAG}.tar.gz")
|
||||
sha256sums=('${SHA256}')
|
||||
build() {
|
||||
cd "\$srcdir/\$pkgname-\$pkgver"
|
||||
cd "\$srcdir/\$pkgname-${PKGVER_ORIG}"
|
||||
cargo build --release --locked
|
||||
}
|
||||
package() {
|
||||
cd "\$srcdir/\$pkgname-\$pkgver"
|
||||
cd "\$srcdir/\$pkgname-${PKGVER_ORIG}"
|
||||
install -Dm755 "target/release/dcr" "\$pkgdir/usr/bin/dcr"
|
||||
install -Dm644 LICENSE "\$pkgdir/usr/share/licenses/\$pkgname/LICENSE"
|
||||
}
|
||||
@@ -273,7 +298,7 @@ jobs:
|
||||
|
||||
# ── Publish Packages to Release (Out of Matrix) ────────────────────────────
|
||||
publish-dexoron-packages:
|
||||
needs: [setup, notes, build-linux-packages]
|
||||
needs: [setup, notes, build-linux-packages, create-release]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download Release Notes
|
||||
@@ -302,7 +327,7 @@ jobs:
|
||||
# ── macOS Matrix ───────────────────────────────────────────────────────────
|
||||
build-macos:
|
||||
if: ${{ github.event_name == 'push' || github.event.inputs.run_macos == 'true' }}
|
||||
needs: [setup, notes]
|
||||
needs: [setup, notes, create-release]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -348,7 +373,7 @@ jobs:
|
||||
# ── BSD Systems Matrix ─────────────────────────────────────────────────────
|
||||
build-bsd:
|
||||
if: ${{ github.event_name == 'push' || github.event.inputs.run_bsd == 'true' }}
|
||||
needs: [setup, notes]
|
||||
needs: [setup, notes, create-release]
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -408,9 +433,10 @@ jobs:
|
||||
with:
|
||||
usesh: true
|
||||
run: |
|
||||
export PATH="/usr/pkg/bin:/usr/pkg/sbin:$PATH"
|
||||
pkg_add rust perl gmake
|
||||
export MAKE=gmake
|
||||
mkdir -p /tmp/bin
|
||||
ln -sf /usr/pkg/bin/gmake /tmp/bin/make
|
||||
export PATH="/tmp/bin:/usr/pkg/bin:/usr/pkg/sbin:$PATH"
|
||||
cargo build --release
|
||||
|
||||
- name: Prepare NetBSD Artifact
|
||||
@@ -420,6 +446,8 @@ jobs:
|
||||
cp target/release/dcr "$ASSET"
|
||||
echo "BSD_ASSET=${ASSET}" >> "$GITHUB_ENV"
|
||||
|
||||
|
||||
|
||||
# --- UPLOAD TARGET ---
|
||||
- name: Upload BSD Asset to Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
@@ -432,7 +460,7 @@ jobs:
|
||||
# ── Windows Matrix ─────────────────────────────────────────────────────────
|
||||
build-windows:
|
||||
if: ${{ github.event_name == 'push' || github.event.inputs.run_windows == 'true' }}
|
||||
needs: [setup, notes]
|
||||
needs: [setup, notes, create-release]
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -499,14 +527,15 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
PKGVER='${{ needs.setup.outputs.version }}'
|
||||
PKGVER_ORIG='${{ needs.setup.outputs.version }}'
|
||||
PKGVER_CLEAN="${PKGVER_ORIG//-/.}"
|
||||
SRC_URL='${{ steps.meta.outputs.source_url }}'
|
||||
SHA256='${{ steps.meta.outputs.sha256 }}'
|
||||
|
||||
mkdir -p packages/aur/dcr
|
||||
cat > packages/aur/dcr/PKGBUILD <<PKG
|
||||
pkgname=dcr
|
||||
pkgver=${PKGVER}
|
||||
pkgver=${PKGVER_CLEAN}
|
||||
pkgrel=1
|
||||
pkgdesc="Cargo-like utility to manage C/C++ projects"
|
||||
arch=('x86_64' 'aarch64')
|
||||
@@ -519,11 +548,11 @@ jobs:
|
||||
'gcc: build C/C++ projects with GCC'
|
||||
'clang: build C/C++ projects with Clang'
|
||||
)
|
||||
source=("\$pkgname-\$pkgver.tar.gz::\$url/archive/refs/tags/v\$pkgver.tar.gz")
|
||||
source=("\$pkgname-\$pkgver.tar.gz::\${SRC_URL}")
|
||||
sha256sums=('${SHA256}')
|
||||
|
||||
build() {
|
||||
cd "\$srcdir/\$pkgname-\$pkgver"
|
||||
cd "\$srcdir/\$pkgname-${PKGVER_ORIG}"
|
||||
export CARGO_PROFILE_RELEASE_LTO=false
|
||||
export RUSTFLAGS="\${RUSTFLAGS:-}"
|
||||
export RUSTFLAGS="\${RUSTFLAGS/-C link-arg=-fuse-ld=lld/}"
|
||||
@@ -531,7 +560,7 @@ jobs:
|
||||
}
|
||||
|
||||
package() {
|
||||
cd "\$srcdir/\$pkgname-\$pkgver"
|
||||
cd "\$srcdir/\$pkgname-${PKGVER_ORIG}"
|
||||
install -Dm755 "target/release/dcr" "\$pkgdir/usr/bin/dcr"
|
||||
install -Dm644 LICENSE "\$pkgdir/usr/share/licenses/\$pkgname/LICENSE"
|
||||
}
|
||||
@@ -540,7 +569,7 @@ jobs:
|
||||
cat > packages/aur/dcr/.SRCINFO <<SRC
|
||||
pkgbase = dcr
|
||||
pkgdesc = Cargo-like utility to manage C/C++ projects
|
||||
pkgver = ${PKGVER}
|
||||
pkgver = ${PKGVER_CLEAN}
|
||||
pkgrel = 1
|
||||
url = https://github.com/dexoron/dcr
|
||||
arch = x86_64
|
||||
@@ -551,7 +580,7 @@ jobs:
|
||||
optdepends = gcc: build C/C++ projects with GCC
|
||||
optdepends = clang: build C/C++ projects with Clang
|
||||
options = !lto
|
||||
source = dcr-${PKGVER}.tar.gz::${SRC_URL}
|
||||
source = dcr-${PKGVER_CLEAN}.tar.gz::${SRC_URL}
|
||||
sha256sums = ${SHA256}
|
||||
|
||||
pkgname = dcr
|
||||
@@ -611,14 +640,15 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
PKGVER='${{ needs.setup.outputs.version }}'
|
||||
PKGVER_ORIG='${{ needs.setup.outputs.version }}'
|
||||
PKGVER_CLEAN="${PKGVER_ORIG//-/.}"
|
||||
BIN_URL='${{ steps.meta.outputs.bin_url }}'
|
||||
SHA256='${{ steps.meta.outputs.sha256 }}'
|
||||
|
||||
mkdir -p packages/aur/dcr-bin
|
||||
cat > packages/aur/dcr-bin/PKGBUILD <<PKG
|
||||
pkgname=dcr-bin
|
||||
pkgver=${PKGVER}
|
||||
pkgver=${PKGVER_CLEAN}
|
||||
pkgrel=1
|
||||
pkgdesc="Cargo-like utility to manage C/C++ projects (pre-compiled musl binary)"
|
||||
arch=('x86_64')
|
||||
@@ -641,7 +671,7 @@ jobs:
|
||||
cat > packages/aur/dcr-bin/.SRCINFO <<SRC
|
||||
pkgbase = dcr-bin
|
||||
pkgdesc = Cargo-like utility to manage C/C++ projects (pre-compiled musl binary)
|
||||
pkgver = ${PKGVER}
|
||||
pkgver = ${PKGVER_CLEAN}
|
||||
pkgrel = 1
|
||||
url = https://github.com/dexoron/dcr
|
||||
arch = x86_64
|
||||
@@ -650,7 +680,7 @@ jobs:
|
||||
conflicts = dcr
|
||||
optdepends = gcc: build C/C++ projects with GCC
|
||||
optdepends = clang: build C/C++ projects with Clang
|
||||
source_x86_64 = dcr-${PKGVER}::${BIN_URL}
|
||||
source_x86_64 = dcr-${PKGVER_CLEAN}::${BIN_URL}
|
||||
sha256sums_x86_64 = ${SHA256}
|
||||
|
||||
pkgname = dcr-bin
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
## [0.7.0] - 2026-05-28
|
||||
|
||||
Added:
|
||||
|
||||
- **FreeBSD, OpenBSD, NetBSD, DragonflyBSD target support** — full platform routing and dynamically generated target triples.
|
||||
|
||||
## [0.6.9] - 2026-05-28
|
||||
|
||||
Added:
|
||||
|
||||
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -112,7 +112,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "dcr"
|
||||
version = "0.6.9"
|
||||
version = "0.7.0"
|
||||
dependencies = [
|
||||
"ctrlc",
|
||||
"git2",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "dcr"
|
||||
version = "0.6.9"
|
||||
version = "0.7.0"
|
||||
edition = "2024"
|
||||
build = "build.rs"
|
||||
description = "DCR is a utility for managing C/C++ projects in a Cargo-like style."
|
||||
@@ -42,7 +42,10 @@ sha2 = "0.10"
|
||||
toml = "1.0.3"
|
||||
serde_json = "1"
|
||||
ctrlc = "3"
|
||||
git2 = { version = "0.20.4", features = ["vendored-libgit2", "vendored-openssl"] }
|
||||
|
||||
[target.'cfg(not(windows))'.dependencies]
|
||||
git2 = { version = "0.20.4", features = ["vendored-libgit2", "vendored-openssl"] }
|
||||
openssl = { version = "0.10", features = ["vendored"] }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
git2 = { version = "0.20.4", features = ["vendored-libgit2"] }
|
||||
16
README.md
16
README.md
@@ -71,6 +71,16 @@ cargo install dcr
|
||||
curl -fsSL https://dcr.dexoron.su/install.sh | bash
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>BSD (script)</b></td>
|
||||
<td>
|
||||
|
||||
```sh
|
||||
curl -fsSL https://raw.githubusercontent.com/dexoron/dcr/dev/install_bsd.sh | bash
|
||||
```
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -159,9 +169,9 @@ other in [docs/](docs/) and `dcr --help`
|
||||
| Linux | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ |
|
||||
| macOS | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
||||
| Windows | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ |
|
||||
| FreeBSD | ⚠️ | ⚠️ | ❌ | ❌ | ❌ | ⚠️ | ❌ | ❌ | ❌ |
|
||||
| OpenBSD | ⚠️ | ⚠️ | ❌ | ❌ | ❌ | ⚠️ | ❌ | ❌ | ❌ |
|
||||
| NetBSD | ⚠️ | ⚠️ | ❌ | ❌ | ❌ | ⚠️ | ❌ | ❌ | ❌ |
|
||||
| FreeBSD | ✅ | ⚠️ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
|
||||
| OpenBSD | ✅ | ⚠️ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
|
||||
| NetBSD | ✅ | ⚠️ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
|
||||
|
||||
✅ officially supported · ⚠️ community / best-effort · ❌ not supported
|
||||
|
||||
|
||||
36
install.sh
36
install.sh
@@ -30,8 +30,8 @@ CHANNEL=""
|
||||
|
||||
check_os() {
|
||||
case "$(uname -s)" in
|
||||
Linux|Darwin) ;;
|
||||
*) error "Only Linux and macOS are supported"; exit 1 ;;
|
||||
Linux|Darwin|FreeBSD|OpenBSD|NetBSD) ;;
|
||||
*) error "Only Linux, macOS, and BSD systems are supported"; exit 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
@@ -42,14 +42,25 @@ detect_target() {
|
||||
|
||||
case "$os:$arch" in
|
||||
Linux:x86_64) TARGET_TRIPLE="x86_64-unknown-linux-gnu" ;;
|
||||
Linux:aarch64|Linux:arm64) TARGET_TRIPLE="aarch64-unknown-linux-gnu" ;;
|
||||
Darwin:x86_64) TARGET_TRIPLE="x86_64-apple-darwin" ;;
|
||||
Darwin:arm64|Darwin:aarch64) TARGET_TRIPLE="aarch64-apple-darwin" ;;
|
||||
FreeBSD:x86_64|FreeBSD:amd64) TARGET_TRIPLE="x86_64-unknown-freebsd" ;;
|
||||
OpenBSD:x86_64|OpenBSD:amd64) TARGET_TRIPLE="x86_64-unknown-openbsd" ;;
|
||||
NetBSD:x86_64|NetBSD:amd64) TARGET_TRIPLE="x86_64-unknown-netbsd" ;;
|
||||
|
||||
*) error "Unsupported platform: $os/$arch"; exit 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
check_common_dependencies() {
|
||||
command -v curl >/dev/null 2>&1 || { error "curl is not installed"; exit 1; }
|
||||
if [[ "$CHANNEL" == "dev" ]]; then
|
||||
if ! command -v python3 >/dev/null 2>&1 && ! command -v jq >/dev/null 2>&1; then
|
||||
error "For dev installations, either 'python3' or 'jq' must be installed to parse GitHub API response."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
check_build_dependencies() {
|
||||
@@ -83,14 +94,15 @@ select_install_mode() {
|
||||
esac
|
||||
}
|
||||
|
||||
# Возвращает JSON нужного релиза в stdout
|
||||
# Returns JSON of the requested release to stdout
|
||||
fetch_release_json() {
|
||||
if [[ "$CHANNEL" == "dev" ]]; then
|
||||
log "Looking for latest dev (pre-release)..."
|
||||
local json
|
||||
local json result
|
||||
json="$(curl -fsSL "$GITHUB_API_ALL")"
|
||||
# Первый pre-release в списке
|
||||
local result
|
||||
if command -v jq >/dev/null 2>&1; then
|
||||
result="$(printf '%s' "$json" | jq -e -c 'map(select(.prerelease == true))[0]')"
|
||||
else
|
||||
result="$(printf '%s' "$json" | python3 - <<'EOF'
|
||||
import sys, json
|
||||
releases = json.load(sys.stdin)
|
||||
@@ -101,7 +113,9 @@ else:
|
||||
print(json.dumps(pre[0]), end="")
|
||||
EOF
|
||||
)"
|
||||
if [[ -z "$result" || "$result" == "{}" ]]; then
|
||||
fi
|
||||
|
||||
if [[ -z "$result" || "$result" == "{}" || "$result" == "null" ]]; then
|
||||
error "No dev (pre-release) found on GitHub"
|
||||
exit 1
|
||||
fi
|
||||
@@ -123,9 +137,9 @@ download_binary() {
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Версия без ведущего 'v'
|
||||
# Version without leading 'v'
|
||||
version="${tag#v}"
|
||||
# Имя бинарника: dcr-<triple>-<version>
|
||||
# Binary name: dcr-<triple>-<version>
|
||||
asset_name="dcr-${TARGET_TRIPLE}-${version}"
|
||||
|
||||
log "Fetching release ${tag} (channel: ${CHANNEL})..."
|
||||
@@ -149,7 +163,7 @@ prepare_sources() {
|
||||
rm -rf "$TMPDIR"
|
||||
git clone --depth 1 "$REPO_URL" "$TMPDIR"
|
||||
if [[ "$CHANNEL" == "dev" ]]; then
|
||||
# Клонируем dev ветку, если она есть
|
||||
# Clone dev branch if it exists
|
||||
git clone --depth 1 --branch dev "$REPO_URL" "$TMPDIR" 2>/dev/null || \
|
||||
git clone --depth 1 "$REPO_URL" "$TMPDIR"
|
||||
fi
|
||||
@@ -193,8 +207,8 @@ main() {
|
||||
|
||||
check_os
|
||||
detect_target
|
||||
check_common_dependencies
|
||||
select_channel
|
||||
check_common_dependencies
|
||||
select_install_mode
|
||||
cleanup
|
||||
|
||||
|
||||
223
install_bsd.sh
Executable file
223
install_bsd.sh
Executable file
@@ -0,0 +1,223 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
TMPDIR="/tmp/dcr-install"
|
||||
INSTALL_PATH="$HOME/.local/share/dcr"
|
||||
BINPATH="$HOME/.local/bin"
|
||||
LOGFILE="$HOME/.cache/dcr-install.log"
|
||||
REPO_URL="https://github.com/dexoron/dcr"
|
||||
GITHUB_API_LATEST="https://api.github.com/repos/dexoron/dcr/releases/latest"
|
||||
GITHUB_API_ALL="https://api.github.com/repos/dexoron/dcr/releases"
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
mkdir -p "$(dirname "$LOGFILE")"
|
||||
|
||||
log() { printf "${BLUE}[%s]${NC} %s\n" "$(date '+%Y-%m-%d %H:%M:%S')" "$1" | tee -a "$LOGFILE"; }
|
||||
success() { printf "${GREEN}✔ %s${NC}\n" "$1" | tee -a "$LOGFILE"; }
|
||||
warn() { printf "${YELLOW}⚠ %s${NC}\n" "$1" | tee -a "$LOGFILE"; }
|
||||
error() { printf "${RED}✖ %s${NC}\n" "$1" | tee -a "$LOGFILE" >&2; }
|
||||
|
||||
INSTALL_MODE=""
|
||||
CHANNEL=""
|
||||
|
||||
check_os() {
|
||||
case "$(uname -s)" in
|
||||
FreeBSD|OpenBSD|NetBSD|DragonFly) ;;
|
||||
*) error "Only BSD systems (FreeBSD, OpenBSD, NetBSD, DragonFly) are supported by this script"; exit 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
detect_target() {
|
||||
local os arch
|
||||
os="$(uname -s)"
|
||||
arch="$(uname -m)"
|
||||
|
||||
case "$os:$arch" in
|
||||
FreeBSD:x86_64|FreeBSD:amd64) TARGET_TRIPLE="x86_64-unknown-freebsd" ;;
|
||||
OpenBSD:x86_64|OpenBSD:amd64) TARGET_TRIPLE="x86_64-unknown-openbsd" ;;
|
||||
NetBSD:x86_64|NetBSD:amd64) TARGET_TRIPLE="x86_64-unknown-netbsd" ;;
|
||||
DragonFly:x86_64|DragonFly:amd64) TARGET_TRIPLE="x86_64-unknown-dragonfly" ;;
|
||||
*) error "Unsupported BSD platform: $os/$arch"; exit 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
check_common_dependencies() {
|
||||
command -v curl >/dev/null 2>&1 || { error "curl is not installed"; exit 1; }
|
||||
if [ "$CHANNEL" = "dev" ]; then
|
||||
if ! command -v python3 >/dev/null 2>&1 && ! command -v jq >/dev/null 2>&1; then
|
||||
error "For dev installations, either 'python3' or 'jq' must be installed to parse GitHub API response."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
check_build_dependencies() {
|
||||
command -v git >/dev/null 2>&1 || { error "git is not installed"; exit 1; }
|
||||
command -v cargo >/dev/null 2>&1 || { error "cargo is not installed"; exit 1; }
|
||||
}
|
||||
|
||||
select_channel() {
|
||||
echo "Choose channel:"
|
||||
echo " 1) Latest stable release (default)"
|
||||
echo " 2) Latest dev (pre-release)"
|
||||
printf "Enter 1 or 2 [1]: "
|
||||
read -r choice
|
||||
|
||||
case "${choice:-1}" in
|
||||
1) CHANNEL="stable" ;;
|
||||
2) CHANNEL="dev" ;;
|
||||
*) error "Unknown option"; exit 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
select_install_mode() {
|
||||
echo "Choose installation mode:"
|
||||
echo " 1) Download prebuilt binary from GitHub Release (recommended)"
|
||||
echo " 2) Build from git"
|
||||
printf "Enter 1 or 2 [1]: "
|
||||
read -r choice
|
||||
|
||||
case "${choice:-1}" in
|
||||
1) INSTALL_MODE="release" ;;
|
||||
2) INSTALL_MODE="build" ;;
|
||||
*) error "Unknown option"; exit 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
fetch_release_json() {
|
||||
if [ "$CHANNEL" = "dev" ]; then
|
||||
log "Looking for latest dev (pre-release)..."
|
||||
local json result
|
||||
json="$(curl -fsSL "$GITHUB_API_ALL")"
|
||||
if command -v jq >/dev/null 2>&1; then
|
||||
result="$(printf '%s' "$json" | jq -e -c 'map(select(.prerelease == true))[0]')"
|
||||
else
|
||||
result="$(printf '%s' "$json" | python3 - <<'EOF'
|
||||
import sys, json
|
||||
releases = json.load(sys.stdin)
|
||||
pre = [r for r in releases if r.get("prerelease", False)]
|
||||
if not pre:
|
||||
print("{}", end="")
|
||||
else:
|
||||
print(json.dumps(pre[0]), end="")
|
||||
EOF
|
||||
)"
|
||||
fi
|
||||
|
||||
if [ -z "$result" ] || [ "$result" = "{}" ] || [ "$result" = "null" ]; then
|
||||
error "No dev (pre-release) found on GitHub"
|
||||
exit 1
|
||||
fi
|
||||
printf '%s' "$result"
|
||||
else
|
||||
curl -fsSL "$GITHUB_API_LATEST"
|
||||
fi
|
||||
}
|
||||
|
||||
download_binary() {
|
||||
local release_json tag version asset_name download_url
|
||||
|
||||
release_json="$(fetch_release_json)"
|
||||
|
||||
tag="$(printf '%s\n' "$release_json" | \
|
||||
sed -n 's/.*"tag_name": "\([^"]*\)".*/\1/p' | head -n1)"
|
||||
if [ -z "$tag" ]; then
|
||||
error "Failed to determine release version"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
version="${tag#v}"
|
||||
asset_name="dcr-${TARGET_TRIPLE}-${version}"
|
||||
|
||||
log "Fetching release ${tag} (channel: ${CHANNEL})..."
|
||||
|
||||
download_url="$(printf '%s\n' "$release_json" | \
|
||||
sed -n "s#.*\"browser_download_url\": \"\([^\"]*/${asset_name}\)\".*#\1#p" | head -n1)"
|
||||
|
||||
if [ -z "$download_url" ]; then
|
||||
error "Asset ${asset_name} not found in release ${tag}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$INSTALL_PATH"
|
||||
curl -fL "$download_url" -o "$INSTALL_PATH/dcr"
|
||||
chmod +x "$INSTALL_PATH/dcr"
|
||||
success "Binary ${asset_name} downloaded (${tag})"
|
||||
}
|
||||
|
||||
prepare_sources() {
|
||||
log "Fetching sources..."
|
||||
rm -rf "$TMPDIR"
|
||||
git clone --depth 1 "$REPO_URL" "$TMPDIR"
|
||||
if [ "$CHANNEL" = "dev" ]; then
|
||||
git clone --depth 1 --branch dev "$REPO_URL" "$TMPDIR" 2>/dev/null || \
|
||||
git clone --depth 1 "$REPO_URL" "$TMPDIR"
|
||||
fi
|
||||
success "Sources fetched"
|
||||
}
|
||||
|
||||
build_binary() {
|
||||
log "Building release binary..."
|
||||
(cd "$TMPDIR" && cargo build --release)
|
||||
success "Build completed"
|
||||
}
|
||||
|
||||
install_built_binary() {
|
||||
mkdir -p "$INSTALL_PATH"
|
||||
cp "$TMPDIR/target/release/dcr" "$INSTALL_PATH/dcr"
|
||||
chmod +x "$INSTALL_PATH/dcr"
|
||||
success "Binary installed from source"
|
||||
}
|
||||
|
||||
install_link() {
|
||||
log "Creating symlink..."
|
||||
mkdir -p "$BINPATH"
|
||||
ln -sf "$INSTALL_PATH/dcr" "$BINPATH/dcr"
|
||||
success "Command 'dcr' added to $BINPATH"
|
||||
}
|
||||
|
||||
check_path() {
|
||||
if ! echo "$PATH" | grep -q "$BINPATH"; then
|
||||
warn "Directory $BINPATH not found in PATH"
|
||||
echo "Add this to ~/.bashrc, ~/.zshrc or ~/.profile:"
|
||||
echo "export PATH=\"$BINPATH:\$PATH\""
|
||||
fi
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
rm -rf "$TMPDIR" 2>/dev/null || true
|
||||
}
|
||||
|
||||
main() {
|
||||
log "Starting DCR installation for BSD"
|
||||
|
||||
check_os
|
||||
select_channel
|
||||
check_common_dependencies
|
||||
detect_target
|
||||
select_install_mode
|
||||
cleanup
|
||||
|
||||
if [ "$INSTALL_MODE" = "build" ]; then
|
||||
check_build_dependencies
|
||||
prepare_sources
|
||||
build_binary
|
||||
install_built_binary
|
||||
else
|
||||
download_binary
|
||||
fi
|
||||
|
||||
install_link
|
||||
check_path
|
||||
cleanup
|
||||
|
||||
success "Installation completed successfully"
|
||||
log "Log saved to $LOGFILE"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
2
rust-toolchain.toml
Normal file
2
rust-toolchain.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
[toolchain]
|
||||
channel = "stable"
|
||||
@@ -80,15 +80,26 @@ pub fn build(args: &[String]) -> i32 {
|
||||
}
|
||||
if flags.target.is_none() {
|
||||
let default_target = if cfg!(target_os = "linux") {
|
||||
"x86_64-unknown-linux-gnu"
|
||||
format!("{}-unknown-linux-gnu", std::env::consts::ARCH)
|
||||
} else if cfg!(target_os = "macos") {
|
||||
"x86_64-apple-darwin"
|
||||
"x86_64-apple-darwin".to_string()
|
||||
} else if cfg!(target_os = "windows") {
|
||||
"x86_64-pc-windows-msvc"
|
||||
"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"
|
||||
"unknown".to_string()
|
||||
};
|
||||
flags.target = Some(default_target.to_string());
|
||||
flags.target = Some(default_target);
|
||||
}
|
||||
if flags.clean {
|
||||
let mut clean_args = Vec::new();
|
||||
@@ -390,6 +401,15 @@ fn ensure_target_dirs(
|
||||
let default_dir = if cfg!(target_os = "linux") {
|
||||
let arch = std::env::consts::ARCH;
|
||||
format!("{arch}-unknown-linux-gnu/{profile}")
|
||||
} else if cfg!(any(
|
||||
target_os = "freebsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "dragonfly"
|
||||
)) {
|
||||
let arch = std::env::consts::ARCH;
|
||||
let os = std::env::consts::OS;
|
||||
format!("{arch}-unknown-{os}/{profile}")
|
||||
} else {
|
||||
profile.to_string()
|
||||
};
|
||||
|
||||
@@ -120,11 +120,22 @@ fn clean_from_root(root: &Path, flags: &CleanFlags) -> Result<(), String> {
|
||||
})
|
||||
.or_else(|| {
|
||||
Some(if cfg!(target_os = "linux") {
|
||||
"x86_64-unknown-linux-gnu".to_string()
|
||||
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()
|
||||
})
|
||||
|
||||
56
src/cli/fmt.rs
Normal file
56
src/cli/fmt.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
use crate::utils::log::error;
|
||||
use crate::utils::text::{BOLD_GREEN, colored};
|
||||
use glob::glob;
|
||||
use std::process::Command;
|
||||
|
||||
pub fn fmt(_args: &[String]) -> i32 {
|
||||
let patterns = [
|
||||
"src/**/*.c",
|
||||
"src/**/*.cpp",
|
||||
"src/**/*.h",
|
||||
"src/**/*.hpp",
|
||||
"tests/**/*.c",
|
||||
"tests/**/*.cpp",
|
||||
"tests/**/*.h",
|
||||
"tests/**/*.hpp",
|
||||
];
|
||||
|
||||
let mut files = Vec::new();
|
||||
for pattern in patterns {
|
||||
if let Ok(paths) = glob(pattern) {
|
||||
for path in paths.flatten() {
|
||||
if let Some(s) = path.to_str() {
|
||||
files.push(s.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if files.is_empty() {
|
||||
println!(" {} No files to format", colored("Format", BOLD_GREEN));
|
||||
return 0;
|
||||
}
|
||||
|
||||
println!(
|
||||
" {} {} files",
|
||||
colored("Formatting", BOLD_GREEN),
|
||||
files.len()
|
||||
);
|
||||
|
||||
let status = Command::new("clang-format").arg("-i").args(&files).status();
|
||||
|
||||
match status {
|
||||
Ok(s) if s.success() => {
|
||||
println!(" {} successful", colored("Format", BOLD_GREEN));
|
||||
0
|
||||
}
|
||||
Ok(s) => {
|
||||
error(&format!("clang-format failed with status: {}", s));
|
||||
1
|
||||
}
|
||||
Err(e) => {
|
||||
error(&format!("Failed to execute clang-format: {}", e));
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ pub fn help() -> i32 {
|
||||
println!(" run [--profile] Build and run the project (default: --debug)");
|
||||
println!(" tree Display the dependency tree");
|
||||
println!(" test Run project tests (alias: tests)");
|
||||
println!(" fmt Format all C/C++ source files using clang-format");
|
||||
println!(" clean Remove the target directory");
|
||||
println!(" gen <subcommand> Generate IDE integration files");
|
||||
printc("FLAGS:", BOLD_GREEN);
|
||||
|
||||
@@ -20,6 +20,7 @@ pub mod build;
|
||||
pub mod clean;
|
||||
pub mod flag_update;
|
||||
pub mod flags;
|
||||
pub mod fmt;
|
||||
pub mod r#gen;
|
||||
pub mod help;
|
||||
pub mod init;
|
||||
|
||||
@@ -160,15 +160,26 @@ fn run_project(
|
||||
let mut target = flags.target.clone();
|
||||
if target.is_none() {
|
||||
let default_target = if cfg!(target_os = "linux") {
|
||||
"x86_64-unknown-linux-gnu"
|
||||
format!("{}-unknown-linux-gnu", std::env::consts::ARCH)
|
||||
} else if cfg!(target_os = "macos") {
|
||||
"x86_64-apple-darwin"
|
||||
"x86_64-apple-darwin".to_string()
|
||||
} else if cfg!(target_os = "windows") {
|
||||
"x86_64-pc-windows-msvc"
|
||||
"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"
|
||||
"unknown".to_string()
|
||||
};
|
||||
target = Some(default_target.to_string());
|
||||
target = Some(default_target);
|
||||
}
|
||||
|
||||
let build_kind = config
|
||||
|
||||
@@ -37,9 +37,6 @@ pub fn collect_sources(
|
||||
collect_sources_rec(root, extensions, &mut sources, exclude_dirs, include_paths)?;
|
||||
}
|
||||
sources.sort();
|
||||
if sources.is_empty() {
|
||||
return Err(format!("No source files found in {}", format_roots(roots)));
|
||||
}
|
||||
Ok(sources)
|
||||
}
|
||||
|
||||
@@ -89,17 +86,6 @@ fn collect_sources_rec(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn format_roots(roots: &[PathBuf]) -> String {
|
||||
if roots.is_empty() {
|
||||
return "<none>".to_string();
|
||||
}
|
||||
let mut out = Vec::new();
|
||||
for root in roots {
|
||||
out.push(root.to_string_lossy().to_string());
|
||||
}
|
||||
out.join(", ")
|
||||
}
|
||||
|
||||
pub fn is_excluded(path: &Path, exclude_dirs: &[PathBuf], include_paths: &[String]) -> bool {
|
||||
if matches_patterns(path, include_paths, true) {
|
||||
return false;
|
||||
@@ -316,6 +302,31 @@ pub fn needs_rebuild(source: &str, object: &str) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn needs_link(objects: &[String], output_path: &str) -> bool {
|
||||
let out_meta = match fs::metadata(output_path) {
|
||||
Ok(m) => m,
|
||||
Err(_) => return true,
|
||||
};
|
||||
let out_time = match out_meta.modified() {
|
||||
Ok(t) => t,
|
||||
Err(_) => return true,
|
||||
};
|
||||
for obj in objects {
|
||||
let obj_meta = match fs::metadata(obj) {
|
||||
Ok(m) => m,
|
||||
Err(_) => return true,
|
||||
};
|
||||
let obj_time = match obj_meta.modified() {
|
||||
Ok(t) => t,
|
||||
Err(_) => return true,
|
||||
};
|
||||
if obj_time > out_time {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn parse_d_file(content: &str) -> Vec<String> {
|
||||
let mut deps = Vec::new();
|
||||
let text = content.replace("\\\n", " ").replace("\\\r\n", " ");
|
||||
@@ -418,6 +429,55 @@ mod tests {
|
||||
assert_eq!(result, "target/debug/obj/main.c.obj");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn needs_link_no_output() {
|
||||
let dir = temp_dir("link_no_out");
|
||||
let obj1 = dir.join("test1.o");
|
||||
let obj2 = dir.join("test2.o");
|
||||
let out = dir.join("app");
|
||||
fs::write(&obj1, "").unwrap();
|
||||
fs::write(&obj2, "").unwrap();
|
||||
let objs = vec![
|
||||
obj1.to_string_lossy().to_string(),
|
||||
obj2.to_string_lossy().to_string(),
|
||||
];
|
||||
assert!(needs_link(&objs, &out.to_string_lossy()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn needs_link_fresh() {
|
||||
let dir = temp_dir("link_fresh");
|
||||
let obj1 = dir.join("test1.o");
|
||||
let obj2 = dir.join("test2.o");
|
||||
let out = dir.join("app");
|
||||
fs::write(&obj1, "").unwrap();
|
||||
fs::write(&obj2, "").unwrap();
|
||||
std::thread::sleep(std::time::Duration::from_millis(50));
|
||||
fs::write(&out, "").unwrap();
|
||||
let objs = vec![
|
||||
obj1.to_string_lossy().to_string(),
|
||||
obj2.to_string_lossy().to_string(),
|
||||
];
|
||||
assert!(!needs_link(&objs, &out.to_string_lossy()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn needs_link_stale() {
|
||||
let dir = temp_dir("link_stale");
|
||||
let obj1 = dir.join("test1.o");
|
||||
let obj2 = dir.join("test2.o");
|
||||
let out = dir.join("app");
|
||||
fs::write(&obj1, "").unwrap();
|
||||
fs::write(&out, "").unwrap();
|
||||
std::thread::sleep(std::time::Duration::from_millis(50));
|
||||
fs::write(&obj2, "").unwrap();
|
||||
let objs = vec![
|
||||
obj1.to_string_lossy().to_string(),
|
||||
obj2.to_string_lossy().to_string(),
|
||||
];
|
||||
assert!(needs_link(&objs, &out.to_string_lossy()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn needs_rebuild_no_object() {
|
||||
let dir = temp_dir("rebuild_no_obj");
|
||||
@@ -550,7 +610,8 @@ mod tests {
|
||||
let result = collect_sources(&roots, &["c"], &[], &include);
|
||||
std::env::set_current_dir(prev).unwrap();
|
||||
|
||||
assert!(result.is_err(), "empty src should return error");
|
||||
let sources = result.expect("empty src should return Ok");
|
||||
assert!(sources.is_empty(), "empty src should return empty vec");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -43,6 +43,10 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
||||
|
||||
if ctx.kind == "staticlib" {
|
||||
let lib_path = platform::lib_path(ctx.profile, ctx.project_name, ctx.target_dir);
|
||||
if !common::needs_link(&objects, &lib_path) {
|
||||
let elapsed = ((start_time.elapsed().as_secs_f64() * 100.0).trunc()) / 100.0;
|
||||
return Ok(elapsed);
|
||||
}
|
||||
let archiver = ctx.archiver.unwrap_or(if cfg!(target_os = "windows") {
|
||||
"lib"
|
||||
} else {
|
||||
@@ -105,6 +109,11 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
||||
} else {
|
||||
platform::bin_path(ctx.profile, &final_name, ctx.target_dir)
|
||||
};
|
||||
|
||||
if !common::needs_link(&objects, &out_path) {
|
||||
let elapsed = ((start_time.elapsed().as_secs_f64() * 100.0).trunc()) / 100.0;
|
||||
return Ok(elapsed);
|
||||
}
|
||||
cmd.arg("-o").arg(out_path);
|
||||
|
||||
if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() {
|
||||
|
||||
@@ -43,6 +43,10 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
||||
|
||||
if ctx.kind == "staticlib" {
|
||||
let lib_path = platform::lib_path(ctx.profile, ctx.project_name, ctx.target_dir);
|
||||
if !common::needs_link(&objects, &lib_path) {
|
||||
let elapsed = ((start_time.elapsed().as_secs_f64() * 100.0).trunc()) / 100.0;
|
||||
return Ok(elapsed);
|
||||
}
|
||||
let mut cmd = Command::new(ctx.archiver.unwrap_or("lib"));
|
||||
cmd.arg("/nologo").arg(format!("/OUT:{lib_path}"));
|
||||
for obj in &objects {
|
||||
@@ -117,6 +121,11 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
||||
} else {
|
||||
platform::bin_path(ctx.profile, ctx.project_name, ctx.target_dir)
|
||||
};
|
||||
|
||||
if !common::needs_link(&objects, &out_path) {
|
||||
let elapsed = ((start_time.elapsed().as_secs_f64() * 100.0).trunc()) / 100.0;
|
||||
return Ok(elapsed);
|
||||
}
|
||||
cmd.arg(format!("/Fe:{out_path}"));
|
||||
|
||||
if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() {
|
||||
|
||||
@@ -43,6 +43,10 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
||||
|
||||
if ctx.kind == "staticlib" {
|
||||
let lib_path = platform::lib_path(ctx.profile, ctx.project_name, ctx.target_dir);
|
||||
if !common::needs_link(&objects, &lib_path) {
|
||||
let elapsed = ((start_time.elapsed().as_secs_f64() * 100.0).trunc()) / 100.0;
|
||||
return Ok(elapsed);
|
||||
}
|
||||
let archiver = ctx.archiver.unwrap_or(if cfg!(target_os = "windows") {
|
||||
"lib"
|
||||
} else {
|
||||
@@ -105,6 +109,11 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
||||
} else {
|
||||
platform::bin_path(ctx.profile, &final_name, ctx.target_dir)
|
||||
};
|
||||
|
||||
if !common::needs_link(&objects, &out_path) {
|
||||
let elapsed = ((start_time.elapsed().as_secs_f64() * 100.0).trunc()) / 100.0;
|
||||
return Ok(elapsed);
|
||||
}
|
||||
cmd.arg("-o").arg(out_path);
|
||||
|
||||
if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() {
|
||||
|
||||
@@ -45,6 +45,10 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
||||
|
||||
if ctx.kind == "staticlib" {
|
||||
let lib_path = platform::lib_path(ctx.profile, ctx.project_name, ctx.target_dir);
|
||||
if !common::needs_link(&objects, &lib_path) {
|
||||
let elapsed = ((start_time.elapsed().as_secs_f64() * 100.0).trunc()) / 100.0;
|
||||
return Ok(elapsed);
|
||||
}
|
||||
let mut cmd = Command::new(ctx.archiver.unwrap_or("ar"));
|
||||
cmd.arg("rcs").arg(&lib_path);
|
||||
for obj in &objects {
|
||||
@@ -106,6 +110,11 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
||||
} else {
|
||||
platform::bin_path(ctx.profile, &final_name, ctx.target_dir)
|
||||
};
|
||||
|
||||
if !common::needs_link(&objects, &out_path) {
|
||||
let elapsed = ((start_time.elapsed().as_secs_f64() * 100.0).trunc()) / 100.0;
|
||||
return Ok(elapsed);
|
||||
}
|
||||
cmd.arg("-o").arg(out_path);
|
||||
|
||||
if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() {
|
||||
|
||||
@@ -27,7 +27,15 @@ 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"];
|
||||
const VALID_KINDS: &[&str] = &[
|
||||
"bin",
|
||||
"staticlib",
|
||||
"sharedlib",
|
||||
"efi",
|
||||
"elf",
|
||||
"none",
|
||||
"custom",
|
||||
];
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ConfigError {
|
||||
@@ -101,7 +109,8 @@ pub struct PackageConfig {
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct BuildConfig {
|
||||
pub language: LanguageConfig,
|
||||
pub standard: String,
|
||||
#[serde(default)]
|
||||
pub standard: Option<String>,
|
||||
#[serde(default)]
|
||||
pub cxx_standard: Option<String>,
|
||||
pub compiler: String,
|
||||
@@ -353,7 +362,14 @@ impl Config {
|
||||
}
|
||||
|
||||
validate_language_config(&build.language, "build.language")?;
|
||||
if build.standard.trim().is_empty() {
|
||||
let lang_is_asm = match &build.language {
|
||||
LanguageConfig::One(s) => s.trim().eq_ignore_ascii_case("asm"),
|
||||
LanguageConfig::Many(v) => v.len() == 1 && v[0].trim().eq_ignore_ascii_case("asm"),
|
||||
};
|
||||
if let Some(ref std) = build.standard
|
||||
&& std.trim().is_empty()
|
||||
&& !lang_is_asm
|
||||
{
|
||||
return Err(ConfigError::Invalid("build.standard is empty".into()));
|
||||
}
|
||||
if build.compiler.trim().is_empty() {
|
||||
@@ -754,7 +770,9 @@ fn format_toml(value: &Value) -> Result<String, ConfigError> {
|
||||
} else {
|
||||
out.push_str(&format!("language = \"{language}\"\n"));
|
||||
}
|
||||
if !standard.is_empty() {
|
||||
out.push_str(&format!("standard = \"{standard}\"\n"));
|
||||
}
|
||||
out.push_str(&format!("compiler = \"{compiler}\"\n"));
|
||||
let kind = build
|
||||
.get("kind")
|
||||
|
||||
@@ -22,12 +22,10 @@ use std::process::Command;
|
||||
pub fn run_binary(project_name: &str, profile: &str, target_dir: Option<&str>) -> i32 {
|
||||
let bin_path = platform::bin_path(profile, project_name, target_dir);
|
||||
if Path::new(&bin_path).exists() {
|
||||
let output = Command::new(&bin_path).output();
|
||||
match output {
|
||||
Ok(o) => {
|
||||
print!("{}", String::from_utf8_lossy(&o.stdout));
|
||||
eprint!("{}", String::from_utf8_lossy(&o.stderr));
|
||||
return o.status.code().unwrap_or(0);
|
||||
let status = Command::new(&bin_path).status();
|
||||
match status {
|
||||
Ok(s) => {
|
||||
return s.code().unwrap_or(0);
|
||||
}
|
||||
Err(_) => {
|
||||
return 1;
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
//
|
||||
// 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 crate::utils::log::error;
|
||||
mod cli;
|
||||
mod config;
|
||||
@@ -41,6 +40,7 @@ fn main() {
|
||||
"run" => cli::run::run(rest),
|
||||
"tree" => cli::tree::tree(rest),
|
||||
"test" | "tests" => cli::test::test(rest),
|
||||
"fmt" => cli::fmt::fmt(rest),
|
||||
"clean" => cli::clean::clean(rest),
|
||||
"gen" => cli::r#gen::r#gen(rest),
|
||||
"--version" => {
|
||||
|
||||
88
src/platform/bsd.rs
Normal file
88
src/platform/bsd.rs
Normal file
@@ -0,0 +1,88 @@
|
||||
pub fn bin_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 os = std::env::consts::OS;
|
||||
let target = format!("{arch}-unknown-{os}");
|
||||
format!("./target/{target}/{profile}/{name}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 => {
|
||||
let arch = std::env::consts::ARCH;
|
||||
let os = std::env::consts::OS;
|
||||
let target = format!("{arch}-unknown-{os}");
|
||||
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 os = std::env::consts::OS;
|
||||
let target = format!("{arch}-unknown-{os}");
|
||||
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 os = std::env::consts::OS;
|
||||
let target = format!("{arch}-unknown-{os}");
|
||||
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 => {
|
||||
let arch = std::env::consts::ARCH;
|
||||
let os = std::env::consts::OS;
|
||||
let target = format!("{arch}-unknown-{os}");
|
||||
format!("./target/{target}/{profile}/lib{name}.so")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn bin_path_default() {
|
||||
let arch = std::env::consts::ARCH;
|
||||
let os = std::env::consts::OS;
|
||||
let expected = format!("./target/{arch}-unknown-{os}/debug/hello");
|
||||
assert_eq!(bin_path("debug", "hello", None), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lib_path_default() {
|
||||
let arch = std::env::consts::ARCH;
|
||||
let os = std::env::consts::OS;
|
||||
let expected = format!("./target/{arch}-unknown-{os}/debug/libmylib.a");
|
||||
assert_eq!(lib_path("debug", "mylib", None), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shared_lib_path_default() {
|
||||
let arch = std::env::consts::ARCH;
|
||||
let os = std::env::consts::OS;
|
||||
let expected = format!("./target/{arch}-unknown-{os}/debug/libmylib.so");
|
||||
assert_eq!(shared_lib_path("debug", "mylib", None), expected);
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,13 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#[cfg(any(
|
||||
target_os = "freebsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "dragonfly"
|
||||
))]
|
||||
pub mod bsd;
|
||||
#[cfg(target_os = "linux")]
|
||||
pub mod linux;
|
||||
#[cfg(target_os = "macos")]
|
||||
@@ -35,7 +42,24 @@ pub fn bin_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
||||
{
|
||||
return windows::bin_path(profile, name, target_dir);
|
||||
}
|
||||
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
|
||||
#[cfg(any(
|
||||
target_os = "freebsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "dragonfly"
|
||||
))]
|
||||
{
|
||||
return bsd::bin_path(profile, name, target_dir);
|
||||
}
|
||||
#[cfg(not(any(
|
||||
target_os = "linux",
|
||||
target_os = "macos",
|
||||
target_os = "windows",
|
||||
target_os = "freebsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "dragonfly"
|
||||
)))]
|
||||
{
|
||||
match target_dir {
|
||||
Some(dir) => format!("{}/{}", dir.trim_end_matches('/'), name),
|
||||
@@ -57,7 +81,24 @@ pub fn elf_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
||||
{
|
||||
return windows::elf_path(profile, name, target_dir);
|
||||
}
|
||||
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
|
||||
#[cfg(any(
|
||||
target_os = "freebsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "dragonfly"
|
||||
))]
|
||||
{
|
||||
return bsd::elf_path(profile, name, target_dir);
|
||||
}
|
||||
#[cfg(not(any(
|
||||
target_os = "linux",
|
||||
target_os = "macos",
|
||||
target_os = "windows",
|
||||
target_os = "freebsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "dragonfly"
|
||||
)))]
|
||||
{
|
||||
match target_dir {
|
||||
Some(dir) => format!("{}/{}", dir.trim_end_matches('/'), name),
|
||||
@@ -79,7 +120,24 @@ pub fn efi_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
||||
{
|
||||
return windows::efi_path(profile, name, target_dir);
|
||||
}
|
||||
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
|
||||
#[cfg(any(
|
||||
target_os = "freebsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "dragonfly"
|
||||
))]
|
||||
{
|
||||
return bsd::efi_path(profile, name, target_dir);
|
||||
}
|
||||
#[cfg(not(any(
|
||||
target_os = "linux",
|
||||
target_os = "macos",
|
||||
target_os = "windows",
|
||||
target_os = "freebsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "dragonfly"
|
||||
)))]
|
||||
{
|
||||
match target_dir {
|
||||
Some(dir) => format!("{}/{}.efi", dir.trim_end_matches('/'), name),
|
||||
@@ -101,7 +159,24 @@ pub fn lib_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
||||
{
|
||||
return windows::lib_path(profile, name, target_dir);
|
||||
}
|
||||
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
|
||||
#[cfg(any(
|
||||
target_os = "freebsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "dragonfly"
|
||||
))]
|
||||
{
|
||||
return bsd::lib_path(profile, name, target_dir);
|
||||
}
|
||||
#[cfg(not(any(
|
||||
target_os = "linux",
|
||||
target_os = "macos",
|
||||
target_os = "windows",
|
||||
target_os = "freebsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "dragonfly"
|
||||
)))]
|
||||
{
|
||||
match target_dir {
|
||||
Some(dir) => format!("{}/lib{}.a", dir.trim_end_matches('/'), name),
|
||||
@@ -123,7 +198,24 @@ pub fn shared_lib_path(profile: &str, name: &str, target_dir: Option<&str>) -> S
|
||||
{
|
||||
return windows::shared_lib_path(profile, name, target_dir);
|
||||
}
|
||||
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
|
||||
#[cfg(any(
|
||||
target_os = "freebsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "dragonfly"
|
||||
))]
|
||||
{
|
||||
return bsd::shared_lib_path(profile, name, target_dir);
|
||||
}
|
||||
#[cfg(not(any(
|
||||
target_os = "linux",
|
||||
target_os = "macos",
|
||||
target_os = "windows",
|
||||
target_os = "freebsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "dragonfly"
|
||||
)))]
|
||||
{
|
||||
match target_dir {
|
||||
Some(dir) => format!("{}/lib{}.so", dir.trim_end_matches('/'), name),
|
||||
|
||||
Reference in New Issue
Block a user