mirror of
https://github.com/dexoron/dcr.git
synced 2026-10-21 14:18:36 +00:00
Added:
- `build.kind = "efi"` — UEFI PE32+ executable support. Links with `-shared -nostdlib -Wl,-dll -Wl,--subsystem,10`, output via `efi_path()` with `.efi` extension, rejected by `dcr run`.
- `build.kind = "elf"` — bare-metal ELF executable support. Uses `elf_path()` output path, rejected by `dcr run`, no extra linker flags.
Fixed:
- `dcr run` with `--target` or `build.target` now correctly finds the binary in the arch-specific target directory.
- `dcr run` checks profile-specific `build.{profile}.kind` before rejecting library builds (previously only checked `build.kind`).
- `dcr clean` now reads `build.target` from `dcr.toml` when no `--target` flag is given.
- Linux: target directory is now consistent — `target/<arch>-unknown-linux-gnu/<profile>/` (no stale `target/<profile>/`).
- `dcr.lock` is now populated with all resolved dependencies (registry, path, git) instead of being always empty.
- Git dependencies are now properly recognized and recorded in `dcr.lock`.
- `is_registry_dep` correctly identifies version-based registry dependency tables (`{ version = "..." }`) and rejects tables with unknown keys.
- `dcr test` accepts `--debug`/`--release` flags (defaults to `debug` profile instead of always `release`).
- Build fingerprint cache (`build_cache_path`) now includes `target_dir` — different targets no longer share a single cache entry.
- `object_path` no longer hardcodes the `src/` prefix — works correctly with custom `build.roots` (e.g. `roots = ["lib"]`).
- `dcr update` now generates `.exe` candidates for all Windows targets, not just `x86_64-pc-windows-msvc`.
- Compiler existence is verified before starting the build (clear error if compiler is not found in PATH).
- Consolidated duplicate `OUTPUT_MUTEX` definitions — MSVC backend now uses the shared mutex from `common.rs`.
- Registry cache root directory is created if missing (prevents cryptic errors when `~/.dcr/` does not exist).
This commit is contained in:
18
CHANGELOG.md
18
CHANGELOG.md
@@ -1,9 +1,11 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## [0.6.7] - 2026-05-21
|
## [0.6.7] - 2026-05-26
|
||||||
|
|
||||||
Added:
|
Added:
|
||||||
|
|
||||||
|
- `build.kind = "efi"` — UEFI PE32+ executable support. Links with `-shared -nostdlib -Wl,-dll -Wl,--subsystem,10`, output via `efi_path()` with `.efi` extension, rejected by `dcr run`.
|
||||||
|
- `build.kind = "elf"` — bare-metal ELF executable support. Uses `elf_path()` output path, rejected by `dcr run`, no extra linker flags.
|
||||||
- `build.filename` and `build.extension` in `dcr.toml` — complete control over the final artifact name without relying on `package.name`.
|
- `build.filename` and `build.extension` in `dcr.toml` — complete control over the final artifact name without relying on `package.name`.
|
||||||
Example:
|
Example:
|
||||||
```toml
|
```toml
|
||||||
@@ -21,6 +23,20 @@ Added:
|
|||||||
Fixed:
|
Fixed:
|
||||||
|
|
||||||
- `build.target` declared in `dcr.toml` is now correctly used as the default target even when `--target` is not passed on the command line (previously the host target was always forced for config resolution).
|
- `build.target` declared in `dcr.toml` is now correctly used as the default target even when `--target` is not passed on the command line (previously the host target was always forced for config resolution).
|
||||||
|
- `dcr run` with `--target` or `build.target` now correctly finds the binary in the arch-specific target directory.
|
||||||
|
- `dcr run` checks profile-specific `build.{profile}.kind` before rejecting library builds (previously only checked `build.kind`).
|
||||||
|
- `dcr clean` now reads `build.target` from `dcr.toml` when no `--target` flag is given.
|
||||||
|
- Linux: target directory is now consistent — `target/<arch>-unknown-linux-gnu/<profile>/` (no stale `target/<profile>/`).
|
||||||
|
- `dcr.lock` is now populated with all resolved dependencies (registry, path, git) instead of being always empty.
|
||||||
|
- Git dependencies are now properly recognized and recorded in `dcr.lock`.
|
||||||
|
- `is_registry_dep` correctly identifies version-based registry dependency tables (`{ version = "..." }`) and rejects tables with unknown keys.
|
||||||
|
- `dcr test` accepts `--debug`/`--release` flags (defaults to `debug` profile instead of always `release`).
|
||||||
|
- Build fingerprint cache (`build_cache_path`) now includes `target_dir` — different targets no longer share a single cache entry.
|
||||||
|
- `object_path` no longer hardcodes the `src/` prefix — works correctly with custom `build.roots` (e.g. `roots = ["lib"]`).
|
||||||
|
- `dcr update` now generates `.exe` candidates for all Windows targets, not just `x86_64-pc-windows-msvc`.
|
||||||
|
- Compiler existence is verified before starting the build (clear error if compiler is not found in PATH).
|
||||||
|
- Consolidated duplicate `OUTPUT_MUTEX` definitions — MSVC backend now uses the shared mutex from `common.rs`.
|
||||||
|
- Registry cache root directory is created if missing (prevents cryptic errors when `~/.dcr/` does not exist).
|
||||||
|
|
||||||
## [0.6.6] - 2026-05-18
|
## [0.6.6] - 2026-05-18
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ The current implementation is written in Rust.
|
|||||||
- Clean build artifacts
|
- Clean build artifacts
|
||||||
- Generate IDE integration files (VS Code, CLion) and compilation databases
|
- Generate IDE integration files (VS Code, CLion) and compilation databases
|
||||||
- Generate a minimal C project template
|
- Generate a minimal C project template
|
||||||
- Build static and shared libraries
|
- Build static, shared libraries, UEFI PE32+ executables, and bare-metal ELF executables
|
||||||
- Dependency management:
|
- Dependency management:
|
||||||
- Git and Path dependencies with `dcr.lock`
|
- Git and Path dependencies with `dcr.lock`
|
||||||
- Registry-based dependency resolution
|
- Registry-based dependency resolution
|
||||||
@@ -154,7 +154,9 @@ You can also set `build.target` to override the output directory (profile-indepe
|
|||||||
You can set `build.platform` to pass `-march=<platform>` (GCC/Clang) or `/arch:*` (MSVC) where supported.
|
You can set `build.platform` to pass `-march=<platform>` (GCC/Clang) or `/arch:*` (MSVC) where supported.
|
||||||
Use `build.kind = "staticlib"` to build a static library instead of a binary.
|
Use `build.kind = "staticlib"` to build a static library instead of a binary.
|
||||||
Use `build.kind = "sharedlib"` to build a shared library (`.so`/`.dylib`/`.dll`).
|
Use `build.kind = "sharedlib"` to build a shared library (`.so`/`.dylib`/`.dll`).
|
||||||
`dcr run` is only for `build.kind = "bin"` and will fail for libraries.
|
Use `build.kind = "efi"` to build a UEFI PE32+ executable (`.efi`).
|
||||||
|
Use `build.kind = "elf"` to build a bare-metal ELF executable.
|
||||||
|
`dcr run` is only for `build.kind = "bin"` and will fail for libraries, EFI, and ELF.
|
||||||
Use `build.exclude`/`build.include` to control source/header collection.
|
Use `build.exclude`/`build.include` to control source/header collection.
|
||||||
Use `build.roots` and `build.src_disable` to replace the default `src/` root.
|
Use `build.roots` and `build.src_disable` to replace the default `src/` root.
|
||||||
Use `build.steps` and `build.post_steps` to run custom commands.
|
Use `build.steps` and `build.post_steps` to run custom commands.
|
||||||
@@ -203,7 +205,7 @@ type = "app" # or "lib", "none"
|
|||||||
language = "c"
|
language = "c"
|
||||||
standard = "c11"
|
standard = "c11"
|
||||||
compiler = "clang"
|
compiler = "clang"
|
||||||
kind = "bin" # "bin", "staticlib", "sharedlib"
|
kind = "bin" # "bin", "staticlib", "sharedlib", "efi", "elf"
|
||||||
# Optional platform hint
|
# Optional platform hint
|
||||||
# platform = "x86_64"
|
# platform = "x86_64"
|
||||||
# Optional custom flags
|
# Optional custom flags
|
||||||
|
|||||||
@@ -64,7 +64,9 @@ dcr build --debug --clean
|
|||||||
- Incremental rebuild checks source/object mtime and tracked header dependencies.
|
- Incremental rebuild checks source/object mtime and tracked header dependencies.
|
||||||
- `--force` skips build cache checks and recompiles.
|
- `--force` skips build cache checks and recompiles.
|
||||||
- `--clean` removes `target/<profile>` and `build.clean` paths before building.
|
- `--clean` removes `target/<profile>` and `build.clean` paths before building.
|
||||||
- `build.filename` + `build.extension` allow full control over the final artifact name (e.g. `KERNEL.BIN` instead of using `package.name`).
|
- `build.filename` + `build.extension` allow full control over the final artifact name (e.g. `KERNEL.BIN` instead of using `package.name`). When `build.kind = "efi"`, the output is a PE32+ executable with `.efi` extension.
|
||||||
|
- `build.kind = "efi"` links with `-shared -nostdlib -Wl,-dll -Wl,--subsystem,10` for UEFI PE32+ format. Use `build.filename` and `build.extension` for full control over the artifact name.
|
||||||
|
- `build.kind = "elf"` produces a bare-metal ELF executable. Linking flags are user-controlled via `build.ldflags` (no PIE/CRT defaults are added).
|
||||||
- Default GCC/Clang profile flags: `debug` -> `-O0 -g -Wall -Wextra -fno-omit-frame-pointer -DDCR_DEBUG`, `release` -> `-O3 -DNDEBUG`.
|
- Default GCC/Clang profile flags: `debug` -> `-O0 -g -Wall -Wextra -fno-omit-frame-pointer -DDCR_DEBUG`, `release` -> `-O3 -DNDEBUG`.
|
||||||
These defaults are **not applied** for bare-metal targets (e.g. `aarch64-none-elf`, targets containing `none` / `-elf` / `eabi`).
|
These defaults are **not applied** for bare-metal targets (e.g. `aarch64-none-elf`, targets containing `none` / `-elf` / `eabi`).
|
||||||
- For `language = "asm"` with `compiler = "as"`/`"gas"`, use `.s` files (no preprocessing). For `.S`, use `gcc` or `clang`.
|
- For `language = "asm"` with `compiler = "as"`/`"gas"`, use `.s` files (no preprocessing). For `.S`, use `gcc` or `clang`.
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ dcr clean --release --all
|
|||||||
## Behavior
|
## Behavior
|
||||||
|
|
||||||
- Without arguments: removes entire `target/`.
|
- Without arguments: removes entire `target/`.
|
||||||
- With profile: removes only `target/<profile>/`.
|
- With profile: removes only the profile-specific directory. Respects `build.target` from config: if `build.target = "aarch64-unknown-linux-gnu"`, cleans `target/aarch64-unknown-linux-gnu/<profile>/`.
|
||||||
- With `--all`: also cleans all workspace members.
|
- With `--all`: also cleans all workspace members.
|
||||||
- Additionally removes paths from `build.clean` if configured.
|
- Additionally removes paths from `build.clean` if configured.
|
||||||
- `build.clean` supports `{profile}` and version placeholders.
|
- `build.clean` supports `{profile}` and version placeholders.
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ dcr run --debug --clean
|
|||||||
|
|
||||||
## Restrictions
|
## Restrictions
|
||||||
|
|
||||||
- If `build.kind = "staticlib"` or `build.kind = "sharedlib"`, `run` exits with error.
|
- If `build.kind` (or `build.{profile}.kind`) is `"staticlib"` or `"sharedlib"`, `run` exits with error.
|
||||||
- The profile is selected from the first argument.
|
- The profile is selected from the first argument.
|
||||||
- `--target <triple>` runs the binary for specified target (short names: `linux`, `macos`, `windows`).
|
- `--target <triple>` runs the binary for specified target (short names: `linux`, `macos`, `windows`).
|
||||||
- `--force` skips build cache checks and recompiles.
|
- `--force` skips build cache checks and recompiles.
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ Runs the project's test suite.
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
dcr test
|
dcr test
|
||||||
|
dcr test --debug
|
||||||
dcr test --release
|
dcr test --release
|
||||||
dcr test --target linux
|
dcr test --target linux
|
||||||
dcr test --init
|
dcr test --init
|
||||||
@@ -39,6 +40,6 @@ DCR uses a simple test framework:
|
|||||||
|
|
||||||
- Test binary is linked with the main project (for lib projects) or standalone.
|
- Test binary is linked with the main project (for lib projects) or standalone.
|
||||||
- `--init` creates `tests/dcr_test.h` and `tests/test.c` if missing.
|
- `--init` creates `tests/dcr_test.h` and `tests/test.c` if missing.
|
||||||
- Tests run in release mode by default for performance.
|
- Tests run in debug mode by default. Use `--release` for release mode.
|
||||||
- Custom test logic can be added by editing `tests/test.c`.
|
- Custom test logic can be added by editing `tests/test.c`.
|
||||||
- Fails if no `tests/test.c` exists (run `dcr test --init` first).
|
- Fails if no `tests/test.c` exists (run `dcr test --init` first).
|
||||||
@@ -33,7 +33,7 @@ kind = "bin"
|
|||||||
Mixed languages can be specified as an array, for example `["c", "c++", "asm"]`.
|
Mixed languages can be specified as an array, for example `["c", "c++", "asm"]`.
|
||||||
- `standard` (string, required): language standard passed to compiler.
|
- `standard` (string, required): language standard passed to compiler.
|
||||||
- `compiler` (string, required): compiler command (for example `clang`, `gcc`, `cl`).
|
- `compiler` (string, required): compiler command (for example `clang`, `gcc`, `cl`).
|
||||||
- `kind` (string, required): `bin`, `staticlib`, or `sharedlib`.
|
- `kind` (string, required): `bin`, `staticlib`, `sharedlib`, `efi`, or `elf`.
|
||||||
- `filename` (string, optional): custom output filename (without extension). Overrides `package.name` for the final artifact.
|
- `filename` (string, optional): custom output filename (without extension). Overrides `package.name` for the final artifact.
|
||||||
- `extension` (string, optional): custom file extension without the leading dot (e.g. `BIN`, `efi`, `EFI`). Combined with `filename`.
|
- `extension` (string, optional): custom file extension without the leading dot (e.g. `BIN`, `efi`, `EFI`). Combined with `filename`.
|
||||||
- `inherit` (bool, optional): inherit settings from base `[build]` (default `true`). If `false`, only use target/profile specific settings.
|
- `inherit` (bool, optional): inherit settings from base `[build]` (default `true`). If `false`, only use target/profile specific settings.
|
||||||
@@ -151,4 +151,4 @@ rcc = "rcc"
|
|||||||
|
|
||||||
- Compiler backend is selected by compiler string (`gcc`, `clang`, `cl`, `clang-cl`).
|
- Compiler backend is selected by compiler string (`gcc`, `clang`, `cl`, `clang-cl`).
|
||||||
- Empty or unknown compiler value falls back to clang-like build path.
|
- Empty or unknown compiler value falls back to clang-like build path.
|
||||||
- `run` is not allowed for library kinds (`staticlib`, `sharedlib`).
|
- `run` is not allowed for library kinds (`staticlib`, `sharedlib`), `efi`, or `elf`.
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ dcr clean --release --all
|
|||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
- `run` is unavailable for `build.kind = "staticlib"` and `build.kind = "sharedlib"`.
|
- `run` is unavailable for `build.kind = "staticlib"`, `build.kind = "sharedlib"`, `build.kind = "efi"`, and `build.kind = "elf"`.
|
||||||
- Build profile flag (`--debug` / `--release`) can be passed in any argument position.
|
- Build profile flag (`--debug` / `--release`) can be passed in any argument position.
|
||||||
- Unknown profile flags produce an error.
|
- Unknown profile flags produce an error.
|
||||||
- In workspace root, `clean --all` cleans all member projects.
|
- In workspace root, `clean --all` cleans all member projects.
|
||||||
|
|||||||
@@ -355,9 +355,15 @@ fn ensure_target_dirs(items: &[String], profile: &str, target_dir: Option<String
|
|||||||
if let Some(dir) = &target_dir {
|
if let Some(dir) = &target_dir {
|
||||||
let _ = fs::create_dir_all(dir);
|
let _ = fs::create_dir_all(dir);
|
||||||
} else {
|
} else {
|
||||||
|
let default_dir = if cfg!(target_os = "linux") {
|
||||||
|
let arch = std::env::consts::ARCH;
|
||||||
|
format!("{arch}-unknown-linux-gnu/{profile}")
|
||||||
|
} else {
|
||||||
|
profile.to_string()
|
||||||
|
};
|
||||||
let target_items = check_dir(Some("target")).unwrap_or_default();
|
let target_items = check_dir(Some("target")).unwrap_or_default();
|
||||||
if !target_items.contains(&profile.to_string()) {
|
if !target_items.contains(&default_dir) {
|
||||||
let _ = fs::create_dir(format!("./target/{profile}"));
|
let _ = fs::create_dir_all(format!("./target/{default_dir}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1317,8 +1323,10 @@ fn write_build_fingerprint(ctx: &BuildContext, fingerprint: &str) -> Result<(),
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn build_cache_path(profile: &str, target_dir: Option<&str>) -> std::path::PathBuf {
|
fn build_cache_path(profile: &str, target_dir: Option<&str>) -> std::path::PathBuf {
|
||||||
let _ = target_dir;
|
match target_dir {
|
||||||
Path::new("./target").join(profile).join(".dcr-build.hash")
|
Some(dir) => Path::new(dir).join(".dcr-build.hash"),
|
||||||
|
None => Path::new("./target").join(profile).join(".dcr-build.hash"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_output_path(ctx: &BuildContext) -> String {
|
fn build_output_path(ctx: &BuildContext) -> String {
|
||||||
@@ -1332,12 +1340,17 @@ fn build_output_path(ctx: &BuildContext) -> String {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if ctx.kind == "staticlib" {
|
if ctx.kind == "staticlib" {
|
||||||
// Для библиотек добавляем lib префикс если нужно, но упростим
|
|
||||||
return crate::platform::lib_path(ctx.profile, &final_name, ctx.target_dir);
|
return crate::platform::lib_path(ctx.profile, &final_name, ctx.target_dir);
|
||||||
}
|
}
|
||||||
if ctx.kind == "sharedlib" {
|
if ctx.kind == "sharedlib" {
|
||||||
return crate::platform::shared_lib_path(ctx.profile, &final_name, ctx.target_dir);
|
return crate::platform::shared_lib_path(ctx.profile, &final_name, ctx.target_dir);
|
||||||
}
|
}
|
||||||
|
if ctx.kind == "efi" {
|
||||||
|
return crate::platform::efi_path(ctx.profile, &final_name, ctx.target_dir);
|
||||||
|
}
|
||||||
|
if ctx.kind == "elf" {
|
||||||
|
return crate::platform::elf_path(ctx.profile, &final_name, ctx.target_dir);
|
||||||
|
}
|
||||||
crate::platform::bin_path(ctx.profile, &final_name, ctx.target_dir)
|
crate::platform::bin_path(ctx.profile, &final_name, ctx.target_dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -91,7 +91,17 @@ fn parse_clean_flags(args: &[String]) -> Result<CleanFlags, String> {
|
|||||||
fn clean_from_root(root: &Path, flags: &CleanFlags) -> Result<(), String> {
|
fn clean_from_root(root: &Path, flags: &CleanFlags) -> Result<(), String> {
|
||||||
let config = Config::open("./dcr.toml").map_err(|err| err.to_string())?;
|
let config = Config::open("./dcr.toml").map_err(|err| err.to_string())?;
|
||||||
|
|
||||||
let target = flags.target.clone().or_else(|| {
|
let target = flags
|
||||||
|
.target
|
||||||
|
.clone()
|
||||||
|
.or_else(|| {
|
||||||
|
config
|
||||||
|
.get("build.target")
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
|
.map(|s| s.trim().to_string())
|
||||||
|
.filter(|s| !s.is_empty())
|
||||||
|
})
|
||||||
|
.or_else(|| {
|
||||||
Some(if cfg!(target_os = "linux") {
|
Some(if cfg!(target_os = "linux") {
|
||||||
"x86_64-unknown-linux-gnu".to_string()
|
"x86_64-unknown-linux-gnu".to_string()
|
||||||
} else if cfg!(target_os = "macos") {
|
} else if cfg!(target_os = "macos") {
|
||||||
|
|||||||
@@ -138,8 +138,8 @@ fn download_asset(client: &Client, url: &str) -> Result<Vec<u8>, String> {
|
|||||||
fn asset_candidates(target: &str) -> Vec<String> {
|
fn asset_candidates(target: &str) -> Vec<String> {
|
||||||
let mut names = vec![format!("dcr-{target}")];
|
let mut names = vec![format!("dcr-{target}")];
|
||||||
|
|
||||||
if target == "x86_64-pc-windows-msvc" {
|
if target.contains("-windows-") || target.ends_with("-windows") {
|
||||||
names.push("dcr-x86_64-pc-windows-msvc.exe".to_string());
|
names.push(format!("dcr-{target}.exe"));
|
||||||
}
|
}
|
||||||
|
|
||||||
names
|
names
|
||||||
|
|||||||
@@ -69,15 +69,6 @@ pub fn run(args: &[String]) -> i32 {
|
|||||||
.get("package.name")
|
.get("package.name")
|
||||||
.and_then(|v| v.as_str())
|
.and_then(|v| v.as_str())
|
||||||
.unwrap_or("");
|
.unwrap_or("");
|
||||||
let build_kind = config
|
|
||||||
.get("build.kind")
|
|
||||||
.and_then(|v| v.as_str())
|
|
||||||
.unwrap_or("");
|
|
||||||
let target_dir = config
|
|
||||||
.get("build.target")
|
|
||||||
.and_then(|v| v.as_str())
|
|
||||||
.map(|s| s.trim())
|
|
||||||
.filter(|s| !s.is_empty());
|
|
||||||
|
|
||||||
let mut flags = match parse_build_run_flags(args) {
|
let mut flags = match parse_build_run_flags(args) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
@@ -98,6 +89,12 @@ pub fn run(args: &[String]) -> i32 {
|
|||||||
flags.target = Some(default_target.to_string());
|
flags.target = Some(default_target.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let build_kind = config
|
||||||
|
.get(&format!("build.{}.kind", flags.profile))
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
|
.or_else(|| config.get("build.kind").and_then(|v| v.as_str()))
|
||||||
|
.unwrap_or("");
|
||||||
|
|
||||||
let normalized_target_dir = flags
|
let normalized_target_dir = flags
|
||||||
.target
|
.target
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@@ -110,7 +107,9 @@ pub fn run(args: &[String]) -> i32 {
|
|||||||
let run_cmd = get_run_cmd(&config, &flags.profile, flags.target.as_deref(), version);
|
let run_cmd = get_run_cmd(&config, &flags.profile, flags.target.as_deref(), version);
|
||||||
|
|
||||||
let kind = build_kind.trim();
|
let kind = build_kind.trim();
|
||||||
if run_cmd.is_none() && (kind == "staticlib" || kind == "sharedlib") {
|
if run_cmd.is_none()
|
||||||
|
&& (kind == "staticlib" || kind == "sharedlib" || kind == "efi" || kind == "elf")
|
||||||
|
{
|
||||||
error("Cannot run library build");
|
error("Cannot run library build");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -128,13 +127,21 @@ pub fn run(args: &[String]) -> i32 {
|
|||||||
}
|
}
|
||||||
println!("\n {} {}", colored("Running", BOLD_GREEN), bin_path);
|
println!("\n {} {}", colored("Running", BOLD_GREEN), bin_path);
|
||||||
println!("--------------------------------");
|
println!("--------------------------------");
|
||||||
return run_binary(project_name, &flags.profile, target_dir);
|
return run_binary(
|
||||||
|
project_name,
|
||||||
|
&flags.profile,
|
||||||
|
normalized_target_dir.as_deref(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let fallback_code = if let Some(cmd) = run_cmd {
|
let fallback_code = if let Some(cmd) = run_cmd {
|
||||||
run_shell(&cmd)
|
run_shell(&cmd)
|
||||||
} else {
|
} else {
|
||||||
run_binary(project_name, &flags.profile, target_dir)
|
run_binary(
|
||||||
|
project_name,
|
||||||
|
&flags.profile,
|
||||||
|
normalized_target_dir.as_deref(),
|
||||||
|
)
|
||||||
};
|
};
|
||||||
if fallback_code != 1 {
|
if fallback_code != 1 {
|
||||||
return fallback_code;
|
return fallback_code;
|
||||||
|
|||||||
@@ -16,10 +16,11 @@ const BOLD_BLUE: &str = "\x1b[1m\x1b[94m";
|
|||||||
|
|
||||||
pub fn test(args: &[String]) -> i32 {
|
pub fn test(args: &[String]) -> i32 {
|
||||||
let mut init_header = false;
|
let mut init_header = false;
|
||||||
|
let mut profile = "debug";
|
||||||
for arg in args {
|
for arg in args {
|
||||||
if arg == "--help" {
|
if arg == "--help" {
|
||||||
println!("USAGE:");
|
println!("USAGE:");
|
||||||
println!(" dcr test [--init]");
|
println!(" dcr test [--init] [--debug | --release]");
|
||||||
println!();
|
println!();
|
||||||
println!("ALIASES:");
|
println!("ALIASES:");
|
||||||
println!(" dcr tests");
|
println!(" dcr tests");
|
||||||
@@ -29,12 +30,18 @@ pub fn test(args: &[String]) -> i32 {
|
|||||||
println!();
|
println!();
|
||||||
println!("OPTIONS:");
|
println!("OPTIONS:");
|
||||||
println!(" --init Create tests/dcr_test.h in current project");
|
println!(" --init Create tests/dcr_test.h in current project");
|
||||||
|
println!(" --debug Build and run tests with debug profile (default)");
|
||||||
|
println!(" --release Build and run tests with release profile");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if arg == "--init" {
|
if arg == "--init" {
|
||||||
init_header = true;
|
init_header = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if arg == "--debug" || arg == "--release" {
|
||||||
|
profile = arg.trim_start_matches("--");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
error("Unknown argument");
|
error("Unknown argument");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -68,7 +75,7 @@ pub fn test(args: &[String]) -> i32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match with_dir(&root, run_testsuite) {
|
match with_dir(&root, || run_testsuite(profile)) {
|
||||||
Ok(code) => code,
|
Ok(code) => code,
|
||||||
Err(msg) => {
|
Err(msg) => {
|
||||||
error(&msg);
|
error(&msg);
|
||||||
@@ -77,13 +84,12 @@ pub fn test(args: &[String]) -> i32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_testsuite() -> Result<i32, String> {
|
fn run_testsuite(profile: &str) -> Result<i32, String> {
|
||||||
if build::build(&[String::from("--release")]) != 0 {
|
if build::build(&[format!("--{profile}")]) != 0 {
|
||||||
return Ok(1);
|
return Ok(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
let config = Config::open("./dcr.toml").map_err(|_| "Failed to read dcr.toml".to_string())?;
|
let config = Config::open("./dcr.toml").map_err(|_| "Failed to read dcr.toml".to_string())?;
|
||||||
let profile = "release";
|
|
||||||
let name = get_config_str(&config, "package.name");
|
let name = get_config_str(&config, "package.name");
|
||||||
let language = get_language_with_profile(&config, profile)?;
|
let language = get_language_with_profile(&config, profile)?;
|
||||||
let compiler_cfg = get_string_with_profile(&config, "compiler", profile);
|
let compiler_cfg = get_string_with_profile(&config, "compiler", profile);
|
||||||
|
|||||||
@@ -5,6 +5,10 @@ use std::sync::{Mutex, OnceLock};
|
|||||||
|
|
||||||
static OUTPUT_MUTEX: OnceLock<Mutex<()>> = OnceLock::new();
|
static OUTPUT_MUTEX: OnceLock<Mutex<()>> = OnceLock::new();
|
||||||
|
|
||||||
|
pub fn get_output_lock() -> &'static Mutex<()> {
|
||||||
|
OUTPUT_MUTEX.get_or_init(|| Mutex::new(()))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn collect_sources(
|
pub fn collect_sources(
|
||||||
roots: &[PathBuf],
|
roots: &[PathBuf],
|
||||||
extensions: &[&str],
|
extensions: &[&str],
|
||||||
@@ -230,10 +234,16 @@ pub fn normalize_source_path(path: &Path) -> String {
|
|||||||
|
|
||||||
pub fn object_path(obj_dir: &Path, source: &str, obj_ext: &str) -> String {
|
pub fn object_path(obj_dir: &Path, source: &str, obj_ext: &str) -> String {
|
||||||
let src_path = Path::new(source);
|
let src_path = Path::new(source);
|
||||||
let rel = src_path
|
let stripped = src_path.strip_prefix("./").unwrap_or(src_path);
|
||||||
.strip_prefix("./src")
|
let rel = stripped
|
||||||
.or_else(|_| src_path.strip_prefix("src"))
|
.components()
|
||||||
.unwrap_or(src_path);
|
.skip(1)
|
||||||
|
.collect::<std::path::PathBuf>();
|
||||||
|
let rel = if rel.components().next().is_some() {
|
||||||
|
rel
|
||||||
|
} else {
|
||||||
|
stripped.to_path_buf()
|
||||||
|
};
|
||||||
let mut out = obj_dir.join(rel);
|
let mut out = obj_dir.join(rel);
|
||||||
out.set_extension(obj_ext.trim_start_matches('.'));
|
out.set_extension(obj_ext.trim_start_matches('.'));
|
||||||
out.to_string_lossy().to_string()
|
out.to_string_lossy().to_string()
|
||||||
|
|||||||
@@ -18,7 +18,10 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
|||||||
};
|
};
|
||||||
let start_time = Instant::now();
|
let start_time = Instant::now();
|
||||||
let sources = collect_sources(ctx)?;
|
let sources = collect_sources(ctx)?;
|
||||||
let obj_dir = Path::new("./target").join(ctx.profile).join("obj");
|
let obj_dir = match ctx.target_dir {
|
||||||
|
Some(dir) => Path::new(dir).join("obj"),
|
||||||
|
None => Path::new("./target").join(ctx.profile).join("obj"),
|
||||||
|
};
|
||||||
let objects = build_objects(assembler, &sources, &obj_dir, ctx, "o")?;
|
let objects = build_objects(assembler, &sources, &obj_dir, ctx, "o")?;
|
||||||
|
|
||||||
if ctx.kind == "staticlib" {
|
if ctx.kind == "staticlib" {
|
||||||
@@ -80,6 +83,8 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
|||||||
|
|
||||||
let out_path = if ctx.kind == "sharedlib" {
|
let out_path = if ctx.kind == "sharedlib" {
|
||||||
platform::shared_lib_path(ctx.profile, &final_name, ctx.target_dir)
|
platform::shared_lib_path(ctx.profile, &final_name, ctx.target_dir)
|
||||||
|
} else if ctx.kind == "elf" {
|
||||||
|
platform::elf_path(ctx.profile, &final_name, ctx.target_dir)
|
||||||
} else {
|
} else {
|
||||||
platform::bin_path(ctx.profile, &final_name, ctx.target_dir)
|
platform::bin_path(ctx.profile, &final_name, ctx.target_dir)
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -33,6 +33,12 @@ pub struct BuildContext<'a> {
|
|||||||
|
|
||||||
pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
||||||
let compiler = ctx.compiler.to_lowercase();
|
let compiler = ctx.compiler.to_lowercase();
|
||||||
|
if !check_compiler_exists(ctx.compiler) {
|
||||||
|
return Err(format!(
|
||||||
|
"Compiler not found: {}. Make sure it is installed and available in PATH.",
|
||||||
|
ctx.compiler
|
||||||
|
));
|
||||||
|
}
|
||||||
if compiler.contains("clang-cl") {
|
if compiler.contains("clang-cl") {
|
||||||
return msvc::build(ctx);
|
return msvc::build(ctx);
|
||||||
}
|
}
|
||||||
@@ -48,6 +54,14 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
|||||||
unix_cc::build(ctx)
|
unix_cc::build(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_compiler_exists(compiler: &str) -> bool {
|
||||||
|
let name = if compiler.is_empty() { "cc" } else { compiler };
|
||||||
|
std::process::Command::new(name)
|
||||||
|
.arg("--version")
|
||||||
|
.output()
|
||||||
|
.is_ok()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn collect_sources(ctx: &BuildContext) -> Result<Vec<String>, String> {
|
pub fn collect_sources(ctx: &BuildContext) -> Result<Vec<String>, String> {
|
||||||
let compiler = ctx.compiler.to_lowercase();
|
let compiler = ctx.compiler.to_lowercase();
|
||||||
if compiler.contains("clang-cl") || compiler == "cl" || compiler.contains("msvc") {
|
if compiler.contains("clang-cl") || compiler == "cl" || compiler.contains("msvc") {
|
||||||
|
|||||||
@@ -18,7 +18,10 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
|||||||
}
|
}
|
||||||
let start_time = Instant::now();
|
let start_time = Instant::now();
|
||||||
let sources = collect_sources(ctx)?;
|
let sources = collect_sources(ctx)?;
|
||||||
let obj_dir = Path::new("./target").join(ctx.profile).join("obj");
|
let obj_dir = match ctx.target_dir {
|
||||||
|
Some(dir) => Path::new(dir).join("obj"),
|
||||||
|
None => Path::new("./target").join(ctx.profile).join("obj"),
|
||||||
|
};
|
||||||
let objects = build_objects(compiler, &sources, &obj_dir, ctx, "obj")?;
|
let objects = build_objects(compiler, &sources, &obj_dir, ctx, "obj")?;
|
||||||
|
|
||||||
if ctx.kind == "staticlib" {
|
if ctx.kind == "staticlib" {
|
||||||
@@ -92,6 +95,8 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
|||||||
}
|
}
|
||||||
let out_path = if ctx.kind == "sharedlib" {
|
let out_path = if ctx.kind == "sharedlib" {
|
||||||
platform::shared_lib_path(ctx.profile, ctx.project_name, ctx.target_dir)
|
platform::shared_lib_path(ctx.profile, ctx.project_name, ctx.target_dir)
|
||||||
|
} else if ctx.kind == "elf" {
|
||||||
|
platform::elf_path(ctx.profile, ctx.project_name, ctx.target_dir)
|
||||||
} else {
|
} else {
|
||||||
platform::bin_path(ctx.profile, ctx.project_name, ctx.target_dir)
|
platform::bin_path(ctx.profile, ctx.project_name, ctx.target_dir)
|
||||||
};
|
};
|
||||||
@@ -185,8 +190,6 @@ fn default_flags(profile: &str) -> &'static [&'static str] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static OUTPUT_MUTEX: std::sync::Mutex<()> = std::sync::Mutex::new(());
|
|
||||||
|
|
||||||
fn build_objects(
|
fn build_objects(
|
||||||
compiler: &str,
|
compiler: &str,
|
||||||
sources: &[String],
|
sources: &[String],
|
||||||
@@ -282,7 +285,7 @@ fn build_object(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let _lock = OUTPUT_MUTEX.lock().unwrap();
|
let _lock = common::get_output_lock().lock().unwrap();
|
||||||
|
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
eprint!("{}", clean_stdout);
|
eprint!("{}", clean_stdout);
|
||||||
|
|||||||
@@ -18,7 +18,10 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
|||||||
};
|
};
|
||||||
let start_time = Instant::now();
|
let start_time = Instant::now();
|
||||||
let sources = collect_sources(ctx)?;
|
let sources = collect_sources(ctx)?;
|
||||||
let obj_dir = Path::new("./target").join(ctx.profile).join("obj");
|
let obj_dir = match ctx.target_dir {
|
||||||
|
Some(dir) => Path::new(dir).join("obj"),
|
||||||
|
None => Path::new("./target").join(ctx.profile).join("obj"),
|
||||||
|
};
|
||||||
let objects = build_objects(assembler, &sources, &obj_dir, ctx, "o")?;
|
let objects = build_objects(assembler, &sources, &obj_dir, ctx, "o")?;
|
||||||
|
|
||||||
if ctx.kind == "staticlib" {
|
if ctx.kind == "staticlib" {
|
||||||
@@ -80,6 +83,8 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
|||||||
|
|
||||||
let out_path = if ctx.kind == "sharedlib" {
|
let out_path = if ctx.kind == "sharedlib" {
|
||||||
platform::shared_lib_path(ctx.profile, &final_name, ctx.target_dir)
|
platform::shared_lib_path(ctx.profile, &final_name, ctx.target_dir)
|
||||||
|
} else if ctx.kind == "elf" {
|
||||||
|
platform::elf_path(ctx.profile, &final_name, ctx.target_dir)
|
||||||
} else {
|
} else {
|
||||||
platform::bin_path(ctx.profile, &final_name, ctx.target_dir)
|
platform::bin_path(ctx.profile, &final_name, ctx.target_dir)
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,7 +20,10 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
|||||||
ctx.exclude_dirs,
|
ctx.exclude_dirs,
|
||||||
ctx.include_paths,
|
ctx.include_paths,
|
||||||
)?;
|
)?;
|
||||||
let obj_dir = Path::new("./target").join(ctx.profile).join("obj");
|
let obj_dir = match ctx.target_dir {
|
||||||
|
Some(dir) => Path::new(dir).join("obj"),
|
||||||
|
None => Path::new("./target").join(ctx.profile).join("obj"),
|
||||||
|
};
|
||||||
let objects = build_objects(compiler, &sources, &obj_dir, ctx, "o")?;
|
let objects = build_objects(compiler, &sources, &obj_dir, ctx, "o")?;
|
||||||
|
|
||||||
if ctx.kind == "staticlib" {
|
if ctx.kind == "staticlib" {
|
||||||
@@ -51,6 +54,12 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
|||||||
cmd.arg("-shared");
|
cmd.arg("-shared");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ctx.kind == "efi" {
|
||||||
|
cmd.arg("-shared");
|
||||||
|
cmd.arg("-nostdlib");
|
||||||
|
cmd.arg("-Wl,-dll");
|
||||||
|
cmd.arg("-Wl,--subsystem,10");
|
||||||
|
}
|
||||||
for obj in &objects {
|
for obj in &objects {
|
||||||
cmd.arg(obj);
|
cmd.arg(obj);
|
||||||
}
|
}
|
||||||
@@ -73,6 +82,10 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
|
|||||||
|
|
||||||
let out_path = if ctx.kind == "sharedlib" {
|
let out_path = if ctx.kind == "sharedlib" {
|
||||||
platform::shared_lib_path(ctx.profile, &final_name, ctx.target_dir)
|
platform::shared_lib_path(ctx.profile, &final_name, ctx.target_dir)
|
||||||
|
} else if ctx.kind == "efi" {
|
||||||
|
platform::efi_path(ctx.profile, &final_name, ctx.target_dir)
|
||||||
|
} else if ctx.kind == "elf" {
|
||||||
|
platform::elf_path(ctx.profile, &final_name, ctx.target_dir)
|
||||||
} else {
|
} else {
|
||||||
platform::bin_path(ctx.profile, &final_name, ctx.target_dir)
|
platform::bin_path(ctx.profile, &final_name, ctx.target_dir)
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ const DEFAULT_LANGUAGE: &str = "c";
|
|||||||
const DEFAULT_STANDARD: &str = "c11";
|
const DEFAULT_STANDARD: &str = "c11";
|
||||||
const DEFAULT_COMPILER: &str = "clang";
|
const DEFAULT_COMPILER: &str = "clang";
|
||||||
const DEFAULT_KIND: &str = "bin";
|
const DEFAULT_KIND: &str = "bin";
|
||||||
|
const VALID_KINDS: &[&str] = &["bin", "staticlib", "sharedlib", "efi", "elf"];
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ConfigError {
|
pub enum ConfigError {
|
||||||
@@ -242,7 +243,7 @@ impl Config {
|
|||||||
validate_toolchain(self.typed.toolchain.as_ref())?;
|
validate_toolchain(self.typed.toolchain.as_ref())?;
|
||||||
if let Some(kind) = &self.typed.build.kind {
|
if let Some(kind) = &self.typed.build.kind {
|
||||||
let kind = kind.trim();
|
let kind = kind.trim();
|
||||||
if !kind.is_empty() && kind != "bin" && kind != "staticlib" && kind != "sharedlib" {
|
if !kind.is_empty() && !VALID_KINDS.contains(&kind) {
|
||||||
return Err(ConfigError::Invalid("build.kind is invalid".into()));
|
return Err(ConfigError::Invalid("build.kind is invalid".into()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -410,7 +411,7 @@ impl Config {
|
|||||||
}
|
}
|
||||||
if let Some(kind) = table.get("kind").and_then(|v| v.as_str()) {
|
if let Some(kind) = table.get("kind").and_then(|v| v.as_str()) {
|
||||||
let kind = kind.trim();
|
let kind = kind.trim();
|
||||||
if !kind.is_empty() && kind != "bin" && kind != "staticlib" && kind != "sharedlib" {
|
if !kind.is_empty() && !VALID_KINDS.contains(&kind) {
|
||||||
return Err(ConfigError::Invalid(format!(
|
return Err(ConfigError::Invalid(format!(
|
||||||
"build.{profile}.kind is invalid"
|
"build.{profile}.kind is invalid"
|
||||||
)));
|
)));
|
||||||
@@ -919,14 +920,16 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn validate_valid_kinds() {
|
fn validate_valid_kinds() {
|
||||||
for kind in ["bin", "staticlib", "sharedlib"] {
|
for &kind in VALID_KINDS {
|
||||||
let dir = temp_dir(&format!("kind_{kind}"));
|
let dir = temp_dir("valid_kind");
|
||||||
let content = format!(
|
let toml = format!(
|
||||||
"[package]\nname = \"test\"\nversion = \"0.1.0\"\n\n[build]\nlanguage = \"c\"\nstandard = \"c11\"\ncompiler = \"clang\"\nkind = \"{kind}\"\n\n[dependencies]\n"
|
"[package]\nname = \"test\"\nversion = \"0.1.0\"\n\n[build]\nlanguage = \"c\"\nstandard = \"c11\"\ncompiler = \"clang\"\nkind = \"{kind}\"\n\n[dependencies]\n"
|
||||||
);
|
);
|
||||||
let path = write_toml_file(&dir, &content);
|
let path = write_toml_file(&dir, &toml);
|
||||||
let result = Config::open(&path.to_string_lossy());
|
assert!(
|
||||||
assert!(result.is_ok(), "kind '{kind}' should be valid");
|
Config::open(&path.to_string_lossy()).is_ok(),
|
||||||
|
"kind '{kind}' should be valid"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,20 @@ pub mod register;
|
|||||||
|
|
||||||
use crate::core::config::Config;
|
use crate::core::config::Config;
|
||||||
use crate::core::deps::common::ResolvedDeps;
|
use crate::core::deps::common::ResolvedDeps;
|
||||||
|
use crate::core::deps::lock::{DepLock, write_lock};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
fn dep_version(path: &Path) -> String {
|
||||||
|
Config::open(&path.join("dcr.toml").to_string_lossy())
|
||||||
|
.ok()
|
||||||
|
.and_then(|c| {
|
||||||
|
c.get("package.version")
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
})
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn resolve_deps(
|
pub fn resolve_deps(
|
||||||
config: &Config,
|
config: &Config,
|
||||||
_profile: &str,
|
_profile: &str,
|
||||||
@@ -26,7 +38,7 @@ pub fn resolve_deps(
|
|||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
let deps_table = config.get("dependencies").and_then(|v| v.as_table());
|
let deps_table = config.get("dependencies").and_then(|v| v.as_table());
|
||||||
let lock_packages = Vec::new();
|
let mut lock_packages: Vec<DepLock> = Vec::new();
|
||||||
|
|
||||||
if let Some(deps) = deps_table {
|
if let Some(deps) = deps_table {
|
||||||
for (name, value) in deps {
|
for (name, value) in deps {
|
||||||
@@ -45,6 +57,23 @@ pub fn resolve_deps(
|
|||||||
.to_string(),
|
.to_string(),
|
||||||
);
|
);
|
||||||
resolved.libs.push(name.clone());
|
resolved.libs.push(name.clone());
|
||||||
|
|
||||||
|
lock_packages.push(DepLock {
|
||||||
|
name: name.clone(),
|
||||||
|
version: pkg_info
|
||||||
|
.get("version")
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
|
.unwrap_or("")
|
||||||
|
.to_string(),
|
||||||
|
checksum: String::new(),
|
||||||
|
source: format!(
|
||||||
|
"registry+{}",
|
||||||
|
pkg_info
|
||||||
|
.get("registry_url")
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
|
.unwrap_or("https://dcr-registry.pages.dev")
|
||||||
|
),
|
||||||
|
});
|
||||||
} else if let Some(path) = path_dep_path(value) {
|
} else if let Some(path) = path_dep_path(value) {
|
||||||
let dep_root = project_root.join(path);
|
let dep_root = project_root.join(path);
|
||||||
if let Some(table) = value.as_table() {
|
if let Some(table) = value.as_table() {
|
||||||
@@ -86,11 +115,25 @@ pub fn resolve_deps(
|
|||||||
push_default_lib_dirs(&mut resolved.lib_dirs, &dep_root);
|
push_default_lib_dirs(&mut resolved.lib_dirs, &dep_root);
|
||||||
resolved.libs.push(name.clone());
|
resolved.libs.push(name.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lock_packages.push(DepLock {
|
||||||
|
name: name.clone(),
|
||||||
|
version: dep_version(&dep_root),
|
||||||
|
checksum: String::new(),
|
||||||
|
source: format!("path+{}", dep_root.display()),
|
||||||
|
});
|
||||||
|
} else if let Some(git_info) = git_dep(value) {
|
||||||
|
lock_packages.push(DepLock {
|
||||||
|
name: name.clone(),
|
||||||
|
version: git_info.version.unwrap_or_default(),
|
||||||
|
checksum: String::new(),
|
||||||
|
source: format!("git+{}", git_info.url),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
crate::core::deps::lock::write_lock(
|
write_lock(
|
||||||
project_root,
|
project_root,
|
||||||
&project_name,
|
&project_name,
|
||||||
&project_version,
|
&project_version,
|
||||||
@@ -100,6 +143,24 @@ pub fn resolve_deps(
|
|||||||
Ok(resolved)
|
Ok(resolved)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct GitDep<'a> {
|
||||||
|
url: &'a str,
|
||||||
|
version: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn git_dep(value: &toml::Value) -> Option<GitDep<'_>> {
|
||||||
|
if let Some(table) = value.as_table()
|
||||||
|
&& let Some(url) = table.get("git").and_then(|v| v.as_str())
|
||||||
|
{
|
||||||
|
let version = table
|
||||||
|
.get("version")
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
|
.map(|s| s.to_string());
|
||||||
|
return Some(GitDep { url, version });
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn path_dep_path(value: &toml::Value) -> Option<&str> {
|
fn path_dep_path(value: &toml::Value) -> Option<&str> {
|
||||||
if let Some(table) = value.as_table() {
|
if let Some(table) = value.as_table() {
|
||||||
return table.get("path").and_then(|v| v.as_str());
|
return table.get("path").and_then(|v| v.as_str());
|
||||||
|
|||||||
@@ -51,10 +51,18 @@ pub fn get_index_path() -> PathBuf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_registry_cache_root() -> PathBuf {
|
pub fn get_registry_cache_root() -> PathBuf {
|
||||||
get_index_path()
|
let index_path = get_index_path();
|
||||||
.parent()
|
if let Some(parent) = index_path.parent() {
|
||||||
.map(Path::to_path_buf)
|
if parent.exists() {
|
||||||
.unwrap_or_else(|| PathBuf::from("."))
|
return parent.to_path_buf();
|
||||||
|
}
|
||||||
|
if let Err(e) = std::fs::create_dir_all(parent) {
|
||||||
|
eprintln!("Warning: failed to create registry cache directory: {e}");
|
||||||
|
}
|
||||||
|
parent.to_path_buf()
|
||||||
|
} else {
|
||||||
|
PathBuf::from(".")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn package_root_from_registry_info(pkg_info: &JsonValue) -> Result<PathBuf, String> {
|
pub fn package_root_from_registry_info(pkg_info: &JsonValue) -> Result<PathBuf, String> {
|
||||||
@@ -124,12 +132,16 @@ pub fn is_registry_dep(value: &TomlValue) -> bool {
|
|||||||
return !is_path_like_string(raw) && !is_git_like_string(raw);
|
return !is_path_like_string(raw) && !is_git_like_string(raw);
|
||||||
}
|
}
|
||||||
if let Some(table) = value.as_table() {
|
if let Some(table) = value.as_table() {
|
||||||
return !table.contains_key("git")
|
if table.contains_key("git") || table.contains_key("path") || table.contains_key("url") {
|
||||||
&& !table.contains_key("path")
|
return false;
|
||||||
&& !table.contains_key("url")
|
|
||||||
&& !table.contains_key("version");
|
|
||||||
}
|
}
|
||||||
|
table.contains_key("version")
|
||||||
|
|| table.contains_key("features")
|
||||||
|
|| table.contains_key("optional")
|
||||||
|
|| table.contains_key("registry")
|
||||||
|
} else {
|
||||||
false
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn path_from_string_dep(value: &TomlValue) -> Option<&str> {
|
pub fn path_from_string_dep(value: &TomlValue) -> Option<&str> {
|
||||||
|
|||||||
@@ -12,14 +12,44 @@ pub fn bin_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
|||||||
pub fn lib_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
pub fn lib_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
||||||
match target_dir {
|
match target_dir {
|
||||||
Some(dir) => format!("{}/lib{}.a", dir.trim_end_matches('/'), name),
|
Some(dir) => format!("{}/lib{}.a", dir.trim_end_matches('/'), name),
|
||||||
None => format!("./target/{profile}/lib{name}.a"),
|
None => {
|
||||||
|
let arch = std::env::consts::ARCH;
|
||||||
|
let target = format!("{arch}-unknown-linux-gnu");
|
||||||
|
format!("./target/{target}/{profile}/lib{name}.a")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn elf_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
||||||
|
match target_dir {
|
||||||
|
Some(dir) => format!("{}/{}", dir.trim_end_matches('/'), name),
|
||||||
|
None => {
|
||||||
|
let arch = std::env::consts::ARCH;
|
||||||
|
let target = format!("{arch}-unknown-linux-gnu");
|
||||||
|
format!("./target/{target}/{profile}/{name}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn efi_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
||||||
|
match target_dir {
|
||||||
|
Some(dir) => format!("{}/{}.efi", dir.trim_end_matches('/'), name),
|
||||||
|
None => {
|
||||||
|
let arch = std::env::consts::ARCH;
|
||||||
|
let target = format!("{arch}-unknown-linux-gnu");
|
||||||
|
format!("./target/{target}/{profile}/{name}.efi")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn shared_lib_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
pub fn shared_lib_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
||||||
match target_dir {
|
match target_dir {
|
||||||
Some(dir) => format!("{}/lib{}.so", dir.trim_end_matches('/'), name),
|
Some(dir) => format!("{}/lib{}.so", dir.trim_end_matches('/'), name),
|
||||||
None => format!("./target/{profile}/lib{name}.so"),
|
None => {
|
||||||
|
let arch = std::env::consts::ARCH;
|
||||||
|
let target = format!("{arch}-unknown-linux-gnu");
|
||||||
|
format!("./target/{target}/{profile}/lib{name}.so")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,10 +83,9 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn lib_path_default() {
|
fn lib_path_default() {
|
||||||
assert_eq!(
|
let arch = std::env::consts::ARCH;
|
||||||
lib_path("debug", "mylib", None),
|
let expected = format!("./target/{arch}-unknown-linux-gnu/debug/libmylib.a");
|
||||||
"./target/debug/libmylib.a"
|
assert_eq!(lib_path("debug", "mylib", None), expected);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -66,10 +95,9 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn shared_lib_path_default() {
|
fn shared_lib_path_default() {
|
||||||
assert_eq!(
|
let arch = std::env::consts::ARCH;
|
||||||
shared_lib_path("debug", "mylib", None),
|
let expected = format!("./target/{arch}-unknown-linux-gnu/debug/libmylib.so");
|
||||||
"./target/debug/libmylib.so"
|
assert_eq!(shared_lib_path("debug", "mylib", None), expected);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -12,6 +12,20 @@ pub fn lib_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn elf_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
||||||
|
match target_dir {
|
||||||
|
Some(dir) => format!("{}/{}", dir.trim_end_matches('/'), name),
|
||||||
|
None => format!("./target/{profile}/{name}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn efi_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
||||||
|
match target_dir {
|
||||||
|
Some(dir) => format!("{}/{}.efi", dir.trim_end_matches('/'), name),
|
||||||
|
None => format!("./target/{profile}/{name}.efi"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn shared_lib_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
pub fn shared_lib_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
||||||
match target_dir {
|
match target_dir {
|
||||||
Some(dir) => format!("{}/lib{}.dylib", dir.trim_end_matches('/'), name),
|
Some(dir) => format!("{}/lib{}.dylib", dir.trim_end_matches('/'), name),
|
||||||
|
|||||||
@@ -27,6 +27,50 @@ pub fn bin_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn elf_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
{
|
||||||
|
linux::elf_path(profile, name, target_dir)
|
||||||
|
}
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
{
|
||||||
|
return macos::elf_path(profile, name, target_dir);
|
||||||
|
}
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
{
|
||||||
|
return windows::elf_path(profile, name, target_dir);
|
||||||
|
}
|
||||||
|
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
|
||||||
|
{
|
||||||
|
match target_dir {
|
||||||
|
Some(dir) => format!("{}/{}", dir.trim_end_matches('/'), name),
|
||||||
|
None => format!("./target/{profile}/{name}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn efi_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
{
|
||||||
|
linux::efi_path(profile, name, target_dir)
|
||||||
|
}
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
{
|
||||||
|
return macos::efi_path(profile, name, target_dir);
|
||||||
|
}
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
{
|
||||||
|
return windows::efi_path(profile, name, target_dir);
|
||||||
|
}
|
||||||
|
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
|
||||||
|
{
|
||||||
|
match target_dir {
|
||||||
|
Some(dir) => format!("{}/{}.efi", dir.trim_end_matches('/'), name),
|
||||||
|
None => format!("./target/{profile}/{name}.efi"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn lib_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
pub fn lib_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,6 +12,20 @@ pub fn lib_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn elf_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
||||||
|
match target_dir {
|
||||||
|
Some(dir) => format!("{}/{}.exe", dir.trim_end_matches('/'), name),
|
||||||
|
None => format!("./target/{profile}/{name}.exe"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn efi_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
||||||
|
match target_dir {
|
||||||
|
Some(dir) => format!("{}/{}.efi", dir.trim_end_matches('/'), name),
|
||||||
|
None => format!("./target/{profile}/{name}.efi"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn shared_lib_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
pub fn shared_lib_path(profile: &str, name: &str, target_dir: Option<&str>) -> String {
|
||||||
match target_dir {
|
match target_dir {
|
||||||
Some(dir) => format!("{}/{}.dll", dir.trim_end_matches('/'), name),
|
Some(dir) => format!("{}/{}.dll", dir.trim_end_matches('/'), name),
|
||||||
|
|||||||
Reference in New Issue
Block a user