mirror of
https://github.com/dexoron/dcr.git
synced 2026-10-21 11:28:29 +00:00
feat: add pkg-config system dependencies and fix dcr new in non-ascii paths
This commit is contained in:
@@ -25,7 +25,7 @@ use toml::map::Map;
|
||||
|
||||
/// Parsed arguments for the `dcr add` command.
|
||||
///
|
||||
/// Exactly one of `path`, `git`, or `version_from_registry` is expected to be set,
|
||||
/// Exactly one of `path`, `git`, `pkg_config`, or `version_from_registry` is expected to be set,
|
||||
/// depending on how the dependency source was specified.
|
||||
pub struct AddArgs {
|
||||
/// Dependency name as it will appear under `[dependencies]` in `dcr.toml`.
|
||||
@@ -42,6 +42,8 @@ pub struct AddArgs {
|
||||
pub rev: Option<String>,
|
||||
/// Version string resolved from the package registry when no explicit source is given.
|
||||
pub version_from_registry: Option<String>,
|
||||
/// Package names resolved through pkg-config.
|
||||
pub pkg_config: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
/// Adds a dependency to the current project's `dcr.toml`.
|
||||
@@ -71,6 +73,7 @@ pub fn add(args: &[String]) -> i32 {
|
||||
println!(" gitlab:user/repo GitLab repository");
|
||||
println!(" git:host.com/user/repo Generic git repository");
|
||||
println!(" path:./path/to/lib Local path dependency");
|
||||
println!(" pkg-config:fmt System pkg-config dependency");
|
||||
println!(" <version> Version from registry");
|
||||
println!();
|
||||
printc("OPTIONS:", BOLD_GREEN);
|
||||
@@ -133,6 +136,17 @@ pub fn add(args: &[String]) -> i32 {
|
||||
}
|
||||
Value::Table(table)
|
||||
}
|
||||
} else if let Some(packages) = add_args.pkg_config {
|
||||
let mut table = Map::new();
|
||||
if packages.len() == 1 {
|
||||
table.insert("pkg-config".to_string(), Value::String(packages[0].clone()));
|
||||
} else {
|
||||
table.insert(
|
||||
"pkg-config".to_string(),
|
||||
Value::Array(packages.into_iter().map(Value::String).collect()),
|
||||
);
|
||||
}
|
||||
Value::Table(table)
|
||||
} else if let Some(path) = add_args.path {
|
||||
let mut table = Map::new();
|
||||
table.insert("path".to_string(), Value::String(path));
|
||||
@@ -140,7 +154,7 @@ pub fn add(args: &[String]) -> i32 {
|
||||
} else if let Some(version) = add_args.version_from_registry {
|
||||
Value::String(version)
|
||||
} else {
|
||||
error("Dependency source (path or git) must be provided");
|
||||
error("Dependency source (path, git, or pkg-config) must be provided");
|
||||
return 1;
|
||||
};
|
||||
|
||||
@@ -171,6 +185,7 @@ fn parse_add_args(args: &[String]) -> Result<AddArgs, i32> {
|
||||
error(" gitlab:user/repo -> gitlab.com/user/repo");
|
||||
error(" git:host.com/user/repo -> host.com/user/repo");
|
||||
error(" path:./path/to/lib -> local path");
|
||||
error(" pkg-config:fmt -> system pkg-config package");
|
||||
return Err(1);
|
||||
}
|
||||
|
||||
@@ -221,6 +236,7 @@ fn parse_add_args(args: &[String]) -> Result<AddArgs, i32> {
|
||||
tag: None,
|
||||
rev: None,
|
||||
version_from_registry: Some(version.to_string()),
|
||||
pkg_config: None,
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -262,9 +278,25 @@ fn parse_add_args(args: &[String]) -> Result<AddArgs, i32> {
|
||||
|
||||
let mut path = None;
|
||||
let mut git = None;
|
||||
let mut pkg_config = None;
|
||||
|
||||
// Map source prefixes / bare URLs to path or git fields
|
||||
if let Some(p) = source_spec.strip_prefix("path:") {
|
||||
if let Some(p) = source_spec
|
||||
.strip_prefix("pkg-config:")
|
||||
.or_else(|| source_spec.strip_prefix("pkg:"))
|
||||
{
|
||||
let packages = p
|
||||
.split(',')
|
||||
.map(str::trim)
|
||||
.filter(|name| !name.is_empty())
|
||||
.map(str::to_string)
|
||||
.collect::<Vec<_>>();
|
||||
if packages.is_empty() {
|
||||
error("pkg-config source must name at least one package");
|
||||
return Err(1);
|
||||
}
|
||||
pkg_config = Some(packages);
|
||||
} else if let Some(p) = source_spec.strip_prefix("path:") {
|
||||
path = Some(p.to_string());
|
||||
} else if let Some(g) = source_spec.strip_prefix("github:") {
|
||||
git = Some(format!("https://github.com/{}", g));
|
||||
@@ -295,7 +327,7 @@ fn parse_add_args(args: &[String]) -> Result<AddArgs, i32> {
|
||||
path = Some(source_spec);
|
||||
} else {
|
||||
error(
|
||||
"Source must be a path, a prefixed source (path:, git:, github:, gitlab:), or a full URL",
|
||||
"Source must be a path, pkg-config:, a prefixed source (path:, git:, github:, gitlab:), or a full URL",
|
||||
);
|
||||
return Err(1);
|
||||
}
|
||||
@@ -308,5 +340,6 @@ fn parse_add_args(args: &[String]) -> Result<AddArgs, i32> {
|
||||
tag,
|
||||
rev,
|
||||
version_from_registry: None,
|
||||
pkg_config,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -42,11 +42,13 @@ struct GenDeps {
|
||||
include_dirs: Vec<String>,
|
||||
lib_dirs: Vec<String>,
|
||||
libs: Vec<String>,
|
||||
cflags: Vec<String>,
|
||||
ldflags: Vec<String>,
|
||||
}
|
||||
|
||||
/// Collects path-dependency include/lib/libs from `dcr.toml` for code generation.
|
||||
/// Collects dependency metadata from `dcr.toml` for code generation.
|
||||
///
|
||||
/// Skips `system` deps and non-path entries. Falls back to common include/lib
|
||||
/// Resolves path layouts and pkg-config flags while skipping system deps. Falls back to common include/lib
|
||||
/// directory names only when those directories exist.
|
||||
fn resolve_deps_for_gen(config: &Config, profile: &str, project_root: &Path) -> GenDeps {
|
||||
let deps_val = match config.get("dependencies") {
|
||||
@@ -56,6 +58,8 @@ fn resolve_deps_for_gen(config: &Config, profile: &str, project_root: &Path) ->
|
||||
include_dirs: vec![],
|
||||
lib_dirs: vec![],
|
||||
libs: vec![],
|
||||
cflags: vec![],
|
||||
ldflags: vec![],
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -66,6 +70,8 @@ fn resolve_deps_for_gen(config: &Config, profile: &str, project_root: &Path) ->
|
||||
include_dirs: vec![],
|
||||
lib_dirs: vec![],
|
||||
libs: vec![],
|
||||
cflags: vec![],
|
||||
ldflags: vec![],
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -73,12 +79,37 @@ fn resolve_deps_for_gen(config: &Config, profile: &str, project_root: &Path) ->
|
||||
let mut include_dirs = Vec::new();
|
||||
let mut lib_dirs = Vec::new();
|
||||
let mut libs = Vec::new();
|
||||
let mut cflags = Vec::new();
|
||||
let mut ldflags = Vec::new();
|
||||
|
||||
for (name, value) in deps_table {
|
||||
let tbl = match value.as_table() {
|
||||
Some(t) => t,
|
||||
None => continue,
|
||||
};
|
||||
let pkg_names = tbl
|
||||
.get("pkg-config")
|
||||
.or_else(|| tbl.get("pkg_config"))
|
||||
.map(|raw| {
|
||||
raw.as_str()
|
||||
.map(|s| vec![s.to_string()])
|
||||
.or_else(|| {
|
||||
raw.as_array().map(|a| {
|
||||
a.iter()
|
||||
.filter_map(|v| v.as_str().map(str::to_string))
|
||||
.collect()
|
||||
})
|
||||
})
|
||||
.unwrap_or_default()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
if !pkg_names.is_empty() {
|
||||
let (flags, link_flags) =
|
||||
crate::utils::build::resolve_pkg_config_flags_lossy(&pkg_names, &[], &[]);
|
||||
cflags.extend(flags);
|
||||
ldflags.extend(link_flags);
|
||||
continue;
|
||||
}
|
||||
// skip system deps
|
||||
if tbl.get("system").and_then(|v| v.as_bool()).unwrap_or(false) {
|
||||
continue;
|
||||
@@ -169,6 +200,8 @@ fn resolve_deps_for_gen(config: &Config, profile: &str, project_root: &Path) ->
|
||||
include_dirs,
|
||||
lib_dirs,
|
||||
libs,
|
||||
cflags,
|
||||
ldflags,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,8 +383,10 @@ fn collect_project_info_inner(
|
||||
});
|
||||
|
||||
let resolved = resolve_deps_for_gen(&config, profile, &root);
|
||||
let (resolved_cflags, resolved_ldflags) =
|
||||
let (mut resolved_cflags, mut resolved_ldflags) =
|
||||
resolve_pkg_config_flags_lossy(&pkg_configs, &base_cflags, &base_ldflags);
|
||||
resolved_cflags.extend(resolved.cflags.clone());
|
||||
resolved_ldflags.extend(resolved.ldflags.clone());
|
||||
|
||||
// Build exclude/include pattern lists (same logic as cli::build)
|
||||
let mut combined_excludes: Vec<PathBuf> = Vec::new();
|
||||
|
||||
@@ -23,7 +23,6 @@ use crate::utils::log::{error, warn};
|
||||
use crate::utils::text::{BOLD_CYAN, BOLD_GREEN, colored, printc};
|
||||
use std::fs;
|
||||
use std::io::Write;
|
||||
use toml::Value;
|
||||
|
||||
/// Initializes the current working directory as a new DCR project.
|
||||
///
|
||||
@@ -91,18 +90,8 @@ pub fn init(args: &[String]) -> i32 {
|
||||
println!("Initializing the project in {cwd}");
|
||||
|
||||
// Create and initialize default manifest (dcr.toml).
|
||||
let mut config = match Config::new("./dcr.toml") {
|
||||
Ok(cfg) => cfg,
|
||||
Err(_) => {
|
||||
error("Failed to create dcr.toml");
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
if config
|
||||
.edit("package.name", Value::String(project_name.clone()))
|
||||
.is_err()
|
||||
{
|
||||
error("Failed to write dcr.toml");
|
||||
if Config::create("./dcr.toml", Some(&project_name)).is_err() {
|
||||
error("Failed to create dcr.toml");
|
||||
return 1;
|
||||
}
|
||||
println!(
|
||||
|
||||
@@ -23,7 +23,6 @@ use crate::utils::log::{error, warn};
|
||||
use crate::utils::text::{BOLD_CYAN, BOLD_GREEN, colored, printc};
|
||||
use std::fs;
|
||||
use std::io::Write;
|
||||
use toml::Value;
|
||||
|
||||
/// Executes the `dcr new` command to scaffold a new C/C++ project directory structure.
|
||||
///
|
||||
@@ -110,18 +109,8 @@ pub fn new(args: &[String]) -> i32 {
|
||||
|
||||
// Initialize and write project configuration manifest (dcr.toml).
|
||||
let toml_path = format!("./{project_name}/dcr.toml");
|
||||
let mut config = match Config::new(&toml_path) {
|
||||
Ok(cfg) => cfg,
|
||||
Err(_) => {
|
||||
error("Failed to create dcr.toml");
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
if config
|
||||
.edit("package.name", Value::String(project_name.to_string()))
|
||||
.is_err()
|
||||
{
|
||||
error("Failed to write dcr.toml");
|
||||
if Config::create(&toml_path, Some(project_name)).is_err() {
|
||||
error("Failed to create dcr.toml");
|
||||
return 1;
|
||||
}
|
||||
println!(
|
||||
|
||||
@@ -134,8 +134,12 @@ fn run_testsuite(profile: &str) -> Result<i32, String> {
|
||||
cflags.extend(get_list_with_profile(&config, "cflags", profile));
|
||||
let ldflags = get_list_with_profile(&config, "ldflags", profile);
|
||||
let pkg_configs = get_list_with_profile(&config, "pkg_config", profile);
|
||||
let (cflags, ldflags) = resolve_pkg_config_flags(&pkg_configs, &cflags, &ldflags)?;
|
||||
let includes = test_include_dirs(&config, profile);
|
||||
let (mut cflags, mut ldflags) = resolve_pkg_config_flags(&pkg_configs, &cflags, &ldflags)?;
|
||||
let deps = crate::core::deps::resolve_deps(&config, profile, None, Path::new("."))?;
|
||||
cflags.extend(deps.cflags);
|
||||
ldflags.extend(deps.ldflags);
|
||||
let mut includes = test_include_dirs(&config, profile);
|
||||
includes.extend(deps.include_dirs);
|
||||
let test_sources = collect_test_sources()?;
|
||||
if test_sources.is_empty() {
|
||||
return Err("tests/test.c not found; run 'dcr test --init' first".to_string());
|
||||
|
||||
@@ -115,6 +115,19 @@ fn print_deps(
|
||||
format!(" v{}", v)
|
||||
} else if let Some(git) = t.get("git").and_then(|v| v.as_str()) {
|
||||
format!(" ({})", git)
|
||||
} else if let Some(pkg) = t.get("pkg-config").or_else(|| t.get("pkg_config")) {
|
||||
if let Some(s) = pkg.as_str() {
|
||||
format!(" (pkg-config: {})", s)
|
||||
} else if let Some(arr) = pkg.as_array() {
|
||||
let names = arr
|
||||
.iter()
|
||||
.filter_map(|v| v.as_str())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
format!(" (pkg-config: {})", names)
|
||||
} else {
|
||||
"".to_string()
|
||||
}
|
||||
} else {
|
||||
"".to_string()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user