feat: add AGENTS.md for quick start and CI guidelines; update documentation for dcr add and dcr build commands

This commit is contained in:
2026-05-22 15:14:56 +08:00
parent ef2fb1e8e4
commit adfa24f8f4
19 changed files with 225 additions and 118 deletions

View File

@@ -1,77 +0,0 @@
# AGENT.md
## Project Overview
**DCR (Dexoron Cargo Realization)** — CLI tool for managing C/C++/ASM projects in a Cargo-style.
- **Implementation Language**: Rust (edition 2024)
- **License**: MIT
- **Repository**: [github.com/dexoron/dcr](https://github.com/dexoron/dcr)
## Architecture
### Source Tree (`src/`)
```
src/
├── main.rs # Entry point, CLI command routing
├── config.rs # Global constants
├── cli/ # CLI command implementation
│ ├── new.rs # dcr new <name>
│ ├── init.rs # dcr init
│ ├── setup.rs # dcr setup (registry initialization)
│ ├── add.rs # dcr add <name>
│ ├── build.rs # dcr build
│ ├── run.rs # dcr run
│ ├── clean.rs # dcr clean
│ ├── help.rs # dcr --help
│ └── flag_update.rs # dcr --update
├── core/ # Business logic
│ ├── config.rs # dcr.toml parsing
│ ├── registry.rs # Package index management (RegistryManager)
│ ├── deps/ # Dependency resolution (path, git)
│ ├── runner.rs # Binary execution
│ ├── workspace.rs # [workspace] support
│ └── builder/ # Compiler backends (gcc, clang, msvc, nasm, gas)
├── platform/ # Platform-specific logic
└── utils/ # Helper utilities
```
### Key Concepts
- **`~/.dcr/`**: Centralized storage for registries and configurations.
- **`config.toml`**: File in `~/.dcr/` defining available package indices and their priorities.
- **`dcr.toml`**: Local project configuration.
- **Registry System**: `RegistryManager` handles package discovery across multiple indices based on priority.
## Supported Platforms
| Platform | Target triple |
|---|---|
| Linux | `x86_64-unknown-linux-gnu` |
| macOS Intel | `x86_64-apple-darwin` |
| macOS ARM | `aarch64-apple-darwin` |
| Windows | `x86_64-pc-windows-msvc` |
## Dependencies (Cargo)
| Crate | Purpose |
|---|---|
| `reqwest` | HTTP requests |
| `serde` | Serialization |
| `toml` | Configuration parsing |
| `git2` | Git dependency handling |
## Development
### Build & Run
```bash
cargo build --release
./target/release/dcr setup
```
## Code Conventions
- Rust stable edition 2024.
- `cargo fmt` and `cargo clippy -- -D warnings`.
- CLI output in English.
- Use `utils::log` for messaging.

56
AGENTS.md Normal file
View File

@@ -0,0 +1,56 @@
# AGENTS.md
## Quick start
```bash
cargo build --release && ./target/release/dcr setup
```
## Pre-PR (CI) gate
```bash
cargo fmt --all --check
cargo clippy --all-targets --all-features -- -D warnings
cargo check --all-targets --all-features
cargo test
```
CI (`.github/workflows/ci.yml`) runs fmt, clippy, check — **not** tests.
## Tests
- Integration tests in `tests/` build the dcr binary and run it in temp dirs under `sandbox/cli-tests/` (gitignored).
- Some tests **skip** if no C compiler (`gcc`/`clang`/`cc`) is found at runtime.
- Compiler can be overridden via `DCR_COMPILER` or `DCR_CC` env vars.
- Tests use `$CARGO_BIN_EXE_dcr` when available, else `target/debug/dcr`.
## Architecture
- **Single Rust package** (edition 2024), not a workspace. Only one binary: `dcr`.
- **Entrypoint**: `src/main.rs` — matches `args[1]` against command names.
- **Compiler backends** in `src/core/builder/`: `unix_cc` (gcc/clang), `msvc` (cl/clang-cl), `gas`, `nasm`. Dispatched by matching compiler name string.
- **Config**: `dcr.toml` (per-project), `~/.dcr/config.toml` (registry), `~/.dcr/index.json` (index). Defaults: `language=c`, `standard=c11`, `compiler=clang`, `kind=bin`.
- **`build.rs`** embeds `TARGET` as `DCR_TARGET` env var (shown in `--version`).
- **CLI output** must be in English. Use `utils::log` for messaging.
## Key commands
| Command | Notes |
|---|---|
| `dcr new <name>` | Creates project dir |
| `dcr init` | Init from CWD (must be empty) |
| `dcr build [--debug\|--release] [--target <triple>]` | Default profile: `--debug` |
| `dcr run [--debug\|--release]` | Only for `kind = "bin"` |
| `dcr clean [--debug\|--release] [--all]` | `--all` for workspace roots |
| `dcr tree` | Dependency tree viewer |
| `dcr test [--init]` | Runs/test project tests |
| `dcr gen <vscode\|clion\|compile-commands>` | IDE integration |
| `dcr --update` | Self-update from GitHub Releases |
## Cross-compilation
`--target` accepts short names (`linux`, `macos`, `windows`) or full triples. In CI, Linux cross-builds use `cargo-zigbuild`. When `build.target` is set in `dcr.toml`, DCR auto-injects `--target=<value>` into cflags (clang). Bare-metal targets (containing `none`/`-elf`/`eabi`/`baremetal`) skip DCR's default compiler flags.
## Dependencies (Cargo)
reqwest (blocking+json+rustls-tls), serde, toml, git2, openssl (vendored), self-replace, sha2, ctrlc.

View File

@@ -14,6 +14,9 @@ Added:
Produces `KERNEL.EFI` (works for `bin`, `staticlib`, and `sharedlib`). Produces `KERNEL.EFI` (works for `bin`, `staticlib`, and `sharedlib`).
- Automatic injection of `--target=<build.target>` into compiler flags when `build.target` is set in `dcr.toml`. This greatly simplifies clang-based cross-compilation (especially for bare-metal targets like `aarch64-none-elf`). - Automatic injection of `--target=<build.target>` into compiler flags when `build.target` is set in `dcr.toml`. This greatly simplifies clang-based cross-compilation (especially for bare-metal targets like `aarch64-none-elf`).
- Bare-metal targets (containing `none`, `-elf`, `eabi`, `baremetal`) no longer receive DCR's internal default flags (`-g`, `-Wall`, `-Wextra`, `-fno-omit-frame-pointer`, `-DDCR_DEBUG`, etc.). Prevents unwanted sections (`.comment`, debug info, etc.) that break custom linker scripts when `inherit = true`. - Bare-metal targets (containing `none`, `-elf`, `eabi`, `baremetal`) no longer receive DCR's internal default flags (`-g`, `-Wall`, `-Wextra`, `-fno-omit-frame-pointer`, `-DDCR_DEBUG`, etc.). Prevents unwanted sections (`.comment`, debug info, etc.) that break custom linker scripts when `inherit = true`.
- `build.ldscript` in `dcr.toml` — linker script path passed as `-T <path>` to the linker. Essential for bare-metal/embedded/freestanding targets.
- `dcr build --verbose` — prints compiler/linker command lines (also works with `DCR_DEBUG` env var).
- `dcr add <name>` without a source argument — if a registry is configured, DCR looks up the package by name and adds it as a version dependency.
Fixed: Fixed:

View File

@@ -5,11 +5,15 @@ Add a dependency to `dcr.toml`.
## Usage ## Usage
```bash ```bash
dcr add <name> <source> [options] dcr add <name> [source] [options]
``` ```
If `<source>` is omitted and a registry is configured, DCR will look up the
package by name in the registry and add it as a version dependency.
## Sources ## Sources
- *omitted*: Look up in the configured registry.
- `path:./path/to/lib`: Local directory. - `path:./path/to/lib`: Local directory.
- `github:user/repo`: GitHub repository. - `github:user/repo`: GitHub repository.
- `gitlab:user/repo`: GitLab repository. - `gitlab:user/repo`: GitLab repository.
@@ -29,4 +33,5 @@ dcr add my_lib path:./libs/my_lib
dcr add cool_lib github:user/cool_lib dcr add cool_lib github:user/cool_lib
dcr add shared_lib gitlab:org/shared --branch develop dcr add shared_lib gitlab:org/shared --branch develop
dcr add custom_lib git:git.internal.com/r/lib --tag v1.0.0 dcr add custom_lib git:git.internal.com/r/lib --tag v1.0.0
dcr add mylib # Looks up in registry
``` ```

View File

@@ -35,6 +35,7 @@ dcr build --debug --clean
- `build.platform` - `build.platform`
- `build.cflags` - `build.cflags`
- `build.ldflags` - `build.ldflags`
- `build.ldscript`
- `build.debug` / `build.release` - `build.debug` / `build.release`
- `build.exclude` - `build.exclude`
- `build.include` - `build.include`
@@ -58,6 +59,7 @@ dcr build --debug --clean
- Profile flag (`--debug` / `--release`) can appear in any argument position (duplicates are rejected). - Profile flag (`--debug` / `--release`) can appear in any argument position (duplicates are rejected).
- Unknown profile flags return an error. - Unknown profile flags return an error.
- `--target <triple>` builds for specified target (short names: `linux`, `macos`, `windows`). - `--target <triple>` builds for specified target (short names: `linux`, `macos`, `windows`).
- `--verbose` prints the compiler/linker command lines to stderr (also works via `DCR_DEBUG` env var).
- If no `--target`, builds for `build.targets` or native. - If no `--target`, builds for `build.targets` or native.
- 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.

View File

@@ -43,6 +43,7 @@ kind = "bin"
- `platform` (string, optional): architecture hint for compiler (used for `-march` or `/arch`). - `platform` (string, optional): architecture hint for compiler (used for `-march` or `/arch`).
- `cflags` (string array, optional): extra compile flags. - `cflags` (string array, optional): extra compile flags.
- `ldflags` (string array, optional): extra link flags. - `ldflags` (string array, optional): extra link flags.
- `ldscript` (string, optional): linker script path. Passed as `-T <path>` to the linker. Useful for bare-metal/embedded/freestanding targets.
- `exclude` (string array, optional): paths or glob patterns to skip during source/header collection. - `exclude` (string array, optional): paths or glob patterns to skip during source/header collection.
- `include` (string array, optional): allowlist paths or globs that override `exclude`. - `include` (string array, optional): allowlist paths or globs that override `exclude`.
- `include` directory entries (non-glob) are also passed to the compiler as `-I` include paths. - `include` directory entries (non-glob) are also passed to the compiler as `-I` include paths.

View File

@@ -20,7 +20,7 @@ dcr --update
## Notes on argument parsing ## Notes on argument parsing
- `build`/`run` parse flags in any order (`--debug|--release`, `--force`, `--clean`) and reject duplicates. - `build`/`run` parse flags in any order (`--debug|--release`, `--force`, `--clean`, `--verbose`) and reject duplicates.
- `new` requires exactly one argument. - `new` requires exactly one argument.
- `init` and `--update` do not accept extra arguments. - `init` and `--update` do not accept extra arguments.
- `clean` accepts `--debug|--release` and optional `--all`. - `clean` accepts `--debug|--release` and optional `--all`.

View File

@@ -18,6 +18,7 @@ target = "string" # optional
platform = "string" # optional platform = "string" # optional
cflags = ["string", "..."] # optional cflags = ["string", "..."] # optional
ldflags = ["string", "..."] # optional ldflags = ["string", "..."] # optional
ldscript = "string" # optional (linker script, passed as -T<path>)
exclude = ["string", "..."] # optional exclude = ["string", "..."] # optional
include = ["string", "..."] # optional include = ["string", "..."] # optional
roots = ["string", "..."] # optional roots = ["string", "..."] # optional

View File

@@ -1,4 +1,5 @@
use crate::core::config::Config; use crate::core::config::Config;
use crate::core::deps::register;
use crate::utils::fs::find_project_root; use crate::utils::fs::find_project_root;
use crate::utils::log::error; use crate::utils::log::error;
use crate::utils::text::{BOLD_GREEN, colored}; use crate::utils::text::{BOLD_GREEN, colored};
@@ -12,6 +13,7 @@ pub struct AddArgs {
pub branch: Option<String>, pub branch: Option<String>,
pub tag: Option<String>, pub tag: Option<String>,
pub rev: Option<String>, pub rev: Option<String>,
pub version_from_registry: Option<String>,
} }
pub fn add(args: &[String]) -> i32 { pub fn add(args: &[String]) -> i32 {
@@ -71,6 +73,8 @@ pub fn add(args: &[String]) -> i32 {
let mut table = Map::new(); let mut table = Map::new();
table.insert("path".to_string(), Value::String(path)); table.insert("path".to_string(), Value::String(path));
Value::Table(table) Value::Table(table)
} else if let Some(version) = add_args.version_from_registry {
Value::String(version)
} else { } else {
error("Dependency source (path or git) must be provided"); error("Dependency source (path or git) must be provided");
return 1; return 1;
@@ -124,8 +128,38 @@ fn parse_add_args(args: &[String]) -> Result<AddArgs, i32> {
} }
source_spec = s.clone(); source_spec = s.clone();
} else { } else {
error("Dependency source is required"); // No source provided — try registry auto-lookup
return Err(1); match register::resolve_package_from_registry(&name) {
Ok(info) => {
let version = info
.get("latest_version")
.or_else(|| info.get("version"))
.and_then(|v| v.as_str())
.ok_or_else(|| {
(
"Registry entry for `".to_string() + &name + "` has no version field",
1,
)
})
.map_err(|(msg, code)| {
error(&msg);
code
})?;
return Ok(AddArgs {
name,
path: None,
git: None,
branch: None,
tag: None,
rev: None,
version_from_registry: Some(version.to_string()),
});
}
Err(e) => {
error(&format!("Cannot add `{}` without source: {}", name, e));
return Err(1);
}
}
} }
while let Some(arg) = iter.next() { while let Some(arg) = iter.next() {
@@ -197,5 +231,6 @@ fn parse_add_args(args: &[String]) -> Result<AddArgs, i32> {
branch, branch,
tag, tag,
rev, rev,
version_from_registry: None,
}) })
} }

