diff --git a/src/build_sys.rs b/src/build_sys.rs index 5ca29af..a60c322 100644 --- a/src/build_sys.rs +++ b/src/build_sys.rs @@ -1,8 +1,10 @@ +use crate::config::Dependnecy; use crate::utils; use crate::utils::Language; use crate::{config::Config, constants::CONFIG_FILE}; use anyhow::{anyhow, Result}; +use std::collections::{HashMap, HashSet}; use std::{fs, path::Path}; pub fn create_project(path: &Path, lang: Language) -> Result<()> { @@ -106,57 +108,40 @@ pub fn link_sys_lib(path: &Path) -> Vec { } pub(super) fn link_dep_files( - proj_dir: &Path, + config: &Config, language: Language, out_buffer: &mut Vec, ) -> Result<()> { - let code_deps = proj_dir.join("dependencies").join("source_code"); - if !code_deps.exists() { - return Ok(()); + let deps = match &config.dependnecy { + Some(d) => d.clone(), + None => return Ok((),) + }; + + // {key: uri, value: version} + let mut packages: HashMap = HashMap::new(); + + // {key: filename, value: uri file is from} + let mut filenames: HashMap = HashMap::new(); + + for dep in deps { + link_dep_files_h(&dep, language, out_buffer, &mut packages, &mut filenames)?; } - for f_name in code_deps.read_dir()? { - let f_name = match f_name { - Ok(f) => f, - Err(e) => { - dbg!(e); - continue; - } - }; - - if !f_name.file_type()?.is_file() { - continue; - } + Ok(()) +} - let f_osstr = f_name.file_name(); - let f_str = f_osstr.to_str().unwrap(); +pub(super) fn link_dep_headers(config: &Config) -> Result> { + let mut header_dirs = vec![]; - let valid_ext = match language { - Language::C => [".c"].as_slice(), - Language::Cpp => [".c", ".cpp"].as_slice(), - Language::Cuda => [".c", ".cpp", ".cu"].as_slice(), - }; + let mut packages: HashSet = HashSet::new(); - if !valid_ext.iter().any(|&ext| f_str.ends_with(ext)) { - continue; + if let Some(deps) = &config.dependnecy { + for dep in deps { + link_dep_headers_h(dep, &mut header_dirs, &mut packages)?; } - - let f_path = code_deps.join(f_str); - - let f_path_str = f_path.to_str().unwrap().to_string(); - out_buffer.push(f_path_str); } - Ok(()) -} - -pub(super) fn link_dep_headers(proj_dir: &Path) -> Result> { - let headers_dir = proj_dir.join("dependencies").join("header_files"); - if !headers_dir.exists() { - return Ok(None); - } - let headers_path = headers_dir.to_str().unwrap().to_string(); - Ok(Some(headers_path)) + Ok(header_dirs) } pub(super) fn link_dep_shared_obj(proj_dir: &Path) -> Result> { @@ -185,7 +170,7 @@ pub fn full_compilation_cmd( profile: &str, link_file: &Vec, link_lib: &Vec, - header_dir: &Option, + header_dirs: &Vec, shared_obj_dir: &Option, flags: &Vec, ) -> Result> { @@ -204,10 +189,11 @@ pub fn full_compilation_cmd( command.extend_from_slice(&["-o".to_string(), build_path]); - if let Some(header_dir) = header_dir { - let tmp = format!("-I{}", header_dir); + for header_dir in header_dirs { + let tmp: String = format!("-I{}", header_dir); command.push(tmp); } + if let Some(shared_obj_dir) = shared_obj_dir { let tmp = format!("-L{}", shared_obj_dir); command.push(tmp); @@ -253,3 +239,130 @@ pub fn validate_proj_repo(path: &Path) -> Result<()> { } Ok(()) } + + +/// Helper function that recursivly links all the source code files +fn link_dep_files_h( + dep: &Dependnecy, + language: Language, + out_buffer: &mut Vec, + packages: &mut HashMap, + filenames: &mut HashMap, +) -> Result<()> { + let dep_id = format!("{}/{}", dep.owner(), dep.repo_name()); + + if let Some(_) = packages.get(&dep_id) { + // TODO: Handle version mismatches + return Ok(()); + } + + let source_dir = match dep.get_source_dir()? { + Some(s) => s, + None => return Err(anyhow!("{} has an ambiguous source dir", &dep.uri)), + }; + + packages.insert(dep_id, dep.version.clone()); + + let valid_ext = match language { + Language::C => [".c"].as_slice(), + Language::Cpp => [".c", ".cpp"].as_slice(), + Language::Cuda => [".c", ".cpp", ".cu"].as_slice(), + }; + + + for file in source_dir.read_dir()? { + let file = match file { + Ok(r) => r, + Err(err) => { + dbg!(err); + continue; + } + }; + if !file.file_type()?.is_file() { + continue; + } + + let filename = file.file_name() + .to_str() + .unwrap() + .to_string(); + + if !valid_ext.iter().any(|&ext| filename.ends_with(ext)) { + continue; + } + + if let Some(other_uri) = filenames.get(&filename) { + eprintln!("Fatal Error: Multiple dependencies have files of the same name:"); + eprintln!("{}", dep.uri); + eprintln!("{}", other_uri); + eprintln!("Common File: {}", &filename); + std::process::exit(1); + } + + filenames.insert(filename.clone(), dep.uri.clone()); + + let filepath = source_dir.join(&filename); + let filepath = filepath.to_str() + .unwrap() + .to_string(); + + out_buffer.push(filepath); + } + + + // Recursivley handle chain dependnecies + if let Some(kiln_cfg) = dep.get_kiln_cfg()? { + if let Some(chain_deps) = kiln_cfg.dependnecy { + for cd in &chain_deps { + link_dep_files_h(cd, language, out_buffer, packages, filenames)?; + } + } + } + + Ok(()) +} + +/// Helper function that recursivly links all the header file directories +fn link_dep_headers_h( + dep: &Dependnecy, + out_buffer: &mut Vec, + packages: &mut HashSet, +) -> Result<()> { + let dep_id = format!("{}/{}", dep.owner(), dep.repo_name()); + + if packages.contains(&dep_id) { + return Ok(()); + } else { + packages.insert(dep_id); + } + + let include_dir = match dep.get_include_dir()? { + Some(s) => s, + None => return Err(anyhow!("{} has an ambiguous include dir", &dep.uri)), + }; + + let inc_dir_string = include_dir + .to_str() + .unwrap() + .to_string(); + + if !include_dir.is_dir() { + eprintln!("Path to include directory points to a non-directory"); + eprintln!("[{}]'s include_dir points to {}", &dep.uri, &inc_dir_string); + eprintln!("Change/add the `include_dir = \"relative/path/to/include\"` fild in Kiln.Toml to fix this"); + std::process::exit(1); + } + + out_buffer.push(inc_dir_string); + + // Recursivley handle chain dependnecies + if let Some(kiln_cfg) = dep.get_kiln_cfg()? { + if let Some(chain_deps) = kiln_cfg.dependnecy { + for cd in &chain_deps { + link_dep_headers_h(cd, out_buffer, packages)?; + } + } + } + + Ok(()) +} \ No newline at end of file diff --git a/src/cli.rs b/src/cli.rs index 25cf88a..4e4f962 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -25,7 +25,7 @@ pub enum Commands { }, GenHeaders, Add { - dep: String, + dep_uri: String, }, PurgeGlobalInstalls, diff --git a/src/config.rs b/src/config.rs index 6e20a10..8e61c6d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,8 +1,11 @@ use anyhow::{anyhow, Result}; use serde::{Deserialize, Serialize}; -use std::{fs, path::Path}; +use std::{fs, path::{Path, PathBuf}}; use toml; +use crate::{constants::{CONFIG_FILE, PACKAGE_CONFIG_FILE, PACKAGE_DIR}, package_manager::{self, DepType}, utils}; +use crate::kiln_package::KilnPackageConfig; + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Config { pub project: Project, @@ -149,7 +152,7 @@ impl BuildOptions { } #[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct Dependnecy { +pub(super) struct Dependnecy { pub uri: String, pub version: String, pub include_dir: Option, @@ -157,3 +160,144 @@ pub struct Dependnecy { pub shared_object_dir: Option, pub static_lib_dir: Option, } + +impl Dependnecy { + pub(super) fn new(owner: &str, repo_name: &str, version: &str) -> Self { + Dependnecy { + uri: format!("https://github.com/{}/{}.git", owner, repo_name), + version: version.to_string(), + ..Dependnecy::default() + } + } + + pub(super) fn owner(&self) -> &str { + let (owner, _repo) = package_manager::parse_github_uri(&self.uri).unwrap(); + owner + } + + pub(super) fn repo_name(&self) -> &str { + let (_owner, repo) = package_manager::parse_github_uri(&self.uri).unwrap(); + repo + } + + pub(super) fn get_global_path(&self) -> PathBuf { + let (owner, repo) = package_manager::parse_github_uri(&self.uri).unwrap(); + + (*PACKAGE_DIR) + .join(owner) + .join(repo) + .join(&self.version) + } + + pub(super) fn get_kiln_cfg(&self) -> Result> { + let cfg_file = self.get_global_path().join(CONFIG_FILE); + if !cfg_file.exists() { + return Ok(None); + } + + let cfg = Config::from(&cfg_file)?; + Ok(Some(cfg)) + } + + pub(super) fn get_include_dir(&self) -> Result> { + let p = self.get_global_path(); + + if let Some(include_dir) = &self.include_dir { + return Ok(Some(utils::join_rel_path(&p, &include_dir))); + } + + let config_path = p.join(CONFIG_FILE); + if config_path.exists() { + let config = Config::from(&config_path)?; + return Ok(Some(p.join(&config.get_include_dir()))); + } + + let pgk_path = p.join(PACKAGE_CONFIG_FILE); + if pgk_path.exists() { + let pkg_cfg = KilnPackageConfig::from(&pgk_path)?; + return Ok(Some(p.join(&pkg_cfg.metadata.include_dir))); + } + + Ok(None) + } + + pub(super) fn get_source_dir(&self) -> Result> { + let p = self.get_global_path(); + + if let Some(source_dir) = &self.source_dir { + return Ok(Some(utils::join_rel_path(&p, &source_dir))); + } + + let config_path = p.join(CONFIG_FILE); + if config_path.exists() { + let config = Config::from(&config_path)?; + return Ok(Some(p.join(&config.get_src_dir()))); + } + + let pgk_path = p.join(PACKAGE_CONFIG_FILE); + if pgk_path.exists() { + let pkg_cfg = KilnPackageConfig::from(&pgk_path)?; + return Ok(Some(p.join(&pkg_cfg.metadata.source_dir))); + } + + Ok(None) + } + + pub(super) fn get_shared_obj_dir(&self) -> Result> { + let p = self.get_global_path(); + let so_path = Path::new("build").join("release"); + + let config_path = p.join(CONFIG_FILE); + if config_path.exists() { + return Ok(Some(p.join(so_path))); + } + + Ok(None) + } + + pub(super) fn get_static_lib_dir(&self) -> Result> { + let p = self.get_global_path(); + let sl_path = Path::new("build").join("release"); + + let config_path = p.join(CONFIG_FILE); + if config_path.exists() { + return Ok(Some(p.join(sl_path))); + } + + Ok(None) + } + + + /// Adds a depdendnecy if it doesn't already exist + /// Returns true if the dependency already exists + pub(super) fn add_dependency(deps: &mut Vec, new_dep: Dependnecy) -> bool { + for dep in deps.iter() { + if *dep == new_dep { + return true; + } + } + deps.push(new_dep); + false + } + + fn get_dep_dir(&self, dep_type: DepType) -> Result> { + match dep_type { + DepType::SourceCode => self.get_source_dir(), + DepType::HeaderFile => self.get_include_dir(), + DepType::SharedObject => self.get_shared_obj_dir(), + DepType::StaticLibrary => self.get_static_lib_dir(), + } + } + +} + + + +/// Computes weak equality. Evaluates to true if the github uri has the same +/// project name and owner +impl PartialEq for Dependnecy { + fn eq(&self, other: &Self) -> bool { + self.owner() == other.owner() && + self.repo_name() == other.repo_name() + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index a937ad0..3855721 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,7 +14,7 @@ use clap::Parser; use config::Config; use constants::{CONFIG_FILE, PACKAGE_DIR, SEPETATOR}; use package_manager::PkgError; -use std::{env, fs, path::Path, process}; +use std::{env, fs, path::Path, process, time}; use utils::Language; use valgrind::VgOutput; @@ -101,14 +101,14 @@ async fn main() { process::exit(1); } } - cli::Commands::Add { dep } => { + cli::Commands::Add { dep_uri } => { if let Err(e) = build_sys::validate_proj_repo(cwd.as_path()) { println!("{}", e); process::exit(1); } let mut config = config.unwrap(); - let (owner, proj_name) = package_manager::parse_github_uri(&dep).unwrap(); + let (owner, proj_name) = package_manager::parse_github_uri(&dep_uri).unwrap(); let res = package_manager::resolve_adding_package(&mut config, owner, proj_name, None); if let Err(err) = res.await { @@ -143,6 +143,7 @@ async fn main() { process::exit(1); } let config = config.unwrap(); + handle_check_installs(&config).await; if let Err(e) = handle_warnings(&config) { eprintln!("An error occured during static analysis:\n{}", e); @@ -163,6 +164,7 @@ async fn main() { process::exit(1); } let config = config.unwrap(); + handle_check_installs(&config).await; if let Err(e) = handle_warnings(&config) { eprintln!("An error occured during static analysis:\n{}", e); @@ -253,14 +255,14 @@ fn handle_build(profile: &str, config: &Config) -> Result<()> { let lang = Language::new(&config.project.language).unwrap(); let mut link_file = vec![]; - build_sys::link_dep_files(&cwd, lang, &mut link_file)?; + build_sys::link_dep_files(&config, lang, &mut link_file)?; build_sys::link_proj_files(&config, &cwd, lang, &mut link_file) .map_err(|err| anyhow!("Failed to link source files: {}", err))?; let link_lib = build_sys::link_sys_lib(&cwd); let opt_flags = build_sys::opt_flags(&profile, config).unwrap(); - let header_dir = build_sys::link_dep_headers(&cwd)?; + let header_dirs = build_sys::link_dep_headers(&config)?; let so_dir = build_sys::link_dep_shared_obj(&cwd)?; let compilation_cmd = build_sys::full_compilation_cmd( @@ -268,7 +270,7 @@ fn handle_build(profile: &str, config: &Config) -> Result<()> { &profile, &link_file, &link_lib, - &header_dir, + &header_dirs, &so_dir, &opt_flags, )?; @@ -387,3 +389,24 @@ fn handle_headers(config: &Config) -> Result<()> { } Ok(()) } + +/// Checks the deps listed in Kiln.Toml config for any that aren't installed globally. +/// If it fins finds any such packages, it installs them. +async fn handle_check_installs(config: &Config) { + let timer = time::Instant::now(); + + let mut config = config.clone(); + let not_installed = package_manager::check_pkgs(&config); + + if not_installed.len() > 1 { + dbg!(¬_installed); + } + + for i in not_installed { + package_manager::resolve_adding_package(&mut config, &i[0], &i[1], Some(&i[2])) + .await + .unwrap(); + } + + dbg!(timer.elapsed()); +} \ No newline at end of file diff --git a/src/package_manager.rs b/src/package_manager.rs index d515588..a8c3f59 100644 --- a/src/package_manager.rs +++ b/src/package_manager.rs @@ -1,14 +1,11 @@ -use crate::config::{self, Config, Dependnecy}; -use crate::constants::{CONFIG_FILE, PACKAGE_CONFIG_FILE, PACKAGE_DIR}; +use crate::config::{self, Dependnecy, Config}; +use crate::constants::{CONFIG_FILE, PACKAGE_CONFIG_FILE}; use crate::kiln_package::{self, KilnPackageConfig}; -use crate::utils; use std::collections::HashSet; -use std::env; -use std::ffi::OsStr; use std::fmt::Debug; use std::io::Write; use std::path::Path; -use std::{fs, path::PathBuf, time::Duration}; +use std::{fs, time::Duration}; use flate2::read::GzDecoder; use reqwest; @@ -58,102 +55,8 @@ pub(super) struct Tag { pub tarball_url: String, } -#[derive(Debug, Clone)] -pub(super) struct Package { - pub owner: String, - pub repo_name: String, - pub tag: Tag, -} - -impl Package { - pub(super) fn get_global_path(&self) -> PathBuf { - let package_dir = (*PACKAGE_DIR).clone(); - let package_name = format!("{}_{}_{}", &self.owner, &self.repo_name, &self.tag.name); - package_dir.join(&package_name) - } - - pub(super) fn get_kiln_cfg(&self) -> Result, PkgError> { - let cfg_file = self.get_global_path().join(CONFIG_FILE); - if !cfg_file.exists() { - return Ok(None); - } - - let cfg = Config::from(&cfg_file)?; - Ok(Some(cfg)) - } - - pub(super) fn get_include_dir(&self) -> Result { - let p = self.get_global_path(); - - let config_path = p.join(CONFIG_FILE); - if config_path.exists() { - let config = Config::from(&config_path)?; - return Ok(Path::new(&config.get_include_dir()).to_path_buf()); - } - - let pgk_path = p.join(PACKAGE_CONFIG_FILE); - if pgk_path.exists() { - let pkg_cfg = KilnPackageConfig::from(&pgk_path)?; - return Ok(Path::new(&pkg_cfg.metadata.include_dir).to_path_buf()); - } - - Err(PkgError::PkgAmbiguous("".to_string())) - } - - pub(super) fn get_source_dir(&self) -> Result { - let p = self.get_global_path(); - - let config_path = p.join(CONFIG_FILE); - if config_path.exists() { - let config = Config::from(&config_path)?; - return Ok(Path::new(&config.get_src_dir()).to_path_buf()); - } - - let pgk_path = p.join(PACKAGE_CONFIG_FILE); - if pgk_path.exists() { - let pkg_cfg = KilnPackageConfig::from(&pgk_path)?; - return Ok(Path::new(&pkg_cfg.metadata.source_dir).to_path_buf()); - } - - Err(PkgError::PkgAmbiguous("".to_string())) - } - - pub(super) fn get_shared_obj_dir(&self) -> Result { - let p = self.get_global_path(); - let so_path = Path::new("build").join("release"); - - let config_path = p.join(CONFIG_FILE); - if config_path.exists() { - return Ok(so_path); - } - - Err(PkgError::PkgAmbiguous("".to_string())) - } - - pub(super) fn get_static_lib_dir(&self) -> Result { - let p = self.get_global_path(); - let so_path = Path::new("build").join("release"); - - let config_path = p.join(CONFIG_FILE); - if config_path.exists() { - return Ok(so_path); - } - - Err(PkgError::PkgAmbiguous("".to_string())) - } - - pub(super) fn get_dep_dir(&self, dep_type: DepType) -> Result { - match dep_type { - DepType::SourceCode => self.get_source_dir(), - DepType::HeaderFile => self.get_include_dir(), - DepType::SharedObject => self.get_shared_obj_dir(), - DepType::StaticLibrary => self.get_static_lib_dir(), - } - } -} - #[derive(Debug, Clone, Copy)] -enum DepType { +pub(super) enum DepType { SourceCode, HeaderFile, SharedObject, @@ -208,7 +111,13 @@ pub(super) fn parse_github_uri(uri: &str) -> Result<(&str, &str), PkgError> { Ok((owner, proj_name)) } -async fn find_tags(owner: &str, repo_name: &str) -> Result, PkgError> { +pub(crate) fn uris_eq(uri1: &str, uri2: &str) -> Result { + let (o1, r1) = parse_github_uri(uri1)?; + let (o2, r2) = parse_github_uri(uri2)?; + Ok(o1 == o2 && r1 == r2) +} + +async fn find_tags(owner: &str, repo_name: &str) -> Result, PkgError> { let endpoint = format!("https://api.github.com/repos/{}/{}/tags", owner, repo_name); // println!("Endpoint: {}", endpoint); @@ -234,23 +143,20 @@ async fn find_tags(owner: &str, repo_name: &str) -> Result, PkgErro let tags: Vec = serde_json::from_str(&body)?; let mut packages = vec![]; for t in tags { - packages.push(Package { - owner: owner.to_string(), - repo_name: repo_name.to_string(), - tag: t, - }); + + packages.push(t); } - + Ok(packages) } /// Installs a package in the glocal cache. does NOT create a kiln-package.toml file /// If the package already exists locally, it does nothing -async fn install_globally(package: &Package) -> Result<(), PkgError> { +async fn install_globally(package: &Dependnecy, tag: &Tag) -> Result<(), PkgError> { let package_dir = package.get_global_path(); - let package_name = format!( + let tarball_tmp_name = format!( "{}_{}_{}", - &package.owner, &package.repo_name, &package.tag.name + &package.owner(), &package.repo_name(), &package.version ); if package_dir.exists() { @@ -259,15 +165,15 @@ async fn install_globally(package: &Package) -> Result<(), PkgError> { fs::create_dir_all(&package_dir)?; let res = reqwest::Client::new() - .get(package.tag.tarball_url.clone()) + .get(tag.tarball_url.clone()) .header("User-Agent", "Kiln Build System") .send(); let res = tokio::spawn(res); // Create a temporary directory - let tmp = TempDir::new()?; - let tmp_file = tmp.path().join(format!("{}.tar.gz", package_name)); + let tmp_dir = TempDir::new()?; + let tmp_file = tmp_dir.path().join(format!("{}.tar.gz", tarball_tmp_name)); let res = res.await??; if !res.status().is_success() { @@ -295,46 +201,6 @@ async fn install_globally(package: &Package) -> Result<(), PkgError> { Ok(()) } -/// Generates the `kiln-package.toml' file -/// PRECONDITION: This package must be locally installed -/// Returns false if the package file could not be infered -fn generate_package_file(package: &Package) -> Result<(), PkgError> { - let package_dir = package.get_global_path(); - - let kiln_pkg_toml = package_dir.join(PACKAGE_CONFIG_FILE); - if kiln_pkg_toml.exists() { - return Ok(()); - } - - let kiln_config = package_dir.join(CONFIG_FILE); - if kiln_config.exists() { - let config = config::Config::from(&kiln_config)?; - - let pkg_cfg = - kiln_package::KilnPackageConfig::new(config.get_include_dir(), config.get_src_dir()); - - let pkg_cfg = toml::to_string_pretty(&pkg_cfg)?; - fs::write(kiln_pkg_toml, pkg_cfg)?; - return Ok(()); - } - - let include_dir = package_dir.join("include"); - let source_dir = package_dir.join("src"); - - if include_dir.exists() && source_dir.exists() { - let pkg_cfg = - kiln_package::KilnPackageConfig::new("include".to_string(), "src".to_string()); - - let pkg_cfg = toml::to_string_pretty(&pkg_cfg)?; - fs::write(kiln_pkg_toml, pkg_cfg)?; - return Ok(()); - } - - Err(PkgError::PkgAmbiguous( - "Package does not exist.".to_string(), - )) -} - /// Takes care of the entire installation process (High Level Function) /// PRECONDITION: CWD must be in the root directory of a kiln project /// This *will* take care of chained dependncies @@ -383,7 +249,8 @@ pub(super) async fn resolve_adding_package( for f in futures { let (chain_deps, cfg) = f.await??; - config.dependnecy.as_mut().unwrap().push(cfg); + let kiln_dcf_deps = config.dependnecy.as_mut().unwrap(); + config::Dependnecy::add_dependency(kiln_dcf_deps, cfg); deps.extend(chain_deps); } } @@ -392,7 +259,7 @@ pub(super) async fn resolve_adding_package( } /// Takes care of the remote to global to local instalation process -/// This recursive helper function to [fn resolve_adding_package] +/// This pseudo-recursive helper function to [fn resolve_adding_package] async fn add_package( owner: String, proj_name: String, @@ -409,33 +276,30 @@ async fn add_package( ))); } - let mut pkg: &Package = &tags[0]; + let mut tag: &Tag = &tags[0]; if let Some(v) = version { let mut assigned = false; for t in &tags { - if t.tag.name == v { - pkg = t; + if t.name == v { + tag = t; assigned = true; - break; } } if !assigned { - return Err(PkgError::UsrErr(format!( - "Version {} doesn't exist for {}", - v, repo_name - ))); + let msg = format!("Version {} does not exist for https:://{}/{}", v, owner, proj_name); + return Err(PkgError::UsrErr(msg)); } - } else { - pkg = tags - .iter() - .max_by(|&a, &b| a.tag.name.cmp(&b.tag.name)) - .unwrap(); } + else { + tag = tags.last().unwrap(); + } + + let pkg = Dependnecy::new(&owner, &proj_name, &tag.name); - install_globally(pkg).await?; - let res = generate_package_file(pkg); + install_globally(&pkg, &tag).await?; - if let Err(PkgError::PkgAmbiguous(_)) = res { + + if let None = pkg.get_include_dir()? { let mut stdin_buf = String::new(); let include_dir: String; let source_dir: String; @@ -455,39 +319,29 @@ async fn add_package( let out_path = pkg.get_global_path().join(PACKAGE_CONFIG_FILE); - #[cfg(debug_assertions)] - { - println!("out_path: {:?}", out_path.to_str()); - } - fs::write(out_path, pkg_cfg_str)?; - } else if let Err(e) = res { - return Err(e); } - copy_deps_global_to_local(pkg, DepType::HeaderFile)?; - copy_deps_global_to_local(pkg, DepType::SourceCode)?; - copy_deps_global_to_local(pkg, DepType::SharedObject)?; - copy_deps_global_to_local(pkg, DepType::StaticLibrary)?; - - let include_dir = match pkg.get_include_dir() { - Ok(r) => Some(r.to_str().unwrap().to_string()), - Err(_) => None, - }; - - let source_dir = match pkg.get_source_dir() { - Ok(r) => Some(r.to_str().unwrap().to_string()), - Err(_) => None, - }; - - let kiln_toml_dep = Dependnecy { - uri: repo_name.clone(), - version: pkg.tag.name.clone(), - include_dir, - source_dir, - shared_object_dir: None, - static_lib_dir: None, - }; + let include_dir = pkg + .get_include_dir()? + .unwrap() + .to_str() + .unwrap() + .to_string(); + + let source_dir = pkg + .get_source_dir()? + .unwrap() + .to_str() + .unwrap() + .to_string(); + + let mut pkg = pkg; + + if !pkg.get_global_path().join(CONFIG_FILE).exists() { + pkg.include_dir = Some(include_dir); + pkg.source_dir = Some(source_dir); + } let mut chain_dep_ids = vec![]; @@ -507,120 +361,55 @@ async fn add_package( } } - Ok((chain_dep_ids, kiln_toml_dep)) + Ok((chain_dep_ids, pkg)) } -/// Copies all the files from their globally installed location to their local spot in the project -/// This function is Atomic: it will remove all files it if an error occurs. -fn copy_deps_global_to_local(pkg: &Package, dep_type: DepType) -> Result<(), PkgError> { - let file_ext = match dep_type { - DepType::SourceCode => [".c", ".cpp", ".cu"].as_slice(), - DepType::HeaderFile => [".h", ".hpp", ".cuh"].as_slice(), - DepType::SharedObject => [".so", ".dylib", ".dll"].as_slice(), - DepType::StaticLibrary => [".a", ".lib"].as_slice(), - }; - let cwd = env::current_dir()?; +/// Ensures that all the packages listed in the Kiln.toml config file are +/// all installed globally. Any that are listed but are not installed will be +/// returned +pub(super) fn check_pkgs<'a>(config: &'a Config) -> Vec<[String; 3]> { + let mut not_installed = vec![]; + let mut pkgs_visited: HashSet = HashSet::new(); - let local_source_dir = cwd.join("dependencies").join(dep_type); - if !local_source_dir.exists() { - fs::create_dir_all(&local_source_dir)?; + if let Some(deps) = &config.dependnecy { + for dep in deps { + check_pkg_h(dep, &mut not_installed, &mut pkgs_visited); + } } - match pkg.get_dep_dir(dep_type) { - Ok(global_dep_dir_relative) => { - let mut global_dep_dir = pkg.get_global_path(); - let gddr_str = global_dep_dir_relative.to_str().unwrap(); + not_installed +} - if !matches!(gddr_str.trim(), "" | "." | "./") { - global_dep_dir = global_dep_dir.join(global_dep_dir_relative); - } +fn check_pkg_h(dep: &Dependnecy, output: &mut Vec<[String; 3]>, pkgs_visited: &mut HashSet) { + if pkgs_visited.contains(dep.uri.as_str()) { + return; + } + pkgs_visited.insert(dep.uri.clone()); - if !global_dep_dir.exists() { - if matches!(dep_type, DepType::SharedObject | DepType::StaticLibrary) { - return Ok(()); - } else { - let msg = format!("global dep dir doesn't exist: `dep_type = {:?}`", dep_type); - return Err(PkgError::Unknown(msg)); - } - } + if !dep.get_global_path().exists() { + let pkg = [ + dep.owner().to_string(), + dep.repo_name().to_string(), + dep.version.clone(), + ]; - for f in global_dep_dir.read_dir()? { - let f = match f { - Ok(r) => r, - Err(e) => { - let msg = format!("\nIterating over directory failed: {:?}", e); - dbg!(msg); - continue; - } - }; - let tmp = f.file_name(); - let g_filename = match tmp.to_str() { - Some(s) => s, - None => { - let msg = "Received `None` from OsStr.as_str()"; - dbg!(msg); - continue; - } - }; - - if !file_ext.iter().any(|&i| g_filename.ends_with(i)) { - let msg = format!("File `{}` doesn't have a valid file extention", g_filename); - dbg!(msg); - continue; - } - - let global_p = global_dep_dir.join(g_filename); - let new_p = local_source_dir.join(g_filename); - if new_p.exists() { - eprintln!( - "File {} already exists. (Multiple packages have files with this same name)", - g_filename - ); - eprintln!( - "Aborting import of https://githib.com/{}/{}", - pkg.owner, pkg.repo_name - ); - - // Remove all files we've added to prevent an invalid state - remove_deps_local(pkg, dep_type)?; - - std::process::exit(1); - } - - dbg!(&global_p); - - fs::copy(global_p, new_p)?; - } - } - Err(e) => { - let msg = format!("Global source dir could not be found: {}", e); - dbg!(msg); + if !output.contains(&pkg) { + output.push(pkg); } + return; } - Ok(()) -} - -fn remove_deps_local(pkg: &Package, dep_type: DepType) -> Result<(), PkgError> { - let cwd = env::current_dir()?; - - let global_source_dir = cwd.join("dependencies").join(dep_type); - let file_names: Vec = - global_source_dir.iter().map(|i| i.to_os_string()).collect(); - - if let Ok(local_source_dir) = pkg.get_source_dir() { - for f in local_source_dir.iter() { - let f = f.to_os_string(); - if file_names.contains(&f) { - fs::remove_file(&f)?; + if let Some(kiln_cfg) = dep.get_kiln_cfg().unwrap() { + if let Some(chain_deps) = &kiln_cfg.dependnecy { + for chain_dep in chain_deps { + check_pkg_h(chain_dep, output, pkgs_visited); } } } - - Ok(()) } + fn unpack_without_top_folder(reader: R, dst: &Path) -> Result<(), PkgError> { let mut archive = Archive::new(reader); for entry_result in archive.entries()? { diff --git a/src/utils.rs b/src/utils.rs index c12c395..485366d 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,6 +1,6 @@ use std::collections::HashSet; use std::fs; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::process::{self, Command}; use anyhow::{anyhow, Result}; @@ -169,21 +169,10 @@ pub fn print_warning( println!("{}\n", err_msg); } - -pub(super) fn create_symlink>(src_file: T, dst_link: T) -> Result<(), std::io::Error> { - #[cfg(target_family = "unix")] - { - std::os::unix::fs::symlink(src_file, dst_link)?; - } - #[cfg(target_family = "windows")] - { - if original.is_file() { - std::os::windows::fs::symlink_file(src_file, dst_link) - } else if original.is_dir() { - std::os::windows::fs::symlink_dir(src_file, dst_link) - } else { - Err(io::Error::new(io::ErrorKind::Other, "Original path is neither a file nor a directory.")) - } +pub(super) fn join_rel_path(abs_path: impl AsRef, rel_path: &str) -> PathBuf { + let path = abs_path.as_ref(); + match rel_path { + "" | "." | "./" => path.to_path_buf(), + _ => path.join(rel_path) } - Ok(()) } \ No newline at end of file