mirror of
https://github.com/dexoron/dcr.git
synced 2026-10-21 19:29:30 +00:00
46
CHANGELOG.md
46
CHANGELOG.md
@@ -1,5 +1,51 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [0.8.1] - 2026-07-20 "OS-Dev: flat-bin, FAT-образы / OS-Dev: flat-bin & FAT Images"
|
||||||
|
|
||||||
|
### RU
|
||||||
|
|
||||||
|
**Добавлено:**
|
||||||
|
|
||||||
|
- **`build.kind = "flat-bin"`** — сырой бинарный артефакт (расширение по умолчанию `.bin`):
|
||||||
|
- **NASM** — `-f bin`, прямой вывод `<stem>.bin`
|
||||||
|
- **FASM** — прямой вывод (ожидается `format binary` в исходнике)
|
||||||
|
- **GAS / MASM / LLVM IR (`llc`)** — объект → `objcopy -O binary` → `<stem>.bin`
|
||||||
|
- **C/C++ (gcc/clang/MSVC)** — compile → link (nostdlib/static) → `objcopy -O binary` → `<name>.bin`
|
||||||
|
- Требуется `llvm-objcopy` / `objcopy` / `gobjcopy` в PATH (кроме NASM/FASM direct)
|
||||||
|
- **Секция `[archive]`** — после успешной сборки DCR собирает образ диска (FAT12/16/32 через `fatfs`): размер, offset, volume label, опциональный bootsector (512 байт в начало при `offset = 0`) и `layout` (копирование файлов/glob в FS). Плейсхолдер `{profile}` в путях.
|
||||||
|
- **Одиночные файлы в `build.roots`** — корень может быть файлом (`.c`, `.asm`, header), а не только каталогом; то же для сбора заголовков в cache.
|
||||||
|
|
||||||
|
**Изменено:**
|
||||||
|
|
||||||
|
- **`dcr build --force` / `dcr run --force`** — force также перезапускает `build.steps` и `build.post_steps`.
|
||||||
|
- **`dcr run` на workspace-only root** — если задан `[run].cmd`, сначала build + run по конфигу корня; иначе делегирование члену workspace (как в 0.8.0).
|
||||||
|
|
||||||
|
**Зависимости:**
|
||||||
|
|
||||||
|
- `fatfs = "0.3"` для упаковки FAT-образов.
|
||||||
|
|
||||||
|
### EN
|
||||||
|
|
||||||
|
**Added:**
|
||||||
|
|
||||||
|
- **`build.kind = "flat-bin"`** — raw binary artifact (default extension `.bin`):
|
||||||
|
- **NASM** — `-f bin`, direct `<stem>.bin`
|
||||||
|
- **FASM** — direct output (`format binary` in source)
|
||||||
|
- **GAS / MASM / LLVM IR (`llc`)** — object → `objcopy -O binary` → `<stem>.bin`
|
||||||
|
- **C/C++ (gcc/clang/MSVC)** — compile → link (`-nostdlib -static`) → `objcopy -O binary` → `<name>.bin`
|
||||||
|
- Needs `llvm-objcopy` / `objcopy` / `gobjcopy` in PATH (except NASM/FASM direct)
|
||||||
|
- **`[archive]` section** — after a successful build, pack a disk image (FAT12/16/32 via `fatfs`): size, offset, volume label, optional bootsector (512 bytes at start when `offset = 0`), and `layout` (file/glob copy into the FS). `{profile}` substitution in paths.
|
||||||
|
- **Single-file `build.roots`** — a root may be a file (`.c`, `.asm`, header), not only a directory; header collection in the cache follows the same rule.
|
||||||
|
|
||||||
|
**Changed:**
|
||||||
|
|
||||||
|
- **`dcr build --force` / `dcr run --force`** — force also re-runs `build.steps` and `build.post_steps`.
|
||||||
|
- **`dcr run` on a workspace-only root** — if `[run].cmd` is set, build + run that command first; otherwise fall back to workspace member delegation (0.8.0 behavior).
|
||||||
|
|
||||||
|
**Dependencies:**
|
||||||
|
|
||||||
|
- `fatfs = "0.3"` for FAT image packing.
|
||||||
|
|
||||||
## [0.8.0] - 2026-07-18 "Модульный Движок Сборки, Новая Архитектура Ядра и Улучшение Workspaces / Modular Build Engine, New Core Architecture, & Workspace Improvements"
|
## [0.8.0] - 2026-07-18 "Модульный Движок Сборки, Новая Архитектура Ядра и Улучшение Workspaces / Modular Build Engine, New Core Architecture, & Workspace Improvements"
|
||||||
|
|
||||||
### RU
|
### RU
|
||||||
|
|||||||
279
Cargo.lock
generated
279
Cargo.lock
generated
@@ -2,18 +2,39 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "android_system_properties"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.102"
|
version = "1.0.102"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.22.1"
|
version = "0.22.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.11.1"
|
version = "2.11.1"
|
||||||
@@ -38,6 +59,28 @@ dependencies = [
|
|||||||
"objc2",
|
"objc2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bumpalo"
|
||||||
|
version = "3.20.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c89588d05638b5b4594a3348a2d6c20277e43a7f5c5202b05cc56888475a47b8"
|
||||||
|
dependencies = [
|
||||||
|
"find-msvc-tools",
|
||||||
|
"shlex",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.4"
|
version = "1.0.4"
|
||||||
@@ -50,6 +93,19 @@ version = "0.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chrono"
|
||||||
|
version = "0.4.45"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327"
|
||||||
|
dependencies = [
|
||||||
|
"iana-time-zone",
|
||||||
|
"js-sys",
|
||||||
|
"num-traits",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation"
|
name = "core-foundation"
|
||||||
version = "0.9.4"
|
version = "0.9.4"
|
||||||
@@ -98,9 +154,10 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dcr"
|
name = "dcr"
|
||||||
version = "0.8.0"
|
version = "0.8.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ctrlc",
|
"ctrlc",
|
||||||
|
"fatfs",
|
||||||
"glob",
|
"glob",
|
||||||
"self-replace",
|
"self-replace",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -127,7 +184,7 @@ version = "0.3.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38"
|
checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 2.11.1",
|
||||||
"block2",
|
"block2",
|
||||||
"libc",
|
"libc",
|
||||||
"objc2",
|
"objc2",
|
||||||
@@ -166,6 +223,24 @@ version = "2.4.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
|
checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fatfs"
|
||||||
|
version = "0.3.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "05669f8e7e2d7badc545c513710f0eba09c2fbef683eb859fd79c46c355048e0"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"byteorder",
|
||||||
|
"chrono",
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "find-msvc-tools"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "foldhash"
|
name = "foldhash"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
@@ -181,6 +256,30 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-core"
|
||||||
|
version = "0.3.33"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-task"
|
||||||
|
version = "0.3.33"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-util"
|
||||||
|
version = "0.3.33"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-task",
|
||||||
|
"pin-project-lite",
|
||||||
|
"slab",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "generic-array"
|
name = "generic-array"
|
||||||
version = "0.14.7"
|
version = "0.14.7"
|
||||||
@@ -231,6 +330,30 @@ version = "0.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iana-time-zone"
|
||||||
|
version = "0.1.65"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470"
|
||||||
|
dependencies = [
|
||||||
|
"android_system_properties",
|
||||||
|
"core-foundation-sys",
|
||||||
|
"iana-time-zone-haiku",
|
||||||
|
"js-sys",
|
||||||
|
"log",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"windows-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iana-time-zone-haiku"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "icu_collections"
|
name = "icu_collections"
|
||||||
version = "2.2.0"
|
version = "2.2.0"
|
||||||
@@ -358,6 +481,17 @@ version = "1.0.18"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "js-sys"
|
||||||
|
version = "0.3.103"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"futures-util",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "leb128fmt"
|
name = "leb128fmt"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -400,12 +534,21 @@ version = "0.31.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d"
|
checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 2.11.1",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"cfg_aliases",
|
"cfg_aliases",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "objc2"
|
name = "objc2"
|
||||||
version = "0.6.4"
|
version = "0.6.4"
|
||||||
@@ -439,6 +582,12 @@ version = "2.3.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-lite"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "potential_utf"
|
name = "potential_utf"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
@@ -488,7 +637,7 @@ version = "1.1.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
|
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 2.11.1",
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys",
|
||||||
@@ -526,6 +675,12 @@ dependencies = [
|
|||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustversion"
|
||||||
|
version = "1.0.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "schannel"
|
name = "schannel"
|
||||||
version = "0.1.29"
|
version = "0.1.29"
|
||||||
@@ -541,7 +696,7 @@ version = "2.11.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
|
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 2.11.1",
|
||||||
"core-foundation",
|
"core-foundation",
|
||||||
"core-foundation-sys",
|
"core-foundation-sys",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -638,6 +793,18 @@ dependencies = [
|
|||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shlex"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "slab"
|
||||||
|
version = "0.4.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "1.15.1"
|
version = "1.15.1"
|
||||||
@@ -833,6 +1000,51 @@ dependencies = [
|
|||||||
"wit-bindgen 0.51.0",
|
"wit-bindgen 0.51.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen"
|
||||||
|
version = "0.2.126"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
"rustversion",
|
||||||
|
"wasm-bindgen-macro",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro"
|
||||||
|
version = "0.2.126"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"wasm-bindgen-macro-support",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro-support"
|
||||||
|
version = "0.2.126"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-shared"
|
||||||
|
version = "0.2.126"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-encoder"
|
name = "wasm-encoder"
|
||||||
version = "0.244.0"
|
version = "0.244.0"
|
||||||
@@ -861,18 +1073,71 @@ version = "0.244.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
|
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 2.11.1",
|
||||||
"hashbrown 0.15.5",
|
"hashbrown 0.15.5",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"semver",
|
"semver",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-core"
|
||||||
|
version = "0.62.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
|
||||||
|
dependencies = [
|
||||||
|
"windows-implement",
|
||||||
|
"windows-interface",
|
||||||
|
"windows-link",
|
||||||
|
"windows-result",
|
||||||
|
"windows-strings",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-implement"
|
||||||
|
version = "0.60.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-interface"
|
||||||
|
version = "0.59.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-link"
|
name = "windows-link"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-result"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-strings"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.52.0"
|
version = "0.52.0"
|
||||||
@@ -1034,7 +1299,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
|
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bitflags",
|
"bitflags 2.11.1",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"log",
|
"log",
|
||||||
"serde",
|
"serde",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "dcr"
|
name = "dcr"
|
||||||
version = "0.8.0"
|
version = "0.8.1"
|
||||||
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."
|
||||||
@@ -69,6 +69,7 @@ toml = "1.0.3"
|
|||||||
toml_edit = "0.22"
|
toml_edit = "0.22"
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
ctrlc = "3"
|
ctrlc = "3"
|
||||||
|
fatfs = "0.3"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
opt-level = "z"
|
opt-level = "z"
|
||||||
|
|||||||
@@ -51,8 +51,9 @@ dcr gen vscode
|
|||||||
|
|
||||||
- **Incremental builds** — mtime + `.d` headers + SHA256 fingerprint
|
- **Incremental builds** — mtime + `.d` headers + SHA256 fingerprint
|
||||||
- **Parallel compilation** — thread::scope, atomic task queue
|
- **Parallel compilation** — thread::scope, atomic task queue
|
||||||
- **4 backends** — gcc/clang, MSVC/clang-cl, GAS (ARM), NASM (x86)
|
- **7 backends** — gcc/clang (unix_cc), MSVC/clang-cl (msvc), GAS, NASM, MASM, FASM, LLVM IR
|
||||||
- **7 project kinds** — bin, staticlib, sharedlib, efi, elf, none, custom
|
- **8 project kinds** — bin, staticlib, sharedlib, efi, elf, none, custom, flat-bin
|
||||||
|
- **Disk images** — optional `[archive]` packs FAT12/16/32 images after build
|
||||||
- **Cross-compilation** — short names and full triples
|
- **Cross-compilation** — short names and full triples
|
||||||
- **Profiles** — debug / release with field overrides
|
- **Profiles** — debug / release with field overrides
|
||||||
- **Workspaces** — multi-package projects with topological sort
|
- **Workspaces** — multi-package projects with topological sort
|
||||||
|
|||||||
@@ -4,6 +4,108 @@ sidebar_label: Changelog
|
|||||||
|
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.8.1 (2026-07-20)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **`build.kind = "flat-bin"`** — raw binary (default `.bin`) for OS-dev payloads:
|
||||||
|
- **NASM** — `-f bin` → `<stem>.bin`
|
||||||
|
- **FASM** — direct output (`format binary` in source)
|
||||||
|
- **GAS / MASM / LLC** — object → `objcopy -O binary` → `<stem>.bin`
|
||||||
|
- **C/C++** — objects → freestanding link → `objcopy -O binary` → `<name>.bin`
|
||||||
|
- Requires `llvm-objcopy` / `objcopy` / `gobjcopy` except for NASM/FASM direct emit
|
||||||
|
- **`[archive]` section** — after a successful build, pack a FAT disk image (`fatfs`): `output`, `format` (`fat12`/`fat16`/`fat32`), optional `size` (default 1.44 MiB), `offset`, `label`, `bootsector`, and `layout` (`from` → `to`, supports globs). `{profile}` works in paths.
|
||||||
|
- **Single-file `build.roots`** — a root may be a single source or header file, not only a directory.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- **`--force`** also re-runs `build.steps` and `build.post_steps`.
|
||||||
|
- **`dcr run` on workspace-only root** — if `[run].cmd` is set, run that after build; otherwise delegate to a workspace member.
|
||||||
|
|
||||||
|
### Dependencies
|
||||||
|
|
||||||
|
- `fatfs = "0.3"` for FAT image packing.
|
||||||
|
|
||||||
|
## 0.8.0 (2026-07-18)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **Dedicated Build Engine (`src/core/build/engine.rs`)** — decoupled the build orchestration logic completely from the CLI front-end wrapper in `src/cli/build.rs` to a reusable, decoupled core build engine.
|
||||||
|
- **Polymorphic Language Model (`Language` trait)** — introduced the abstract `Language` trait (`src/core/build/language/mod.rs`). File scanning, compiler resolution, and flags handling are now encapsulated in dedicated language modules for C, C++ (including Qt code generation), LLVM IR, and ASM (GAS, NASM, FASM, MASM).
|
||||||
|
- **Polymorphic Compilation Dispatch (`Builder` trait)** — introduced the `Builder` trait to generalize builder invocations (`src/core/build/builder/mod.rs`). Consolidated GCC/Clang logic into `builder/cc_common.rs` and Microsoft Visual C++ logic into `builder/msvc/`.
|
||||||
|
- **Per-Language Configuration overrides (`[build.<lang>]`)** — introduced support for configuring language-specific compiler, standard, and flags (e.g. `[build.c]`, `[build.cxx]`, `[build.asm]`) independently in `dcr.toml`.
|
||||||
|
- **Automatic Workspace Dependency Injection** — during workspace builds, include/lib paths of dependent workspace members (including their source headers, local `include/` folders, and build target `target/include` / `target/lib` folders) are automatically resolved and injected.
|
||||||
|
- **`BuildReporter` Event System** — decoupled build orchestration output into a structured event-driven reporter model (`src/core/build/report.rs`), making DCR suitable for library embedding and IDE integrations without stderr capturing.
|
||||||
|
- **Build Cancellation Support** — introduced thread-safe cancellation tokens (`Arc<AtomicBool>`) allowing clients to safely abort compile runs mid-execution.
|
||||||
|
- **TOML Formatting & Custom Keys Preservation** — migrated the config editor to `toml_edit`, preserving all unknown/user-defined TOML keys, structures, and comments during file write operations (e.g., in `dcr add`).
|
||||||
|
- **Path Dependencies Target Include Resolution** — local path dependencies configured via tables now correctly expose headers built and packaged into their `target/include` folder to consumer packages.
|
||||||
|
- **Modular Integration Test Suite** — refactored the monolith `tests/cli_basic.rs` file into clean, specialized integration tests: `cli_build.rs`, `cli_deps.rs`, `cli_lint.rs`, `cli_new.rs`, `cli_qt.rs`, `cli_test.rs`, `cli_workspace.rs`.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- **Consolidated Linking and Archiving** — artifact generation (linking executables/shared libraries and archiving static libraries) consolidated into a single `src/core/build/builder/artifact.rs` module.
|
||||||
|
- **Workspace-Aware member execution** — `dcr run` executed at the workspace root of a `workspace_only` project now triggers the member build inside the workspace context using the `--workspace` parameter, preventing standalone build issues.
|
||||||
|
- **Elimination of Global State** — removed global variables and states from the build core, encapsulating build logic inside isolated `BuildContext` structs for thread safety.
|
||||||
|
- **Build Cache Separation** — relocated mtime tracking, incremental caching, and header-dependency checking logic to `src/core/build/cache.rs`.
|
||||||
|
- **Modularized Pre-build Steps** — decoupled generator scripts and pre-build commands parsing into `src/core/build/steps.rs`.
|
||||||
|
|
||||||
|
## 0.7.4 (2026-06-17)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **Native Qt support** — automatic meta-object handling (MOC, UIC, RCC) added when `build.qt = true` is set in `dcr.toml`.
|
||||||
|
- **`dcr lint` command** — C/C++ static analysis via `clang-tidy`. Supports `--fix` for automatic fixes. Respects `build.roots` and `build.src_disable` from config.
|
||||||
|
- **Three new assembler backends**:
|
||||||
|
- **MASM** (`compiler = "ml"` / `"ml64"`) — Microsoft Macro Assembler.
|
||||||
|
- **FASM** (`compiler = "fasm"`) — Flat Assembler.
|
||||||
|
- **LLVM IR** (`compiler = "llc"`) — compiles `.ll` files via `llc -filetype=obj`.
|
||||||
|
- **Shared ASM pipeline** — linking and archiving logic consolidated into `core/builder/asm.rs`. Adding a new assembler backend now takes ~30 lines.
|
||||||
|
- **Filesystem utilities** — `to_hex()` and `home_dir()` extracted to `utils/fs.rs`.
|
||||||
|
- **Build utilities** — `normalize_target()`, `normalize_kind()`, `normalize_platform()`, `default_profile_flags()` extracted to `utils/build.rs`.
|
||||||
|
- **Backend helpers** — `asm_lang_flag()`, `source_extensions()`, `elapsed_secs()` added to `core/builder/common.rs`.
|
||||||
|
- **Full Bare-Metal / Freestanding Automation** — introduced the `build.freestanding = true` configuration option. When enabled (or when a bare-metal target is detected), DCR automatically injects `-ffreestanding` during compilation and both `-nostdlib` and `-static` during linking.
|
||||||
|
- **Artifact Optimization (LTO & Strip)** — added `build.lto` (auto-injects `-flto` for compiler and linker) and `build.strip` (automatically strips debug symbols via linker `-s` flag) options to `dcr.toml`.
|
||||||
|
- **Compilation Thread Control** — added the `build.codegen-units` option to strictly limit the maximum number of parallel jobs utilized by the custom `parallel_build` worker pool.
|
||||||
|
- **Panic Behavior Management** — added `build.panic = "abort"` support. For C++ targets, it automatically strips exception handling and unwind tables via `-fno-exceptions`, `-fno-unwind-tables`, and `-fno-asynchronous-unwind-tables`.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- **Intelligent Default Flags Generation** — automated injection of fallback optimization (`-O3`/`-O0`), debug (`-g`), and warning (`-Wall -Wextra`) flags is now suppressed for bare-metal and freestanding builds if `build.cflags` are overridden.
|
||||||
|
- **Centralized Bare-Metal Detection** — relocated the `is_bare_metal_target` helper to common build utilities (`src/utils/build.rs`) for unified access across the building core.
|
||||||
|
- **Architectural Refactoring**:
|
||||||
|
- Renamed `src/config.rs` to `src/templates.rs`.
|
||||||
|
- Started consolidation of configuration and build orchestration logic.
|
||||||
|
|
||||||
|
## 0.7.3 (2026-06-13)
|
||||||
|
|
||||||
|
### 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)
|
||||||
|
|
||||||
|
### 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)
|
## 0.7.1 (2026-06-02)
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ dcr build # debug profile
|
|||||||
dcr build --release # release build
|
dcr build --release # release build
|
||||||
dcr build --debug # explicit debug
|
dcr build --debug # explicit debug
|
||||||
dcr build --target aarch64-unknown-linux-gnu
|
dcr build --target aarch64-unknown-linux-gnu
|
||||||
dcr build --force # full rebuild
|
dcr build --force # full rebuild (also re-runs build.steps / post_steps)
|
||||||
dcr build --clean # clean + build
|
dcr build --clean # clean + build
|
||||||
dcr build --verbose # show compilation commands
|
dcr build --verbose # show compilation commands
|
||||||
dcr build --workspace pkg-a # build specific workspace package
|
dcr build --workspace pkg-a # build specific workspace package
|
||||||
@@ -21,6 +21,8 @@ dcr build --workspace pkg-a # build specific workspace package
|
|||||||
|
|
||||||
Artifacts: `target/<triple>/<profile>/<binary-name>` (Linux with explicit target).
|
Artifacts: `target/<triple>/<profile>/<binary-name>` (Linux with explicit target).
|
||||||
|
|
||||||
|
With `[archive]` in `dcr.toml`, a FAT disk image is packed after a successful build.
|
||||||
|
|
||||||
## `dcr run`
|
## `dcr run`
|
||||||
|
|
||||||
Builds (if sources are newer than artifacts) and runs the binary.
|
Builds (if sources are newer than artifacts) and runs the binary.
|
||||||
@@ -28,8 +30,11 @@ Builds (if sources are newer than artifacts) and runs the binary.
|
|||||||
```bash
|
```bash
|
||||||
dcr run
|
dcr run
|
||||||
dcr run --release
|
dcr run --release
|
||||||
|
dcr run --force # force rebuild then run
|
||||||
```
|
```
|
||||||
|
|
||||||
|
On a **workspace-only** root: if `[run].cmd` is set, that command is used after build; otherwise DCR delegates to a workspace member.
|
||||||
|
|
||||||
## `dcr clean`
|
## `dcr clean`
|
||||||
|
|
||||||
Removes `target/` directory (or `target/<target>/<profile>`).
|
Removes `target/` directory (or `target/<target>/<profile>`).
|
||||||
|
|||||||
@@ -45,6 +45,6 @@ cargo test --all-targets
|
|||||||
CI runs:
|
CI runs:
|
||||||
- `cargo fmt --check`
|
- `cargo fmt --check`
|
||||||
- `cargo clippy --all-targets -- -D warnings`
|
- `cargo clippy --all-targets -- -D warnings`
|
||||||
- `cargo test --all-targets`
|
- `cargo check --all-targets --all-features`
|
||||||
- Build on Linux, macOS, Windows
|
- Build on Linux, macOS, Windows
|
||||||
- Integration tests
|
- Integration tests (compilation check)
|
||||||
|
|||||||
27
docs/faq.mdx
27
docs/faq.mdx
@@ -39,27 +39,6 @@ which clang
|
|||||||
|
|
||||||
Or specify explicitly via `[toolchain]` in `dcr.toml`.
|
Or specify explicitly via `[toolchain]` in `dcr.toml`.
|
||||||
|
|
||||||
## openssl-sys build failed
|
|
||||||
|
|
||||||
When building DCR from source:
|
|
||||||
|
|
||||||
```
|
|
||||||
error: failed to run custom build command for openssl-sys
|
|
||||||
```
|
|
||||||
|
|
||||||
**Solution:** Install OpenSSL development headers:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Ubuntu/Debian
|
|
||||||
sudo apt install libssl-dev
|
|
||||||
|
|
||||||
# Fedora
|
|
||||||
sudo dnf install openssl-devel
|
|
||||||
|
|
||||||
# macOS
|
|
||||||
brew install openssl
|
|
||||||
```
|
|
||||||
|
|
||||||
## Ctrl+C during build
|
## Ctrl+C during build
|
||||||
|
|
||||||
DCR handles SIGINT: interrupts the current compilation and exits with non-zero code.
|
DCR handles SIGINT: interrupts the current compilation and exits with non-zero code.
|
||||||
@@ -76,6 +55,12 @@ See [Cross-Compile Recipe](/docs/recipes/cross-to-windows).
|
|||||||
|
|
||||||
See [Workspace Recipe](/docs/recipes/multi-package-workspace).
|
See [Workspace Recipe](/docs/recipes/multi-package-workspace).
|
||||||
|
|
||||||
|
## Bootloader / pure NASM OS image
|
||||||
|
|
||||||
|
Use `kind = "flat-bin"` (NASM `-f bin`) and optional `[archive]` for a FAT disk image.
|
||||||
|
|
||||||
|
See [OS-dev recipe](/docs/recipes/os-flat-bin-archive).
|
||||||
|
|
||||||
## Lock file
|
## Lock file
|
||||||
|
|
||||||
`dcr.lock` is created during builds (`dcr build`) when registry dependencies are present. To force an update — delete `dcr.lock` and run `dcr build`.
|
`dcr.lock` is created during builds (`dcr build`) when registry dependencies are present. To force an update — delete `dcr.lock` and run `dcr build`.
|
||||||
|
|||||||
@@ -68,3 +68,11 @@ dcr test # run tests
|
|||||||
```bash
|
```bash
|
||||||
dcr fmt # clang-format with .clang-format
|
dcr fmt # clang-format with .clang-format
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Linting
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dcr lint # clang-tidy checks
|
||||||
|
dcr lint --fix # apply fixes automatically
|
||||||
|
```
|
||||||
|
|
||||||
|
|||||||
89
docs/recipes/os-flat-bin-archive.mdx
Normal file
89
docs/recipes/os-flat-bin-archive.mdx
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
---
|
||||||
|
sidebar_label: OS-dev (flat-bin + archive)
|
||||||
|
---
|
||||||
|
|
||||||
|
# Pure ASM / boot image (flat-bin + archive)
|
||||||
|
|
||||||
|
Minimal OS-dev style pipeline: assemble raw binaries with NASM, then pack a FAT floppy/image with an optional boot sector.
|
||||||
|
|
||||||
|
## Project layout
|
||||||
|
|
||||||
|
```
|
||||||
|
myos/
|
||||||
|
dcr.toml
|
||||||
|
src/
|
||||||
|
boot.asm # boot sector (512 bytes)
|
||||||
|
kernel.asm # payload
|
||||||
|
```
|
||||||
|
|
||||||
|
For multiple flat artifacts, use a **workspace** (one member per binary) or separate packages; a single `flat-bin` package produces one binary per source stem.
|
||||||
|
|
||||||
|
## Boot package (`flat-bin`)
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[package]
|
||||||
|
name = "boot"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
language = "asm"
|
||||||
|
compiler = "nasm"
|
||||||
|
kind = "flat-bin"
|
||||||
|
extension = "bin"
|
||||||
|
roots = ["src/boot.asm"]
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dcr build
|
||||||
|
# → target/<…>/debug/boot.bin (NASM -f bin, no link)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Disk image after build
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[archive]
|
||||||
|
output = "target/{profile}/disk.img"
|
||||||
|
format = "fat12"
|
||||||
|
size = "1440K"
|
||||||
|
label = "MYOS"
|
||||||
|
bootsector = "target/{profile}/boot.bin"
|
||||||
|
|
||||||
|
[[archive.layout]]
|
||||||
|
from = "target/{profile}/kernel.bin"
|
||||||
|
to = "KERNEL.BIN"
|
||||||
|
```
|
||||||
|
|
||||||
|
- `format`: `fat12`, `fat16`, or `fat32`
|
||||||
|
- `size`: bytes or `K`/`KB`/`M`/`MB`/`G`/`GB` (default ~1.44 MiB)
|
||||||
|
- `bootsector`: written only when `offset` is omitted or `0`
|
||||||
|
- `from` may be a glob; `{profile}` is substituted in paths
|
||||||
|
|
||||||
|
## C kernel → flat binary
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[build]
|
||||||
|
language = "c"
|
||||||
|
compiler = "clang"
|
||||||
|
kind = "flat-bin"
|
||||||
|
freestanding = true
|
||||||
|
filename = "kernel"
|
||||||
|
extension = "bin"
|
||||||
|
ldflags = ["-T", "linker.ld"]
|
||||||
|
```
|
||||||
|
|
||||||
|
Pipeline: objects → temporary linked ELF → `objcopy -O binary` → `kernel.bin`. Requires `objcopy` / `llvm-objcopy` in PATH.
|
||||||
|
|
||||||
|
## Other assemblers
|
||||||
|
|
||||||
|
| Tool | Notes |
|
||||||
|
|------|--------|
|
||||||
|
| **FASM** | Write `format binary` in the source; DCR writes `<stem>.bin` directly |
|
||||||
|
| **GAS** | Assemble `.s` → obj → `objcopy -O binary` |
|
||||||
|
| **LLC** | `language = "llvm_ir"` → obj → objcopy |
|
||||||
|
| **MASM** | COFF obj → objcopy (needs binutils/LLVM objcopy) |
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Single-file `roots` are supported: `roots = ["src/boot.asm"]`.
|
||||||
|
- `--force` re-runs `build.steps` / `build.post_steps` as well as recompilation.
|
||||||
|
- Prefer `kind = "elf"` if you need a relocatable ELF kernel without stripping to raw binary.
|
||||||
@@ -56,7 +56,7 @@ moc = "moc {in} -o {out}"
|
|||||||
### GAS / NASM / MASM / FASM / LLVM-IR
|
### GAS / NASM / MASM / FASM / LLVM-IR
|
||||||
|
|
||||||
- GAS: `-I`, `-c -o`, `--defsym`
|
- GAS: `-I`, `-c -o`, `--defsym`
|
||||||
- NASM: `-I`, `-o`, `-D`, `-f` (format: win64/elf64/macho64/macho32/elf32)
|
- NASM: `-I`, `-o`, `-D`, `-f` (format: win64/elf64/macho64/macho32/elf32; **`bin` when `kind = "flat-bin"`**)
|
||||||
- MASM: `/nologo /c /Fo<obj> <source>`
|
- MASM: `/nologo /c /Fo<obj> <source>`
|
||||||
- FASM: `<source> <output>` (output path is the object file)
|
- FASM: `<source> <output>` (output path is the object file)
|
||||||
- LLVM IR: `-filetype=obj <source> -o <output>`
|
- LLVM IR: `-filetype=obj <source> -o <output>`
|
||||||
@@ -87,6 +87,11 @@ Three levels of incrementality:
|
|||||||
| `elf` | ELF without stdlib | `target/<triple>/<profile>/<name>` |
|
| `elf` | ELF without stdlib | `target/<triple>/<profile>/<name>` |
|
||||||
| `none` | Compile only, no link | — |
|
| `none` | Compile only, no link | — |
|
||||||
| `custom` | Full filename+extension control | `target/<triple>/<profile>/<filename>.<extension>` |
|
| `custom` | Full filename+extension control | `target/<triple>/<profile>/<filename>.<extension>` |
|
||||||
|
| `flat-bin` | Raw binary | ASM: `<stem>.bin` (NASM `-f bin` / FASM / GAS·MASM·LLC via objcopy); C/C++: `<name>.bin` (link + objcopy) |
|
||||||
|
|
||||||
|
## Disk images (`[archive]`)
|
||||||
|
|
||||||
|
After a successful build, if `[archive]` is present in `dcr.toml`, DCR formats a FAT volume and copies files from `layout` into the image. See [dcr.toml → archive](/docs/reference/dcr-toml#archive).
|
||||||
|
|
||||||
## Build Steps
|
## Build Steps
|
||||||
|
|
||||||
|
|||||||
@@ -57,14 +57,8 @@ dcr build --target aarch64-none-elf
|
|||||||
|
|
||||||
## Target Directory
|
## Target Directory
|
||||||
|
|
||||||
On Linux with explicit target:
|
By default, the compilation output directories are structured as follows:
|
||||||
|
|
||||||
```
|
- **Linux and BSD**: Always output to `target/<triple>/<profile>/` (using host triple if no target is specified).
|
||||||
target/<triple>/<profile>/
|
- **macOS and Windows (without target)**: Output to `target/<profile>/`.
|
||||||
```
|
- **macOS and Windows (with explicit target)**: Output to `target/<triple>/<profile>/`.
|
||||||
|
|
||||||
On macOS/Windows or without target:
|
|
||||||
|
|
||||||
```
|
|
||||||
target/<profile>/
|
|
||||||
```
|
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ Main project configuration file. Located at the project root.
|
|||||||
|
|
||||||
[run]
|
[run]
|
||||||
# run settings
|
# run settings
|
||||||
|
|
||||||
|
[archive]
|
||||||
|
# optional: pack FAT disk image after build
|
||||||
```
|
```
|
||||||
|
|
||||||
## [package]
|
## [package]
|
||||||
@@ -62,18 +65,18 @@ author = "John Doe"
|
|||||||
|
|
||||||
| Field | Default | Description |
|
| Field | Default | Description |
|
||||||
|-------|---------|-------------|
|
|-------|---------|-------------|
|
||||||
| `language` | `"c"` | `"c"`, `"c++"`, `"cpp"`, `"cxx"`, `"asm"` (optional, defaults to `"c"`) |
|
| `language` | `"c"` | `"c"`, `"c++"`, `"cpp"`, `"cxx"`, `"asm"`, `"llvm_ir"`, `"llvm-ir"`, `"ll"` (optional, defaults to `"c"`) |
|
||||||
| `standard` | `"c11"` | C standard (`c11`, `c17`, `c23`) |
|
| `standard` | `"c11"` | C standard (`c11`, `c17`, `c23`) |
|
||||||
| `cxx_standard` | — | C++ standard (`c++17`, `c++20`, `c++23`) |
|
| `cxx_standard` | — | C++ standard (`c++17`, `c++20`, `c++23`) |
|
||||||
| `compiler` | `"clang"` | Preferred compiler (optional, defaults to `"clang"`) |
|
| `compiler` | `"clang"` | Preferred compiler (optional, defaults to `"clang"`) |
|
||||||
| `kind` | `"bin"` | `bin`, `staticlib`, `sharedlib`, `efi`, `elf`, `none`, `custom` |
|
| `kind` | `"bin"` | `bin`, `staticlib`, `sharedlib`, `efi`, `elf`, `none`, `custom`, `flat-bin` |
|
||||||
| `target` | host | Target triple for cross-compilation |
|
| `target` | host | Target triple for cross-compilation |
|
||||||
| `platform` | `"native"` | `native`, `efi` |
|
| `platform` | `"native"` | `native`, `efi` |
|
||||||
| `cflags` | `[]` | Additional C/C++/ASM flags |
|
| `cflags` | `[]` | Additional C/C++/ASM flags |
|
||||||
| `ldflags` | `[]` | Additional linker flags |
|
| `ldflags` | `[]` | Additional linker flags |
|
||||||
| `filename` | `""` | Custom output file name |
|
| `filename` | `""` | Custom output file name |
|
||||||
| `extension` | `""` | Custom file extension |
|
| `extension` | `""` | Custom file extension (for `flat-bin`, default is `bin`) |
|
||||||
| `roots` | `["src"]` | Source root directories |
|
| `roots` | `["src"]` | Source roots: directories and/or individual source/header files |
|
||||||
| `exclude` | `[]` | Exclude patterns |
|
| `exclude` | `[]` | Exclude patterns |
|
||||||
| `include` | `[]` | Additional include directories |
|
| `include` | `[]` | Additional include directories |
|
||||||
| `src_disable` | `false` | Disable auto source discovery |
|
| `src_disable` | `false` | Disable auto source discovery |
|
||||||
@@ -96,7 +99,7 @@ Settings from raw config (not in typed struct):
|
|||||||
- `ldscript` — linker script path
|
- `ldscript` — linker script path
|
||||||
- `build.steps` / `build.post_steps` — codegen steps
|
- `build.steps` / `build.post_steps` — codegen steps
|
||||||
|
|
||||||
## Per-language overrides: `[build.c]`, `[build.cxx]`, `[build.asm]`
|
## Per-language overrides: `[build.c]`, `[build.cxx]`, `[build.asm]`, `[build.llvm_ir]`
|
||||||
|
|
||||||
Each language can have its own table that overrides the flat `[build]` settings:
|
Each language can have its own table that overrides the flat `[build]` settings:
|
||||||
|
|
||||||
@@ -116,6 +119,9 @@ compiler = "g++"
|
|||||||
[build.asm]
|
[build.asm]
|
||||||
compiler = "nasm"
|
compiler = "nasm"
|
||||||
flags = ["-felf64"]
|
flags = ["-felf64"]
|
||||||
|
|
||||||
|
[build.llvm_ir]
|
||||||
|
compiler = "llc"
|
||||||
```
|
```
|
||||||
|
|
||||||
The flat `[build]` acts as fallback; per-language tables take precedence for their language.
|
The flat `[build]` acts as fallback; per-language tables take precedence for their language.
|
||||||
@@ -172,3 +178,82 @@ Default `cmd` = `./target/{profile}/{name}` (macOS/Windows) or `./target/<triple
|
|||||||
## [workspace]
|
## [workspace]
|
||||||
|
|
||||||
See [workspaces](/docs/reference/workspaces).
|
See [workspaces](/docs/reference/workspaces).
|
||||||
|
|
||||||
|
## [archive]
|
||||||
|
|
||||||
|
Optional post-build step: format a FAT volume and copy built artifacts into a disk image. Runs after a successful package build (and after workspace member builds that define `[archive]`).
|
||||||
|
|
||||||
|
| Field | Required | Description |
|
||||||
|
|-------|----------|-------------|
|
||||||
|
| `output` | yes | Image path relative to project root (`{profile}` allowed) |
|
||||||
|
| `format` | yes | `fat12`, `fat16`, or `fat32` |
|
||||||
|
| `size` | no | Image size: bytes or `K`/`KB`/`M`/`MB`/`G`/`GB` (default `1474560` ≈ 1.44 MiB) |
|
||||||
|
| `offset` | no | Byte offset of the FAT volume inside the image (default `0`) |
|
||||||
|
| `label` | no | Volume label (max 11 chars, default `VOLUME`) |
|
||||||
|
| `bootsector` | no | Path to a 512-byte boot sector written at offset 0 when `offset` is 0 (`{profile}` allowed) |
|
||||||
|
| `layout` | no | List of `{ from, to }` entries (files or globs → path inside the volume) |
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[archive]
|
||||||
|
output = "target/{profile}/disk.img"
|
||||||
|
format = "fat12"
|
||||||
|
size = "1440K"
|
||||||
|
label = "MYOS"
|
||||||
|
bootsector = "target/{profile}/boot.bin"
|
||||||
|
|
||||||
|
[[archive.layout]]
|
||||||
|
from = "target/{profile}/kernel.bin"
|
||||||
|
to = "KERNEL.BIN"
|
||||||
|
|
||||||
|
[[archive.layout]]
|
||||||
|
from = "assets/*"
|
||||||
|
to = "/"
|
||||||
|
```
|
||||||
|
|
||||||
|
Typical pairing with `kind = "flat-bin"` (NASM `-f bin`) for bootloaders and pure-ASM OS images.
|
||||||
|
|
||||||
|
## `flat-bin` (kind)
|
||||||
|
|
||||||
|
Produces a raw binary (default extension `bin`) for boot sectors, kernels, and freestanding payloads.
|
||||||
|
|
||||||
|
### Assemblers
|
||||||
|
|
||||||
|
| Tool | Language / compiler | How flat-bin is produced |
|
||||||
|
|------|---------------------|--------------------------|
|
||||||
|
| NASM | `language = "asm"`, `compiler = "nasm"` | `-f bin` → `<stem>.bin` |
|
||||||
|
| FASM | `compiler = "fasm"` | direct write (use `format binary` in source) |
|
||||||
|
| GAS | `compiler = "as"` / `"gas"` | assemble → `objcopy -O binary` |
|
||||||
|
| MASM | `compiler = "ml"` / `"ml64"` | assemble → `objcopy -O binary` |
|
||||||
|
| LLC | `language = "llvm_ir"`, `compiler = "llc"` | `-filetype=obj` → `objcopy -O binary` |
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[build]
|
||||||
|
language = "asm"
|
||||||
|
compiler = "nasm"
|
||||||
|
kind = "flat-bin"
|
||||||
|
extension = "bin"
|
||||||
|
roots = ["src/boot.asm"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### C / C++
|
||||||
|
|
||||||
|
Compile all sources, link with `-nostdlib -static` (plus your `ldflags` / `ldscript`), then convert the intermediate ELF/PE with `objcopy -O binary` to `<filename|name>.bin`.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[build]
|
||||||
|
language = "c"
|
||||||
|
compiler = "clang"
|
||||||
|
kind = "flat-bin"
|
||||||
|
freestanding = true
|
||||||
|
filename = "kernel"
|
||||||
|
extension = "bin"
|
||||||
|
ldscript = "linker.ld"
|
||||||
|
ldflags = ["-T", "linker.ld"]
|
||||||
|
```
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
- Multi-file **ASM** packages emit one `<stem>.bin` per source; **C/C++** emit a single project binary.
|
||||||
|
- `objcopy` tools tried in order: `llvm-objcopy`, `objcopy`, `gobjcopy`.
|
||||||
|
- Incompatible with `build.qt = true`.
|
||||||
|
- `dcr run` rejects `flat-bin` (not a host executable).
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ mylib = { path = "/abs/path/to/lib" }
|
|||||||
mylib = { path = "../relative/path" }
|
mylib = { path = "../relative/path" }
|
||||||
```
|
```
|
||||||
|
|
||||||
|
*Note: Header resolution automatically checks the packaged `target/include` directory of path dependencies, ensuring consumer packages can find headers from compiled static or dynamic libraries.*
|
||||||
|
|
||||||
## dcr.lock
|
## dcr.lock
|
||||||
|
|
||||||
Dependency lock file. Contains package names and sources.
|
Dependency lock file. Contains package names and sources.
|
||||||
|
|||||||
@@ -11,17 +11,18 @@ DCR normalizes target triples. Short platform names are expanded to full triples
|
|||||||
### Linux
|
### Linux
|
||||||
|
|
||||||
```
|
```
|
||||||
target/<arch>-unknown-linux-gnu/<profile>/
|
target/<arch>-unknown-linux-<env>/<profile>/
|
||||||
```
|
```
|
||||||
|
|
||||||
Architectures: `x86_64`, `aarch64` (host-detected).
|
Architectures: `x86_64`, `aarch64`, `i686`, `armv7`, `riscv64` (host-detected).
|
||||||
|
Environments: `gnu` (default), `musl`.
|
||||||
|
|
||||||
Artifact type: ELF. Extensions: `.so` (sharedlib), `.a` (staticlib).
|
Artifact type: ELF. Extensions: `.so` (sharedlib), `.a` (staticlib).
|
||||||
|
|
||||||
### macOS
|
### macOS
|
||||||
|
|
||||||
```
|
```
|
||||||
target/<profile>/
|
target/<profile>/ (default) or target/<triple>/<profile>/ (with target)
|
||||||
```
|
```
|
||||||
|
|
||||||
Architectures: `x86_64`, `aarch64` (host-detected).
|
Architectures: `x86_64`, `aarch64` (host-detected).
|
||||||
@@ -31,10 +32,11 @@ Extensions: `.dylib` (sharedlib), `.a` (staticlib).
|
|||||||
### Windows
|
### Windows
|
||||||
|
|
||||||
```
|
```
|
||||||
target/<profile>/
|
target/<profile>/ (default) or target/<triple>/<profile>/ (with target)
|
||||||
```
|
```
|
||||||
|
|
||||||
Architectures: `x86_64`, `aarch64`.
|
Architectures: `x86_64`, `aarch64`.
|
||||||
|
Environments: `msvc` (default), `gnu` (MinGW).
|
||||||
|
|
||||||
Extensions: `.exe` (bin), `.lib` (staticlib), `.dll` (sharedlib), `.efi` (UEFI).
|
Extensions: `.exe` (bin), `.lib` (staticlib), `.dll` (sharedlib), `.efi` (UEFI).
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,9 @@ dcr build # build all packages in dependency order
|
|||||||
dcr build --workspace app # build only app (dependencies built automatically)
|
dcr build --workspace app # build only app (dependencies built automatically)
|
||||||
```
|
```
|
||||||
|
|
||||||
When building a workspace, member outputs (libraries) are automatically added to include/lib paths of dependent packages.
|
When building a workspace, DCR automatically injects include and library paths of dependent workspace members:
|
||||||
|
- **Include Paths**: Automatically resolves and injects header directories of dependencies, including the member's `src/` directory, local `include/` directory, and the packaged `target/include` directory.
|
||||||
|
- **Library Paths**: Injects compiled library search paths (`target/lib` as well as target-specific build folders) to allow automatic linking with member libraries.
|
||||||
|
|
||||||
## Clean
|
## Clean
|
||||||
|
|
||||||
|
|||||||
@@ -110,8 +110,17 @@ pub fn run(args: &[String]) -> i32 {
|
|||||||
Err(_) => return 1,
|
Err(_) => return 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle workspace-only root: delegate to workspace member
|
|
||||||
if config.is_workspace_only() {
|
if config.is_workspace_only() {
|
||||||
|
if let Some(cmd) = get_run_cmd(&config, &flags.profile, flags.target.as_deref(), "") {
|
||||||
|
let build_status = build(&args_for_build(&flags));
|
||||||
|
if build_status == 0 {
|
||||||
|
println!("\n {} {}", colored("Running", BOLD_GREEN), cmd);
|
||||||
|
println!("--------------------------------");
|
||||||
|
return run_shell(&cmd);
|
||||||
|
}
|
||||||
|
return build_status;
|
||||||
|
}
|
||||||
|
|
||||||
let ws = match crate::core::workspace::parse_workspace(
|
let ws = match crate::core::workspace::parse_workspace(
|
||||||
&config,
|
&config,
|
||||||
&flags.profile,
|
&flags.profile,
|
||||||
@@ -213,7 +222,11 @@ fn run_project(
|
|||||||
|
|
||||||
let kind = build_kind.trim();
|
let kind = build_kind.trim();
|
||||||
if run_cmd.is_none()
|
if run_cmd.is_none()
|
||||||
&& (kind == "staticlib" || kind == "sharedlib" || kind == "efi" || kind == "elf")
|
&& (kind == "staticlib"
|
||||||
|
|| kind == "sharedlib"
|
||||||
|
|| kind == "efi"
|
||||||
|
|| kind == "elf"
|
||||||
|
|| kind == "flat-bin")
|
||||||
{
|
{
|
||||||
return Err("Cannot run library build".to_string());
|
return Err("Cannot run library build".to_string());
|
||||||
}
|
}
|
||||||
|
|||||||
272
src/core/build/archive.rs
Normal file
272
src/core/build/archive.rs
Normal file
@@ -0,0 +1,272 @@
|
|||||||
|
// 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 crate::core::build_config::ArchiveConfig;
|
||||||
|
use fatfs::{FileSystem, FormatVolumeOptions, format_volume};
|
||||||
|
use glob::glob;
|
||||||
|
use std::fs::{self, File, OpenOptions};
|
||||||
|
use std::io::{self, Read, Seek, SeekFrom, Write};
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
struct OffsetFile<T> {
|
||||||
|
inner: T,
|
||||||
|
offset: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Read> Read for OffsetFile<T> {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||||
|
self.inner.read(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Write> Write for OffsetFile<T> {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
self.inner.write(buf)
|
||||||
|
}
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
self.inner.flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Seek> Seek for OffsetFile<T> {
|
||||||
|
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||||
|
match pos {
|
||||||
|
SeekFrom::Start(n) => {
|
||||||
|
let actual = self.inner.seek(SeekFrom::Start(n + self.offset))?;
|
||||||
|
if actual < self.offset {
|
||||||
|
Ok(0)
|
||||||
|
} else {
|
||||||
|
Ok(actual - self.offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SeekFrom::Current(n) => {
|
||||||
|
let actual = self.inner.seek(SeekFrom::Current(n))?;
|
||||||
|
if actual < self.offset {
|
||||||
|
Ok(0)
|
||||||
|
} else {
|
||||||
|
Ok(actual - self.offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SeekFrom::End(n) => {
|
||||||
|
let actual = self.inner.seek(SeekFrom::End(n))?;
|
||||||
|
if actual < self.offset {
|
||||||
|
Ok(0)
|
||||||
|
} else {
|
||||||
|
Ok(actual - self.offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_size(s: &str) -> Result<u64, String> {
|
||||||
|
let s = s.trim();
|
||||||
|
if s.is_empty() {
|
||||||
|
return Ok(0);
|
||||||
|
}
|
||||||
|
let mut num_str = String::new();
|
||||||
|
let mut unit = String::new();
|
||||||
|
for c in s.chars() {
|
||||||
|
if c.is_ascii_digit() || c == '.' {
|
||||||
|
num_str.push(c);
|
||||||
|
} else {
|
||||||
|
unit.push(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let val: f64 = num_str
|
||||||
|
.parse()
|
||||||
|
.map_err(|_| format!("invalid size number: {num_str}"))?;
|
||||||
|
let unit = unit.trim().to_lowercase();
|
||||||
|
let multiplier = match unit.as_str() {
|
||||||
|
"k" | "kb" => 1024.0,
|
||||||
|
"m" | "mb" => 1024.0 * 1024.0,
|
||||||
|
"g" | "gb" => 1024.0 * 1024.0 * 1024.0,
|
||||||
|
"" => 1.0,
|
||||||
|
_ => return Err(format!("unknown unit: {unit}")),
|
||||||
|
};
|
||||||
|
Ok((val * multiplier) as u64)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pack_archive(
|
||||||
|
project_root: &Path,
|
||||||
|
config: &ArchiveConfig,
|
||||||
|
profile: &str,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
let output_substituted = config.output.replace("{profile}", profile);
|
||||||
|
let output_path = project_root.join(&output_substituted);
|
||||||
|
if let Some(parent) = output_path.parent() {
|
||||||
|
fs::create_dir_all(parent)
|
||||||
|
.map_err(|e| format!("failed to create output parent dir: {e}"))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let size = if let Some(sz_str) = &config.size {
|
||||||
|
parse_size(sz_str)?
|
||||||
|
} else {
|
||||||
|
1474560
|
||||||
|
};
|
||||||
|
|
||||||
|
let offset = if let Some(off_str) = &config.offset {
|
||||||
|
parse_size(off_str)?
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.truncate(false)
|
||||||
|
.open(&output_path)
|
||||||
|
.map_err(|e| format!("failed to open output file: {e}"))?;
|
||||||
|
|
||||||
|
let current_len = file.metadata().map(|m| m.len()).unwrap_or(0);
|
||||||
|
if current_len < size {
|
||||||
|
file.set_len(size)
|
||||||
|
.map_err(|e| format!("failed to set image file size: {e}"))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if offset == 0
|
||||||
|
&& let Some(boot_rel) = &config.bootsector
|
||||||
|
{
|
||||||
|
let boot_rel_substituted = boot_rel.replace("{profile}", profile);
|
||||||
|
let boot_path = project_root.join(&boot_rel_substituted);
|
||||||
|
if boot_path.is_file() {
|
||||||
|
let mut boot_file =
|
||||||
|
File::open(&boot_path).map_err(|e| format!("failed to open bootsector: {e}"))?;
|
||||||
|
let mut boot_bytes = vec![0; 512];
|
||||||
|
let _ = boot_file
|
||||||
|
.read(&mut boot_bytes)
|
||||||
|
.map_err(|e| format!("failed to read bootsector: {e}"))?;
|
||||||
|
file.seek(SeekFrom::Start(0))
|
||||||
|
.map_err(|e| format!("failed to seek to start: {e}"))?;
|
||||||
|
file.write_all(&boot_bytes)
|
||||||
|
.map_err(|e| format!("failed to write bootsector: {e}"))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file.seek(SeekFrom::Start(0))
|
||||||
|
.map_err(|e| format!("failed to seek to start: {e}"))?;
|
||||||
|
|
||||||
|
let mut wrapper = OffsetFile {
|
||||||
|
inner: file,
|
||||||
|
offset,
|
||||||
|
};
|
||||||
|
|
||||||
|
let label = config.label.as_deref().unwrap_or("VOLUME");
|
||||||
|
let mut label_bytes = [b' '; 11];
|
||||||
|
let bytes_to_copy = label.len().min(11);
|
||||||
|
label_bytes[..bytes_to_copy].copy_from_slice(&label.as_bytes()[..bytes_to_copy]);
|
||||||
|
|
||||||
|
let fat_type = match config.format.to_lowercase().as_str() {
|
||||||
|
"fat12" => Some(fatfs::FatType::Fat12),
|
||||||
|
"fat16" => Some(fatfs::FatType::Fat16),
|
||||||
|
"fat32" => Some(fatfs::FatType::Fat32),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut options = FormatVolumeOptions::new().volume_label(label_bytes);
|
||||||
|
if let Some(t) = fat_type {
|
||||||
|
options = options.fat_type(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
format_volume(&mut wrapper, options)
|
||||||
|
.map_err(|e| format!("failed to format FAT volume: {e}"))?;
|
||||||
|
|
||||||
|
wrapper
|
||||||
|
.seek(SeekFrom::Start(0))
|
||||||
|
.map_err(|e| format!("failed to seek to start after format: {e}"))?;
|
||||||
|
|
||||||
|
let fs = FileSystem::new(wrapper, fatfs::FsOptions::new())
|
||||||
|
.map_err(|e| format!("failed to mount FAT fs: {e}"))?;
|
||||||
|
let root_dir = fs.root_dir();
|
||||||
|
|
||||||
|
for layout in &config.layout {
|
||||||
|
let from_substituted = layout.from.replace("{profile}", profile);
|
||||||
|
let from_pattern = project_root
|
||||||
|
.join(&from_substituted)
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let paths = if from_pattern.contains('*') || from_pattern.contains('?') {
|
||||||
|
let mut matched = Vec::new();
|
||||||
|
if let Ok(entries) = glob(&from_pattern) {
|
||||||
|
for path in entries.flatten() {
|
||||||
|
matched.push(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
matched
|
||||||
|
} else {
|
||||||
|
let p = project_root.join(&from_substituted);
|
||||||
|
if p.exists() { vec![p] } else { Vec::new() }
|
||||||
|
};
|
||||||
|
|
||||||
|
for path in paths {
|
||||||
|
if !path.is_file() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let filename = path.file_name().and_then(|n| n.to_str()).unwrap_or("");
|
||||||
|
if filename.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut target_filename = filename.to_string();
|
||||||
|
let mut target_dir_str = layout.to.trim_matches('/');
|
||||||
|
|
||||||
|
let is_glob = from_pattern.contains('*') || from_pattern.contains('?');
|
||||||
|
if !is_glob
|
||||||
|
&& !layout.to.ends_with('/')
|
||||||
|
&& let Some(last_part) = Path::new(&layout.to).file_name().and_then(|n| n.to_str())
|
||||||
|
&& last_part.contains('.')
|
||||||
|
{
|
||||||
|
target_filename = last_part.to_string();
|
||||||
|
let parent_path = Path::new(&layout.to).parent().unwrap_or(Path::new(""));
|
||||||
|
target_dir_str = parent_path.to_str().unwrap_or("").trim_matches('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut current_dir = root_dir.clone();
|
||||||
|
if !target_dir_str.is_empty() {
|
||||||
|
let parts = target_dir_str.split('/');
|
||||||
|
for part in parts {
|
||||||
|
if part.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
current_dir = match current_dir.open_dir(part) {
|
||||||
|
Ok(dir) => dir,
|
||||||
|
Err(_) => current_dir
|
||||||
|
.create_dir(part)
|
||||||
|
.map_err(|e| format!("failed to create dir {part}: {e}"))?,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut out_file = current_dir
|
||||||
|
.create_file(&target_filename)
|
||||||
|
.map_err(|e| format!("failed to create file {target_filename}: {e}"))?;
|
||||||
|
let mut in_file =
|
||||||
|
File::open(&path).map_err(|e| format!("failed to open input file: {e}"))?;
|
||||||
|
let mut buffer = Vec::new();
|
||||||
|
in_file
|
||||||
|
.read_to_end(&mut buffer)
|
||||||
|
.map_err(|e| format!("failed to read input file: {e}"))?;
|
||||||
|
out_file
|
||||||
|
.write_all(&buffer)
|
||||||
|
.map_err(|e| format!("failed to write to FAT file {target_filename}: {e}"))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -19,9 +19,130 @@ use crate::core::build::builder::BuildContext;
|
|||||||
use crate::core::build::common;
|
use crate::core::build::common;
|
||||||
use crate::platform;
|
use crate::platform;
|
||||||
use crate::utils::build::is_bare_metal_target;
|
use crate::utils::build::is_bare_metal_target;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
|
pub(crate) fn artifact_dir(ctx: &BuildContext) -> PathBuf {
|
||||||
|
match ctx.target_dir {
|
||||||
|
Some(dir) => Path::new(dir).to_path_buf(),
|
||||||
|
None => Path::new("./target").join(ctx.profile),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn flat_output_path(ctx: &BuildContext) -> String {
|
||||||
|
let name = ctx.output_filename.unwrap_or(ctx.project_name);
|
||||||
|
let ext = ctx.output_extension.unwrap_or("bin");
|
||||||
|
let file = if ext.is_empty() {
|
||||||
|
name.to_string()
|
||||||
|
} else {
|
||||||
|
format!("{name}.{ext}")
|
||||||
|
};
|
||||||
|
artifact_dir(ctx).join(file).to_string_lossy().to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn flat_source_output_path(ctx: &BuildContext, source: &str) -> String {
|
||||||
|
let stem = Path::new(source)
|
||||||
|
.file_stem()
|
||||||
|
.and_then(|s| s.to_str())
|
||||||
|
.unwrap_or("out");
|
||||||
|
let ext = ctx.output_extension.unwrap_or("bin");
|
||||||
|
let file = if ext.is_empty() {
|
||||||
|
stem.to_string()
|
||||||
|
} else {
|
||||||
|
format!("{stem}.{ext}")
|
||||||
|
};
|
||||||
|
artifact_dir(ctx).join(file).to_string_lossy().to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_objcopy() -> Result<String, String> {
|
||||||
|
for candidate in ["llvm-objcopy", "objcopy", "gobjcopy"] {
|
||||||
|
if Command::new(candidate)
|
||||||
|
.arg("--version")
|
||||||
|
.output()
|
||||||
|
.map(|o| o.status.success())
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
return Ok(candidate.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(
|
||||||
|
"flat-bin requires llvm-objcopy, objcopy, or gobjcopy in PATH \
|
||||||
|
(install binutils or LLVM tools)"
|
||||||
|
.to_string(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn objcopy_binary(ctx: &BuildContext, input: &str, output: &str) -> Result<(), String> {
|
||||||
|
if let Some(parent) = Path::new(output).parent() {
|
||||||
|
std::fs::create_dir_all(parent).map_err(|e| format!("output dir error: {e}"))?;
|
||||||
|
}
|
||||||
|
if !common::needs_rebuild(input, output) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let tool = resolve_objcopy()?;
|
||||||
|
let mut cmd = Command::new(&tool);
|
||||||
|
cmd.arg("-O").arg("binary").arg(input).arg(output);
|
||||||
|
if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() {
|
||||||
|
eprintln!("[dcr] {:?}", cmd);
|
||||||
|
}
|
||||||
|
common::run_command_sync_output(&mut cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn link_flat_binary(
|
||||||
|
ctx: &BuildContext,
|
||||||
|
objects: &[String],
|
||||||
|
default_linker: &str,
|
||||||
|
start_time: Instant,
|
||||||
|
) -> Result<f64, String> {
|
||||||
|
let out_path = flat_output_path(ctx);
|
||||||
|
if !common::needs_link(objects, &out_path) {
|
||||||
|
return Ok(common::elapsed_secs(start_time));
|
||||||
|
}
|
||||||
|
|
||||||
|
let intermediate = {
|
||||||
|
let dir = match ctx.target_dir {
|
||||||
|
Some(dir) => Path::new(dir).join("obj"),
|
||||||
|
None => Path::new("./target").join(ctx.profile).join("obj"),
|
||||||
|
};
|
||||||
|
std::fs::create_dir_all(&dir).map_err(|e| format!("obj dir error: {e}"))?;
|
||||||
|
dir.join(format!("{}.flat.elf", ctx.project_name))
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut cmd = Command::new(ctx.linker.unwrap_or(default_linker));
|
||||||
|
cmd.arg("-nostdlib").arg("-static");
|
||||||
|
for obj in objects {
|
||||||
|
cmd.arg(obj);
|
||||||
|
}
|
||||||
|
for dir in ctx.lib_dirs {
|
||||||
|
cmd.arg(format!("-L{dir}"));
|
||||||
|
}
|
||||||
|
for lib in ctx.libs {
|
||||||
|
cmd.arg(format!("-l{lib}"));
|
||||||
|
}
|
||||||
|
for flag in ctx.ldflags {
|
||||||
|
cmd.arg(flag);
|
||||||
|
}
|
||||||
|
cmd.arg("-o").arg(&intermediate);
|
||||||
|
|
||||||
|
if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() {
|
||||||
|
eprintln!("[dcr] {:?}", cmd);
|
||||||
|
}
|
||||||
|
let output = cmd.output().map_err(|err| format!("Build failed: {err}"))?;
|
||||||
|
if !output.status.success() {
|
||||||
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||||
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
|
return Err(format!(
|
||||||
|
"flat-bin link failed:\nstdout: {stdout}\nstderr: {stderr}"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
objcopy_binary(ctx, &intermediate, &out_path)?;
|
||||||
|
Ok(common::elapsed_secs(start_time))
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn archive_static(
|
pub(crate) fn archive_static(
|
||||||
ctx: &BuildContext,
|
ctx: &BuildContext,
|
||||||
objects: &[String],
|
objects: &[String],
|
||||||
@@ -56,6 +177,10 @@ pub(crate) fn link_binary(
|
|||||||
default_linker: &str,
|
default_linker: &str,
|
||||||
start_time: Instant,
|
start_time: Instant,
|
||||||
) -> Result<f64, String> {
|
) -> Result<f64, String> {
|
||||||
|
if crate::utils::build::is_flat_bin(ctx.kind) {
|
||||||
|
return link_flat_binary(ctx, objects, default_linker, start_time);
|
||||||
|
}
|
||||||
|
|
||||||
let mut cmd = Command::new(ctx.linker.unwrap_or(default_linker));
|
let mut cmd = Command::new(ctx.linker.unwrap_or(default_linker));
|
||||||
if ctx.kind == "sharedlib" {
|
if ctx.kind == "sharedlib" {
|
||||||
if cfg!(target_os = "macos") {
|
if cfg!(target_os = "macos") {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
use crate::core::build::builder::BuildContext;
|
use crate::core::build::builder::BuildContext;
|
||||||
use crate::core::build::builder::artifact;
|
use crate::core::build::builder::artifact;
|
||||||
use crate::core::build::common;
|
use crate::core::build::common;
|
||||||
use crate::utils::build::is_bare_metal_target;
|
use crate::utils::build::{is_bare_metal_target, is_compile_only, is_flat_bin};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
@@ -30,6 +30,9 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
|||||||
} else {
|
} else {
|
||||||
ctx.compiler
|
ctx.compiler
|
||||||
};
|
};
|
||||||
|
if is_flat_bin(ctx.kind) && ctx.qt {
|
||||||
|
return Err("flat-bin is not supported with build.qt = true".to_string());
|
||||||
|
}
|
||||||
let start_time = Instant::now();
|
let start_time = Instant::now();
|
||||||
let obj_dir = match ctx.target_dir {
|
let obj_dir = match ctx.target_dir {
|
||||||
Some(dir) => Path::new(dir).join("obj"),
|
Some(dir) => Path::new(dir).join("obj"),
|
||||||
@@ -55,6 +58,10 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
|||||||
)?;
|
)?;
|
||||||
let objects = build_objects(compiler, &sources, &obj_dir, ctx, "o", qt_include_path)?;
|
let objects = build_objects(compiler, &sources, &obj_dir, ctx, "o", qt_include_path)?;
|
||||||
|
|
||||||
|
if is_compile_only(ctx.kind) && !is_flat_bin(ctx.kind) {
|
||||||
|
return Ok(common::elapsed_secs(start_time));
|
||||||
|
}
|
||||||
|
|
||||||
if ctx.kind == "staticlib" {
|
if ctx.kind == "staticlib" {
|
||||||
return artifact::archive_static(ctx, &objects, start_time);
|
return artifact::archive_static(ctx, &objects, start_time);
|
||||||
}
|
}
|
||||||
@@ -138,7 +145,7 @@ fn build_object(
|
|||||||
cmd.arg("-fPIC");
|
cmd.arg("-fPIC");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx.freestanding || is_bare_metal_target(ctx.target))
|
if (ctx.freestanding || is_bare_metal_target(ctx.target) || is_flat_bin(ctx.kind))
|
||||||
&& ctx.language.to_lowercase() != "asm"
|
&& ctx.language.to_lowercase() != "asm"
|
||||||
{
|
{
|
||||||
cmd.arg("-ffreestanding");
|
cmd.arg("-ffreestanding");
|
||||||
@@ -174,8 +181,8 @@ fn build_object(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let use_dcr_defaults =
|
let use_dcr_defaults = ctx.cflags.is_empty()
|
||||||
ctx.cflags.is_empty() && !(ctx.freestanding || is_bare_metal_target(ctx.target));
|
&& !(ctx.freestanding || is_bare_metal_target(ctx.target) || is_flat_bin(ctx.kind));
|
||||||
if use_dcr_defaults {
|
if use_dcr_defaults {
|
||||||
for flag in default_flags(ctx.profile) {
|
for flag in default_flags(ctx.profile) {
|
||||||
cmd.arg(flag);
|
cmd.arg(flag);
|
||||||
|
|||||||
@@ -16,8 +16,10 @@
|
|||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use crate::core::build::builder::BuildContext;
|
use crate::core::build::builder::BuildContext;
|
||||||
|
use crate::core::build::builder::artifact;
|
||||||
use crate::core::build::common;
|
use crate::core::build::common;
|
||||||
use crate::platform;
|
use crate::platform;
|
||||||
|
use crate::utils::build::{is_compile_only, is_flat_bin};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
@@ -33,6 +35,9 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
|||||||
if lang.contains("asm") {
|
if lang.contains("asm") {
|
||||||
return Err("MSVC backend does not support build.language with asm".to_string());
|
return Err("MSVC backend does not support build.language with asm".to_string());
|
||||||
}
|
}
|
||||||
|
if is_flat_bin(ctx.kind) && ctx.qt {
|
||||||
|
return Err("flat-bin is not supported with build.qt = true".to_string());
|
||||||
|
}
|
||||||
let start_time = Instant::now();
|
let start_time = Instant::now();
|
||||||
let sources = collect_sources(ctx)?;
|
let sources = collect_sources(ctx)?;
|
||||||
let obj_dir = match ctx.target_dir {
|
let obj_dir = match ctx.target_dir {
|
||||||
@@ -41,6 +46,14 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
|||||||
};
|
};
|
||||||
let objects = build_objects(compiler, &sources, &obj_dir, ctx, "obj")?;
|
let objects = build_objects(compiler, &sources, &obj_dir, ctx, "obj")?;
|
||||||
|
|
||||||
|
if is_compile_only(ctx.kind) && !is_flat_bin(ctx.kind) {
|
||||||
|
return Ok(common::elapsed_secs(start_time));
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_flat_bin(ctx.kind) {
|
||||||
|
return link_flat_msvc(ctx, compiler, &objects, &obj_dir, start_time);
|
||||||
|
}
|
||||||
|
|
||||||
if ctx.kind == "staticlib" {
|
if ctx.kind == "staticlib" {
|
||||||
let lib_path = platform::lib_path(ctx.profile, ctx.project_name, ctx.target_dir);
|
let lib_path = platform::lib_path(ctx.profile, ctx.project_name, ctx.target_dir);
|
||||||
if !common::needs_link(&objects, &lib_path) {
|
if !common::needs_link(&objects, &lib_path) {
|
||||||
@@ -141,6 +154,61 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn link_flat_msvc(
|
||||||
|
ctx: &BuildContext,
|
||||||
|
compiler: &str,
|
||||||
|
objects: &[String],
|
||||||
|
obj_dir: &Path,
|
||||||
|
start_time: Instant,
|
||||||
|
) -> Result<f64, String> {
|
||||||
|
let out_path = artifact::flat_output_path(ctx);
|
||||||
|
if !common::needs_link(objects, &out_path) {
|
||||||
|
return Ok(common::elapsed_secs(start_time));
|
||||||
|
}
|
||||||
|
fs::create_dir_all(obj_dir).map_err(|e| format!("obj dir error: {e}"))?;
|
||||||
|
let intermediate = obj_dir
|
||||||
|
.join(format!("{}.flat.exe", ctx.project_name))
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let mut cmd = Command::new(compiler);
|
||||||
|
cmd.arg("/nologo");
|
||||||
|
for obj in objects {
|
||||||
|
cmd.arg(obj);
|
||||||
|
}
|
||||||
|
for flag in ctx.cflags {
|
||||||
|
cmd.arg(flag);
|
||||||
|
}
|
||||||
|
for dir in ctx.lib_dirs {
|
||||||
|
cmd.arg(format!("/LIBPATH:{dir}"));
|
||||||
|
}
|
||||||
|
for lib in ctx.libs {
|
||||||
|
if lib.to_lowercase().ends_with(".lib") {
|
||||||
|
cmd.arg(lib);
|
||||||
|
} else {
|
||||||
|
cmd.arg(format!("{lib}.lib"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cmd.arg(format!("/Fe:{intermediate}"));
|
||||||
|
if !ctx.ldflags.is_empty() {
|
||||||
|
cmd.arg("/link");
|
||||||
|
for flag in ctx.ldflags {
|
||||||
|
cmd.arg(flag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() {
|
||||||
|
eprintln!("[dcr] {:?}", cmd);
|
||||||
|
}
|
||||||
|
let status = cmd
|
||||||
|
.status()
|
||||||
|
.map_err(|e| format!("flat-bin link failed: {e}"))?;
|
||||||
|
if !status.success() {
|
||||||
|
return Err("flat-bin link failed".to_string());
|
||||||
|
}
|
||||||
|
artifact::objcopy_binary(ctx, &intermediate, &out_path)?;
|
||||||
|
Ok(common::elapsed_secs(start_time))
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn collect_sources(ctx: &BuildContext) -> Result<Vec<String>, String> {
|
pub(crate) fn collect_sources(ctx: &BuildContext) -> Result<Vec<String>, String> {
|
||||||
let extensions = source_extensions(ctx.language);
|
let extensions = source_extensions(ctx.language);
|
||||||
common::collect_sources(
|
common::collect_sources(
|
||||||
|
|||||||
@@ -107,6 +107,10 @@ fn build_cache_path(profile: &str, target_dir: Option<&str>) -> PathBuf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn build_output_path(ctx: &BuildContext) -> String {
|
fn build_output_path(ctx: &BuildContext) -> String {
|
||||||
|
if crate::utils::build::is_flat_bin(ctx.kind) {
|
||||||
|
return crate::core::build::builder::artifact::flat_output_path(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
let name = ctx.output_filename.unwrap_or(ctx.project_name);
|
let name = ctx.output_filename.unwrap_or(ctx.project_name);
|
||||||
let ext = ctx.output_extension.unwrap_or("");
|
let ext = ctx.output_extension.unwrap_or("");
|
||||||
|
|
||||||
@@ -159,7 +163,13 @@ pub(crate) fn collect_header_files(
|
|||||||
if !root.exists() {
|
if !root.exists() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
collect_header_files_rec(&root, &mut out, ctx.exclude_dirs, ctx.include_paths)?;
|
if root.is_file() {
|
||||||
|
if is_header_file(&root) {
|
||||||
|
out.push(root);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
collect_header_files_rec(&root, &mut out, ctx.exclude_dirs, ctx.include_paths)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
out.sort();
|
out.sort();
|
||||||
out.dedup();
|
out.dedup();
|
||||||
|
|||||||
@@ -35,7 +35,15 @@ pub fn collect_sources(
|
|||||||
) -> Result<Vec<String>, String> {
|
) -> Result<Vec<String>, String> {
|
||||||
let mut sources = Vec::new();
|
let mut sources = Vec::new();
|
||||||
for root in roots {
|
for root in roots {
|
||||||
collect_sources_rec(root, extensions, &mut sources, exclude_dirs, include_paths)?;
|
if root.is_file() {
|
||||||
|
if let Some(ext) = root.extension().and_then(|e| e.to_str())
|
||||||
|
&& extensions.contains(&ext.to_lowercase().as_str())
|
||||||
|
{
|
||||||
|
sources.push(root.to_string_lossy().to_string());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
collect_sources_rec(root, extensions, &mut sources, exclude_dirs, include_paths)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sources.sort();
|
sources.sort();
|
||||||
Ok(sources)
|
Ok(sources)
|
||||||
|
|||||||
@@ -165,6 +165,9 @@ fn build_all(
|
|||||||
&mut *rep,
|
&mut *rep,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
if let Some(archive) = &config.typed().archive {
|
||||||
|
super::archive::pack_archive(root, archive, profile)?;
|
||||||
|
}
|
||||||
return Ok(0.0);
|
return Ok(0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -843,7 +846,10 @@ fn build_project_at(
|
|||||||
version_suffix: &version_info.suffix,
|
version_suffix: &version_info.suffix,
|
||||||
version_suffix_dash: &version_info.suffix_dash,
|
version_suffix_dash: &version_info.suffix_dash,
|
||||||
};
|
};
|
||||||
let steps_dirty = build_steps_need_run(&build_steps, &step_vars)?;
|
let mut steps_dirty = build_steps_need_run(&build_steps, &step_vars)?;
|
||||||
|
if force {
|
||||||
|
steps_dirty = true;
|
||||||
|
}
|
||||||
if steps_dirty {
|
if steps_dirty {
|
||||||
clean_generated_files(&build_generated)?;
|
clean_generated_files(&build_generated)?;
|
||||||
run_build_steps(
|
run_build_steps(
|
||||||
@@ -884,7 +890,10 @@ fn build_project_at(
|
|||||||
if ctx.package_type == Some("lib") || ctx.kind == "staticlib" || ctx.kind == "sharedlib" {
|
if ctx.package_type == Some("lib") || ctx.kind == "staticlib" || ctx.kind == "sharedlib" {
|
||||||
package_library(&ctx, &headers, project_root)?;
|
package_library(&ctx, &headers, project_root)?;
|
||||||
}
|
}
|
||||||
let post_steps_dirty = build_steps_need_run(&build_post_steps, &step_vars)?;
|
let mut post_steps_dirty = build_steps_need_run(&build_post_steps, &step_vars)?;
|
||||||
|
if force {
|
||||||
|
post_steps_dirty = true;
|
||||||
|
}
|
||||||
if post_steps_dirty {
|
if post_steps_dirty {
|
||||||
run_build_steps(
|
run_build_steps(
|
||||||
&build_post_steps,
|
&build_post_steps,
|
||||||
@@ -896,6 +905,9 @@ fn build_project_at(
|
|||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
verify_expectations(&build_expects, &step_vars)?;
|
verify_expectations(&build_expects, &step_vars)?;
|
||||||
|
if let Some(archive) = &config.typed().archive {
|
||||||
|
super::archive::pack_archive(project_root, archive, profile)?;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn find_target_root(dir: &str, fallback: &Path) -> PathBuf {
|
fn find_target_root(dir: &str, fallback: &Path) -> PathBuf {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use crate::core::build::builder::BuildContext;
|
use crate::core::build::builder::BuildContext;
|
||||||
use crate::core::build::builder::artifact;
|
use crate::core::build::builder::artifact;
|
||||||
use crate::core::build::common;
|
use crate::core::build::common;
|
||||||
|
use crate::utils::build::{is_compile_only, is_flat_bin};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
@@ -22,13 +23,63 @@ pub fn build_assembly<F>(
|
|||||||
extensions: &[&str],
|
extensions: &[&str],
|
||||||
build_object: F,
|
build_object: F,
|
||||||
) -> Result<f64, String>
|
) -> Result<f64, String>
|
||||||
|
where
|
||||||
|
F: Fn(&str, &str, &str, &BuildContext) -> Result<(), String> + Sync,
|
||||||
|
{
|
||||||
|
build_assembly_with_mode(
|
||||||
|
ctx,
|
||||||
|
backend_name,
|
||||||
|
default_assembler,
|
||||||
|
extensions,
|
||||||
|
FlatEmit::Direct,
|
||||||
|
build_object,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_assembly_via_objcopy<F>(
|
||||||
|
ctx: &BuildContext,
|
||||||
|
backend_name: &str,
|
||||||
|
default_assembler: &str,
|
||||||
|
extensions: &[&str],
|
||||||
|
build_object: F,
|
||||||
|
) -> Result<f64, String>
|
||||||
|
where
|
||||||
|
F: Fn(&str, &str, &str, &BuildContext) -> Result<(), String> + Sync,
|
||||||
|
{
|
||||||
|
build_assembly_with_mode(
|
||||||
|
ctx,
|
||||||
|
backend_name,
|
||||||
|
default_assembler,
|
||||||
|
extensions,
|
||||||
|
FlatEmit::ViaObjcopy,
|
||||||
|
build_object,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
enum FlatEmit {
|
||||||
|
Direct,
|
||||||
|
ViaObjcopy,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_assembly_with_mode<F>(
|
||||||
|
ctx: &BuildContext,
|
||||||
|
backend_name: &str,
|
||||||
|
default_assembler: &str,
|
||||||
|
extensions: &[&str],
|
||||||
|
flat_mode: FlatEmit,
|
||||||
|
build_object: F,
|
||||||
|
) -> Result<f64, String>
|
||||||
where
|
where
|
||||||
F: Fn(&str, &str, &str, &BuildContext) -> Result<(), String> + Sync,
|
F: Fn(&str, &str, &str, &BuildContext) -> Result<(), String> + Sync,
|
||||||
{
|
{
|
||||||
let lang = ctx.language.to_lowercase();
|
let lang = ctx.language.to_lowercase();
|
||||||
if lang.split(',').any(|p| p.trim() != "asm") {
|
if lang.split(',').any(|p| {
|
||||||
|
let t = p.trim();
|
||||||
|
t != "asm" && t != "llvm_ir" && t != "llvm-ir" && t != "ll"
|
||||||
|
}) {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"{backend_name} backend requires build.language = \"asm\""
|
"{backend_name} backend requires build.language = \"asm\" (or llvm_ir for LLC)"
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,11 +96,50 @@ where
|
|||||||
ctx.exclude_dirs,
|
ctx.exclude_dirs,
|
||||||
ctx.include_paths,
|
ctx.include_paths,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
let flat = is_flat_bin(ctx.kind);
|
||||||
let obj_dir = match ctx.target_dir {
|
let obj_dir = match ctx.target_dir {
|
||||||
Some(dir) => Path::new(dir).join("obj"),
|
Some(dir) => Path::new(dir).join("obj"),
|
||||||
None => Path::new("./target").join(ctx.profile).join("obj"),
|
None => Path::new("./target").join(ctx.profile).join("obj"),
|
||||||
};
|
};
|
||||||
let objects = build_objects_common(assembler, &sources, &obj_dir, ctx, &build_object)?;
|
|
||||||
|
let objects = match (flat, flat_mode) {
|
||||||
|
(true, FlatEmit::Direct) => {
|
||||||
|
let outs: Vec<String> = sources
|
||||||
|
.iter()
|
||||||
|
.map(|s| artifact::flat_source_output_path(ctx, s))
|
||||||
|
.collect();
|
||||||
|
common::parallel_build(
|
||||||
|
sources.len(),
|
||||||
|
|i| build_object(assembler, &sources[i], &outs[i], ctx),
|
||||||
|
ctx.codegen_units,
|
||||||
|
)?;
|
||||||
|
outs
|
||||||
|
}
|
||||||
|
(true, FlatEmit::ViaObjcopy) => {
|
||||||
|
let intermediates: Vec<String> = sources
|
||||||
|
.iter()
|
||||||
|
.map(|s| common::object_path(&obj_dir, s, "o"))
|
||||||
|
.collect();
|
||||||
|
common::parallel_build(
|
||||||
|
sources.len(),
|
||||||
|
|i| build_object(assembler, &sources[i], &intermediates[i], ctx),
|
||||||
|
ctx.codegen_units,
|
||||||
|
)?;
|
||||||
|
let mut outs = Vec::with_capacity(sources.len());
|
||||||
|
for (i, src) in sources.iter().enumerate() {
|
||||||
|
let out = artifact::flat_source_output_path(ctx, src);
|
||||||
|
artifact::objcopy_binary(ctx, &intermediates[i], &out)?;
|
||||||
|
outs.push(out);
|
||||||
|
}
|
||||||
|
outs
|
||||||
|
}
|
||||||
|
(false, _) => build_objects_common(assembler, &sources, &obj_dir, ctx, &build_object)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
if is_compile_only(ctx.kind) {
|
||||||
|
return Ok(common::elapsed_secs(start_time));
|
||||||
|
}
|
||||||
|
|
||||||
if ctx.kind == "staticlib" {
|
if ctx.kind == "staticlib" {
|
||||||
return artifact::archive_static(ctx, &objects, start_time);
|
return artifact::archive_static(ctx, &objects, start_time);
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
|||||||
.map(|s| s.trim().to_string())
|
.map(|s| s.trim().to_string())
|
||||||
.filter(|s| !s.is_empty())
|
.filter(|s| !s.is_empty())
|
||||||
.unwrap_or_else(|| "as".to_string());
|
.unwrap_or_else(|| "as".to_string());
|
||||||
asm::build_assembly(ctx, "GAS", &as_path, &["s"], build_object)
|
asm::build_assembly_via_objcopy(ctx, "GAS", &as_path, &["s"], build_object)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn collect_sources(ctx: &BuildContext) -> Result<Vec<String>, String> {
|
pub(crate) fn collect_sources(ctx: &BuildContext) -> Result<Vec<String>, String> {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use std::path::Path;
|
|||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
||||||
asm::build_assembly(ctx, "MASM", "ml", &["asm"], build_object)
|
asm::build_assembly_via_objcopy(ctx, "MASM", "ml", &["asm"], build_object)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn collect_sources(ctx: &BuildContext) -> Result<Vec<String>, String> {
|
pub(crate) fn collect_sources(ctx: &BuildContext) -> Result<Vec<String>, String> {
|
||||||
|
|||||||
@@ -50,7 +50,11 @@ fn build_object(
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let format = nasm_format(ctx.platform);
|
let format = if crate::utils::build::is_flat_bin(ctx.kind) {
|
||||||
|
"bin"
|
||||||
|
} else {
|
||||||
|
nasm_format(ctx.platform)
|
||||||
|
};
|
||||||
|
|
||||||
let mut cmd = Command::new(assembler);
|
let mut cmd = Command::new(assembler);
|
||||||
cmd.arg("-f")
|
cmd.arg("-f")
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ impl Language for LlvmIr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
||||||
asm::build_assembly(ctx, "LLVM-IR", "llc", &["ll"], build_object)
|
asm::build_assembly_via_objcopy(ctx, "LLVM-IR", "llc", &["ll"], build_object)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn collect_sources(ctx: &BuildContext) -> Result<Vec<String>, String> {
|
pub(crate) fn collect_sources(ctx: &BuildContext) -> Result<Vec<String>, String> {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
pub mod archive;
|
||||||
pub mod builder;
|
pub mod builder;
|
||||||
pub mod cache;
|
pub mod cache;
|
||||||
pub mod common;
|
pub mod common;
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ const VALID_KINDS: &[&str] = &[
|
|||||||
"elf",
|
"elf",
|
||||||
"none",
|
"none",
|
||||||
"custom",
|
"custom",
|
||||||
|
"flat-bin",
|
||||||
];
|
];
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -102,6 +103,27 @@ pub struct DcrConfig {
|
|||||||
pub toolchain: Option<ToolchainConfig>,
|
pub toolchain: Option<ToolchainConfig>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub workspace: BTreeMap<String, WorkspaceMemberConfig>,
|
pub workspace: BTreeMap<String, WorkspaceMemberConfig>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub archive: Option<ArchiveConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct ArchiveConfig {
|
||||||
|
pub output: String,
|
||||||
|
pub format: String,
|
||||||
|
pub size: Option<String>,
|
||||||
|
pub offset: Option<String>,
|
||||||
|
pub label: Option<String>,
|
||||||
|
pub bootsector: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub layout: Vec<ArchiveLayout>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct ArchiveLayout {
|
||||||
|
pub from: String,
|
||||||
|
pub to: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
|||||||
@@ -86,6 +86,14 @@ pub fn normalize_kind(kind: &str) -> &str {
|
|||||||
if trimmed.is_empty() { "bin" } else { trimmed }
|
if trimmed.is_empty() { "bin" } else { trimmed }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_flat_bin(kind: &str) -> bool {
|
||||||
|
kind == "flat-bin"
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_compile_only(kind: &str) -> bool {
|
||||||
|
matches!(kind, "none" | "custom" | "flat-bin")
|
||||||
|
}
|
||||||
|
|
||||||
pub fn normalize_platform(platform: &str) -> Option<&str> {
|
pub fn normalize_platform(platform: &str) -> Option<&str> {
|
||||||
let trimmed = platform.trim();
|
let trimmed = platform.trim();
|
||||||
if trimmed.is_empty() {
|
if trimmed.is_empty() {
|
||||||
|
|||||||
Reference in New Issue
Block a user