View File

@@ -77,7 +77,13 @@ pub fn build(args: &[String]) -> i32 {
let _ = clean(&clean_args); let _ = clean(&clean_args);
} }
match with_dir(&root, || { match with_dir(&root, || {
build_from_root(&root, &flags.profile, flags.target.as_deref(), flags.force) build_from_root(
&root,
&flags.profile,
flags.target.as_deref(),
flags.force,
flags.verbose,
)
}) { }) {
Ok(()) => 0, Ok(()) => 0,
Err(msg) => { Err(msg) => {
@@ -377,6 +383,7 @@ fn build_from_root(
profile: &str, profile: &str,
target: Option<&str>, target: Option<&str>,
force: bool, force: bool,
verbose: bool,
) -> Result<(), String> { ) -> 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 project_name = get_config_str(&config, "package.name"); let project_name = get_config_str(&config, "package.name");
@@ -418,12 +425,19 @@ fn build_from_root(
); );
} }
if let Some(workspace) = parse_workspace(&config, profile, build_target.as_deref(), root)? { if let Some(workspace) = parse_workspace(&config, profile, build_target.as_deref(), root)? {
build_workspace(&workspace, profile, build_target.as_deref(), force)?; build_workspace(&workspace, profile, build_target.as_deref(), force, verbose)?;
let excludes: Vec<std::path::PathBuf> = let excludes: Vec<std::path::PathBuf> =
workspace.members.iter().map(|m| m.path.clone()).collect(); workspace.members.iter().map(|m| m.path.clone()).collect();
build_project_at(root, profile, build_target.as_deref(), &excludes, force)?; build_project_at(
root,
profile,
build_target.as_deref(),
&excludes,
force,
verbose,
)?;
} else { } else {
build_project_at(root, profile, build_target.as_deref(), &[], force)?; build_project_at(root, profile, build_target.as_deref(), &[], force, verbose)?;
} }
} }
let elapsed = ((start_time.elapsed().as_secs_f64() * 100.0).trunc()) / 100.0; let elapsed = ((start_time.elapsed().as_secs_f64() * 100.0).trunc()) / 100.0;
@@ -440,9 +454,10 @@ fn build_workspace(
profile: &str, profile: &str,
target: Option<&str>, target: Option<&str>,
force: bool, force: bool,
verbose: bool,
) -> Result<(), String> { ) -> Result<(), String> {
for member in &workspace.members { for member in &workspace.members {
build_project_at(&member.path, profile, target, &[], force)?; build_project_at(&member.path, profile, target, &[], force, verbose)?;
} }
Ok(()) Ok(())
} }
@@ -453,6 +468,7 @@ fn build_project_at(
target: Option<&str>, target: Option<&str>,
exclude_dirs: &[std::path::PathBuf], exclude_dirs: &[std::path::PathBuf],
force: bool, force: bool,
verbose: bool,
) -> Result<(), String> { ) -> Result<(), String> {
with_dir(project_root, || { with_dir(project_root, || {
check_interrupted()?; check_interrupted()?;
@@ -500,21 +516,30 @@ fn build_project_at(
.or_else(|| get_config_opt(&config, "toolchain.rcc")); .or_else(|| get_config_opt(&config, "toolchain.rcc"));
let mut build_cflags = let mut build_cflags =
get_list_with_profile_and_target(&config, "cflags", profile, build_target)?; get_list_with_profile_and_target(&config, "cflags", profile, build_target)?;
// Автоматически добавляем --target=..., если задан build.target и флаг ещё не указан if let Some(t) = build_target
if let Some(t) = build_target { && !t.trim().is_empty()
if !t.trim().is_empty() { {
let target_flag = format!("--target={}", t.trim()); let target_flag = format!("--target={}", t.trim());
if !build_cflags.iter().any(|f| f == &target_flag || f.starts_with("--target=")) { if !build_cflags
build_cflags.insert(0, target_flag); .iter()
} .any(|f| f == &target_flag || f.starts_with("--target="))
{
build_cflags.insert(0, target_flag);
} }
} }
let build_ldflags = let mut build_ldflags =
get_list_with_profile_and_target(&config, "ldflags", profile, build_target)?; get_list_with_profile_and_target(&config, "ldflags", profile, build_target)?;
let build_ldscript =
get_string_with_profile_and_target(&config, "ldscript", profile, build_target);
if !build_ldscript.is_empty() {
build_ldflags.push(format!("-T{}", build_ldscript));
}
// Новые поля: filename и extension // Новые поля: filename и extension
let output_filename = get_string_with_profile_and_target(&config, "filename", profile, build_target); let output_filename =
let output_extension = get_string_with_profile_and_target(&config, "extension", profile, build_target); get_string_with_profile_and_target(&config, "filename", profile, build_target);
let output_extension =
get_string_with_profile_and_target(&config, "extension", profile, build_target);
let build_excludes = let build_excludes =
get_list_with_profile_and_target(&config, "exclude", profile, build_target)?; get_list_with_profile_and_target(&config, "exclude", profile, build_target)?;
let build_includes = let build_includes =
@@ -578,7 +603,7 @@ fn build_project_at(
dep_root.display() dep_root.display()
)); ));
} }
build_project_at(&dep_root, profile, build_target, &[], force)?; build_project_at(&dep_root, profile, build_target, &[], force, verbose)?;
print!( print!(
"\r{:100}\r {} {} v{}", "\r{:100}\r {} {} v{}",
"", "",
@@ -693,8 +718,17 @@ fn build_project_at(
libs: &resolved.libs, libs: &resolved.libs,
cflags: &resolved_cflags, cflags: &resolved_cflags,
ldflags: &resolved_ldflags, ldflags: &resolved_ldflags,
output_filename: if output_filename.is_empty() { None } else { Some(output_filename.as_str()) }, output_filename: if output_filename.is_empty() {
output_extension: if output_extension.is_empty() { None } else { Some(output_extension.as_str()) }, None
} else {
Some(output_filename.as_str())
},
output_extension: if output_extension.is_empty() {
None
} else {
Some(output_extension.as_str())
},
verbose,
}; };
if std::env::var("DCR_DEBUG").is_ok() { if std::env::var("DCR_DEBUG").is_ok() {
eprintln!("[dcr] debug: compiler={}", ctx.compiler); eprintln!("[dcr] debug: compiler={}", ctx.compiler);

View File

@@ -6,6 +6,7 @@ pub struct BuildRunFlags {
pub target: Option<String>, pub target: Option<String>,
pub force: bool, pub force: bool,
pub clean: bool, pub clean: bool,
pub verbose: bool,
} }
pub fn parse_build_run_flags(args: &[String]) -> Result<BuildRunFlags, i32> { pub fn parse_build_run_flags(args: &[String]) -> Result<BuildRunFlags, i32> {
@@ -13,6 +14,7 @@ pub fn parse_build_run_flags(args: &[String]) -> Result<BuildRunFlags, i32> {
let mut target = None; let mut target = None;
let mut force = false; let mut force = false;
let mut clean = false; let mut clean = false;
let mut verbose = false;
let mut iter = args.iter(); let mut iter = args.iter();
while let Some(arg) = iter.next() { while let Some(arg) = iter.next() {
@@ -29,6 +31,10 @@ pub fn parse_build_run_flags(args: &[String]) -> Result<BuildRunFlags, i32> {
clean = true; clean = true;
continue; continue;
} }
if candidate == "verbose" {
verbose = true;
continue;
}
if candidate == "target" { if candidate == "target" {
if let Some(t) = iter.next() { if let Some(t) = iter.next() {
target = Some(t.clone()); target = Some(t.clone());
@@ -55,5 +61,6 @@ pub fn parse_build_run_flags(args: &[String]) -> Result<BuildRunFlags, i32> {
target, target,
force, force,
clean, clean,
verbose,
}) })
} }

View File

@@ -339,6 +339,7 @@ fn collect_project_info_inner(root: &Path, profile: &str) -> Result<ProjectInfo,
ldflags: &resolved_ldflags, ldflags: &resolved_ldflags,
output_filename: None, output_filename: None,
output_extension: None, output_extension: None,
verbose: false,
}; };
let sources = collect_sources(&ctx).map_err(|e| format!("Failed to collect sources: {e}"))?; let sources = collect_sources(&ctx).map_err(|e| format!("Failed to collect sources: {e}"))?;

View File

@@ -37,7 +37,7 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
for obj in &objects { for obj in &objects {
cmd.arg(obj); cmd.arg(obj);
} }
if std::env::var("DCR_DEBUG").is_ok() { if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() {
eprintln!("[dcr] {:?}", cmd); eprintln!("[dcr] {:?}", cmd);
} }
match cmd.status() { match cmd.status() {
@@ -72,7 +72,11 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
} }
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("");
let final_name = if ext.is_empty() { name.to_string() } else { format!("{}.{}", name, ext) }; let final_name = if ext.is_empty() {
name.to_string()
} else {
format!("{}.{}", name, ext)
};
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)
@@ -81,7 +85,7 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
}; };
cmd.arg("-o").arg(out_path); cmd.arg("-o").arg(out_path);
if std::env::var("DCR_DEBUG").is_ok() { if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() {
eprintln!("[dcr] {:?}", cmd); eprintln!("[dcr] {:?}", cmd);
} }
match cmd.status() { match cmd.status() {

View File

@@ -28,6 +28,7 @@ pub struct BuildContext<'a> {
pub ldflags: &'a [String], pub ldflags: &'a [String],
pub output_filename: Option<&'a str>, pub output_filename: Option<&'a str>,
pub output_extension: Option<&'a str>, pub output_extension: Option<&'a str>,
pub verbose: bool,
} }
pub fn build(ctx: &BuildContext) -> Result<f64, String> { pub fn build(ctx: &BuildContext) -> Result<f64, String> {

View File

@@ -28,7 +28,7 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
for obj in &objects { for obj in &objects {
cmd.arg(obj); cmd.arg(obj);
} }
if std::env::var("DCR_DEBUG").is_ok() { if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() {
eprintln!("[dcr] {:?}", cmd); eprintln!("[dcr] {:?}", cmd);
} }
match cmd.status() { match cmd.status() {
@@ -97,7 +97,7 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
}; };
cmd.arg(format!("/Fe:{out_path}")); cmd.arg(format!("/Fe:{out_path}"));
if std::env::var("DCR_DEBUG").is_ok() { if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() {
eprintln!("[dcr] {:?}", cmd); eprintln!("[dcr] {:?}", cmd);
} }
match cmd.status() { match cmd.status() {

View File

@@ -37,7 +37,7 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
for obj in &objects { for obj in &objects {
cmd.arg(obj); cmd.arg(obj);
} }
if std::env::var("DCR_DEBUG").is_ok() { if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() {
eprintln!("[dcr] {:?}", cmd); eprintln!("[dcr] {:?}", cmd);
} }
match cmd.status() { match cmd.status() {
@@ -72,7 +72,11 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
} }
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("");
let final_name = if ext.is_empty() { name.to_string() } else { format!("{}.{}", name, ext) }; let final_name = if ext.is_empty() {
name.to_string()
} else {
format!("{}.{}", name, ext)
};
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)
@@ -81,7 +85,7 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
}; };
cmd.arg("-o").arg(out_path); cmd.arg("-o").arg(out_path);
if std::env::var("DCR_DEBUG").is_ok() { if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() {
eprintln!("[dcr] {:?}", cmd); eprintln!("[dcr] {:?}", cmd);
} }
match cmd.status() { match cmd.status() {
@@ -151,7 +155,7 @@ fn build_object(
cmd.arg(flag); cmd.arg(flag);
} }
if std::env::var("DCR_DEBUG").is_ok() { if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() {
eprintln!("[dcr] {:?}", cmd); eprintln!("[dcr] {:?}", cmd);
} }

View File

@@ -30,7 +30,7 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
for obj in &objects { for obj in &objects {
cmd.arg(obj); cmd.arg(obj);
} }
if std::env::var("DCR_DEBUG").is_ok() { if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() {
eprintln!("[dcr] {:?}", cmd); eprintln!("[dcr] {:?}", cmd);
} }
match cmd.status() { match cmd.status() {
@@ -65,7 +65,11 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
} }
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("");
let final_name = if ext.is_empty() { name.to_string() } else { format!("{}.{}", name, ext) }; let final_name = if ext.is_empty() {
name.to_string()
} else {
format!("{}.{}", name, ext)
};
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)
@@ -74,7 +78,7 @@ pub fn build(ctx: &BuildContext) -> Result<f64, String> {
}; };
cmd.arg("-o").arg(out_path); cmd.arg("-o").arg(out_path);
if std::env::var("DCR_DEBUG").is_ok() { if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() {
eprintln!("[dcr] {:?}", cmd); eprintln!("[dcr] {:?}", cmd);
} }
match cmd.status() { match cmd.status() {
@@ -214,7 +218,7 @@ fn build_object(
let d_path = Path::new(obj_path).with_extension("d"); let d_path = Path::new(obj_path).with_extension("d");
cmd.arg("-MMD").arg("-MF").arg(&d_path); cmd.arg("-MMD").arg("-MF").arg(&d_path);
if std::env::var("DCR_DEBUG").is_ok() { if ctx.verbose || std::env::var("DCR_DEBUG").is_ok() {
eprintln!("[dcr] {:?}", cmd); eprintln!("[dcr] {:?}", cmd);
} }

View File

@@ -15,9 +15,23 @@ pub struct DcrConfig {
pub registry: std::collections::HashMap<String, Registry>, pub registry: std::collections::HashMap<String, Registry>,
} }
fn home_dir() -> Option<PathBuf> {
if let Ok(home) = std::env::var("HOME") {
return Some(PathBuf::from(home));
}
if let Ok(profile) = std::env::var("USERPROFILE") {
return Some(PathBuf::from(profile));
}
None
}
fn dcr_config_dir() -> Option<PathBuf> {
home_dir().map(|h| h.join(".dcr"))
}
pub fn get_registry_config() -> Option<DcrConfig> { pub fn get_registry_config() -> Option<DcrConfig> {
let home = std::env::var("HOME").ok()?; let home = home_dir()?;
let config_path = Path::new(&home).join(".dcr/config.toml"); let config_path = home.join(".dcr/config.toml");
if !config_path.exists() { if !config_path.exists() {
return None; return None;
} }
@@ -30,8 +44,9 @@ pub fn get_index_path() -> PathBuf {
std::env::var("DCR_INDEX_PATH") std::env::var("DCR_INDEX_PATH")
.map(PathBuf::from) .map(PathBuf::from)
.unwrap_or_else(|_| { .unwrap_or_else(|_| {
let home = std::env::var("HOME").unwrap_or_else(|_| ".".to_string()); dcr_config_dir()
Path::new(&home).join(".dcr").join("index.json") .unwrap_or_else(|| PathBuf::from("."))
.join("index.json")
}) })
} }

View File

@@ -20,10 +20,21 @@ pub struct RegistryManager {
pub path: PathBuf, pub path: PathBuf,
} }
fn home_dir() -> Option<PathBuf> {
if let Ok(home) = std::env::var("HOME") {
return Some(PathBuf::from(home));
}
if let Ok(profile) = std::env::var("USERPROFILE") {
return Some(PathBuf::from(profile));
}
None
}
impl RegistryManager { impl RegistryManager {
pub fn load() -> Result<Self, Box<dyn std::error::Error>> { pub fn load() -> Result<Self, Box<dyn std::error::Error>> {
let home = std::env::var("HOME")?; let dcr_dir = home_dir()
let dcr_dir = PathBuf::from(home).join(".dcr"); .ok_or("Cannot determine home directory")?
.join(".dcr");
let config_path = dcr_dir.join("config.toml"); let config_path = dcr_dir.join("config.toml");
if !config_path.exists() { if !config_path.exists() {