Skip to content

Commit

Permalink
feat: treat kcl.mod file as a compile unit
Browse files Browse the repository at this point in the history
Signed-off-by: peefy <xpf6677@163.com>
  • Loading branch information
Peefy committed May 22, 2024
1 parent 9e1ff13 commit 857b90a
Show file tree
Hide file tree
Showing 5 changed files with 247 additions and 113 deletions.
2 changes: 0 additions & 2 deletions kclvm/config/src/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ pub const KCL_CACHE_PATH_ENV_VAR: &str = "KCL_CACHE_PATH";
pub type CacheInfo = Vec<u8>;
pub type Cache = HashMap<String, CacheInfo>;

#[allow(dead_code)]
pub struct CacheOption {
cache_dir: String,
}
Expand Down Expand Up @@ -150,7 +149,6 @@ fn get_cache_dir(root: &str, cache_dir: Option<&str>) -> String {
}

#[inline]
#[allow(dead_code)]
fn get_cache_filename(root: &str, target: &str, pkgpath: &str, cache_dir: Option<&str>) -> String {
let cache_dir = cache_dir.unwrap_or(DEFAULT_CACHE_DIR);
let root = std::env::var(KCL_CACHE_PATH_ENV_VAR).unwrap_or(root.to_string());
Expand Down
225 changes: 158 additions & 67 deletions kclvm/config/src/modfile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,133 @@

use anyhow::Result;
use kclvm_utils::path::PathPrefix;
use serde::Deserialize;
use std::{env, fs, io::Read, path::PathBuf};
use serde::{Deserialize, Serialize};
use std::{
collections::HashMap,
env, fs,
io::Read,
path::{Path, PathBuf},
};
use toml;

use crate::path::ModRelativePath;

pub const KCL_MOD_FILE: &str = "kcl.mod";
pub const KCL_MOD_LOCK_FILE: &str = "kcl.mod.lock";
pub const KCL_FILE_SUFFIX: &str = ".k";
pub const KCL_FILE_EXTENSION: &str = "k";
pub const KCL_MOD_PATH_ENV: &str = "${KCL_MOD}";
pub const KCL_PKG_PATH: &str = "KCL_PKG_PATH";
pub const DEFAULT_KCL_HOME: &str = ".kcl";
pub const DEFAULT_KPM_SUBDIR: &str = "kpm";

/// ModFile is kcl package file 'kcl.mod'.
#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct ModFile {
pub package: Option<Package>,
pub profile: Option<Profile>,
pub dependencies: Option<Dependencies>,
}

/// Package is the kcl package section of 'kcl.mod'.
#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct Package {
/// The name of the package.
pub name: Option<String>,
/// The kcl compiler version
pub edition: Option<String>,
/// The version of the package.
pub version: Option<String>,
/// Description denotes the description of the package.
pub description: Option<String>,
/// Exclude denote the files to include when publishing.
pub include: Option<Vec<String>>,
/// Exclude denote the files to exclude when publishing.
pub exclude: Option<Vec<String>>,
}

/// Profile is the profile section of 'kcl.mod'.
/// It is used to specify the compilation options of the current package.
#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct Profile {
/// A list of entry-point files.
pub entries: Option<Vec<String>>,
/// Flag that, when true, disables the emission of the special 'none' value in the output.
pub disable_none: Option<bool>,
/// Flag that, when true, ensures keys in maps are sorted.
pub sort_keys: Option<bool>,
/// A list of attribute selectors for conditional compilation.
pub selectors: Option<Vec<String>>,
/// A list of override paths.
pub overrides: Option<Vec<String>>,
/// A list of additional options for the KCL compiler.
pub options: Option<Vec<String>>,
}

/// A map of package names to their respective dependency specifications.
pub type Dependencies = HashMap<String, Dependency>;

/// Dependency represents a single dependency for a package, which may come in different forms
/// such as version, Git repository, OCI repository, or a local path.
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
#[serde(untagged)]
pub enum Dependency {
/// Specifies a version dependency, e.g., "1.0.0".
Version(String),
/// Specifies a Git source dependency.
Git(GitSource),
/// Specifies an OCI (Open Container Initiative) image source dependency.
Oci(OciSource),
/// Specifies a local path dependency.
Local(LocalSource),
}

#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct GitSource {
/// The URL of the Git repository.
pub git: String,
/// An optional branch name within the Git repository.
pub branch: Option<String>,
/// An optional commit hash to check out from the Git repository.
pub commit: Option<String>,
/// An optional tag name to check out from the Git repository.
pub tag: Option<String>,
/// An optional version specification associated with Git source.
pub version: Option<String>,
}

/// Defines an OCI package as a source for a dependency.
#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct OciSource {
// The URI of the OCI repository.
pub oci: String,
/// An optional tag of the OCI package in the registry.
pub tag: Option<String>,
}

/// Defines a local filesystem path as a source for a dependency.
#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct LocalSource {
/// The path to the local directory or file.
pub path: String,
}

impl ModFile {
#[inline]
pub fn get_entries(&self) -> Option<Vec<String>> {
self.profile.as_ref().map(|p| p.entries.clone()).flatten()
}
}

/// Load kcl mod file from path
pub fn load_mod_file<P: AsRef<Path>>(path: P) -> Result<ModFile> {
let file_path = path.as_ref().join(KCL_MOD_FILE);
let mut file = std::fs::File::open(file_path)?;
let mut buffer: Vec<u8> = vec![];
file.read_to_end(&mut buffer)?;
toml::from_slice(buffer.as_slice()).map_err(|e| anyhow::anyhow!(e))
}

/// Get the path holding the external kcl package.
/// From the environment variable KCL_PKG_PATH.
/// If `KCL_PKG_PATH` is not present, then the user root string is returned.
Expand Down Expand Up @@ -51,39 +164,16 @@ pub fn create_default_vendor_home() -> Option<String> {
match kpm_home.canonicalize() {
Ok(path) => return Some(path.display().to_string()),
Err(_) => match fs::create_dir_all(kpm_home.clone()) {
Ok(_) => return Some(kpm_home.canonicalize().unwrap().display().to_string()),
Ok(_) => match kpm_home.canonicalize() {
Ok(p) => Some(p.display().to_string()),
Err(_) => None,
},
Err(_) => None,
},
}
}

#[allow(dead_code)]
#[derive(Default, Deserialize)]
pub struct KCLModFile {
pub root: Option<String>,
pub root_pkg: Option<String>,
pub build: Option<KCLModFileBuildSection>,
pub expected: Option<KCLModFileExpectedSection>,
}

#[allow(dead_code)]
#[derive(Default, Deserialize)]
pub struct KCLModFileBuildSection {
pub enable_pkg_cache: Option<bool>,
pub cached_pkg_prefix: Option<String>,
pub target: Option<String>,
}

#[allow(dead_code)]
#[derive(Default, Deserialize)]
pub struct KCLModFileExpectedSection {
pub min_build_time: Option<String>,
pub max_build_time: Option<String>,
pub kclvm_version: Option<String>,
pub kcl_plugin_version: Option<String>,
pub global_version: Option<String>,
}

/// Get package root path from input file paths and workdir.
pub fn get_pkg_root_from_paths(file_paths: &[String], workdir: String) -> Result<String, String> {
if file_paths.is_empty() {
return Err("No input KCL files or paths".to_string());
Expand Down Expand Up @@ -114,6 +204,7 @@ pub fn get_pkg_root_from_paths(file_paths: &[String], workdir: String) -> Result
}
}

/// Get package root path from the single input file path.
pub fn get_pkg_root(k_file_path: &str) -> Option<String> {
if k_file_path.is_empty() {
return None;
Expand Down Expand Up @@ -143,17 +234,6 @@ pub fn get_pkg_root(k_file_path: &str) -> Option<String> {
None
}

pub fn load_mod_file(root: &str) -> KCLModFile {
let k_mod_file_path = std::path::Path::new(root).join(KCL_MOD_FILE);
if !k_mod_file_path.exists() {
return KCLModFile::default();
}
let mut file = std::fs::File::open(k_mod_file_path.to_str().unwrap()).unwrap();
let mut buffer: Vec<u8> = vec![];
file.read_to_end(&mut buffer).unwrap();
toml::from_slice(buffer.as_slice()).unwrap()
}

#[cfg(test)]
mod modfile_test {
use crate::modfile::*;
Expand Down Expand Up @@ -190,37 +270,48 @@ mod modfile_test {

#[test]
fn test_load_mod_file() {
let kcl_mod = load_mod_file(TEST_ROOT);
assert!(kcl_mod.build.as_ref().unwrap().enable_pkg_cache.unwrap());
let kcl_mod = load_mod_file(TEST_ROOT).unwrap();
assert_eq!(
kcl_mod.package.as_ref().unwrap().name.as_ref().unwrap(),
"test_add_deps"
);
assert_eq!(
kcl_mod.package.as_ref().unwrap().version.as_ref().unwrap(),
"0.0.1"
);
assert_eq!(
kcl_mod.package.as_ref().unwrap().edition.as_ref().unwrap(),
"0.0.1"
);
assert_eq!(
kcl_mod.profile.as_ref().unwrap().entries.as_ref().unwrap(),
&vec!["main.k".to_string()]
);
assert_eq!(
kcl_mod.dependencies.as_ref().unwrap().get("pkg0"),
Some(&Dependency::Git(GitSource {
git: "test_url".to_string(),
tag: Some("test_tag".to_string()),
..Default::default()
}))
);
assert_eq!(
kcl_mod
.build
.as_ref()
.unwrap()
.cached_pkg_prefix
.as_ref()
.unwrap(),
"pkg.path"
kcl_mod.dependencies.as_ref().unwrap().get("pkg1"),
Some(&Dependency::Version("oci_tag1".to_string()))
);
assert_eq!(
kcl_mod
.expected
.as_ref()
.unwrap()
.kclvm_version
.as_ref()
.unwrap(),
"v0.3.0"
kcl_mod.dependencies.as_ref().unwrap().get("pkg2"),
Some(&Dependency::Oci(OciSource {
oci: "oci://ghcr.io/kcl-lang/helloworld".to_string(),
tag: Some("0.1.1".to_string()),
..Default::default()
}))
);
assert_eq!(
kcl_mod
.expected
.as_ref()
.unwrap()
.kcl_plugin_version
.as_ref()
.unwrap(),
"v0.2.0"
kcl_mod.dependencies.as_ref().unwrap().get("pkg3"),
Some(&Dependency::Local(LocalSource {
path: "../pkg".to_string(),
}))
);
}
}
19 changes: 13 additions & 6 deletions kclvm/config/src/testdata/kcl.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
[build]
enable_pkg_cache=true
cached_pkg_prefix="pkg.path"
[expected]
kclvm_version="v0.3.0"
kcl_plugin_version="v0.2.0"
[package]
name = "test_add_deps"
edition = "0.0.1"
version = "0.0.1"

[dependencies]
pkg0 = { git = "test_url", tag = "test_tag" }
pkg1 = "oci_tag1"
pkg2 = { oci = "oci://ghcr.io/kcl-lang/helloworld", tag = "0.1.1" }
pkg3 = { path = "../pkg"}

[profile]
entries = ["main.k"]
12 changes: 6 additions & 6 deletions kclvm/config/src/vfs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@ pub fn is_rel_pkgpath(pkgpath: &str) -> bool {

pub fn fix_import_path(root: &str, filepath: &str, import_path: &str) -> String {
// relpath: import .sub
// FixImportPath(root, "path/to/app/file.k", ".sub") => path.to.app.sub
// FixImportPath(root, "path/to/app/file.k", "..sub") => path.to.sub
// FixImportPath(root, "path/to/app/file.k", "...sub") => path.sub
// FixImportPath(root, "path/to/app/file.k", "....sub") => sub
// FixImportPath(root, "path/to/app/file.k", ".....sub") => ""
// fix_import_path(root, "path/to/app/file.k", ".sub") => path.to.app.sub
// fix_import_path(root, "path/to/app/file.k", "..sub") => path.to.sub
// fix_import_path(root, "path/to/app/file.k", "...sub") => path.sub
// fix_import_path(root, "path/to/app/file.k", "....sub") => sub
// fix_import_path(root, "path/to/app/file.k", ".....sub") => ""
//
// abspath: import path.to.sub
// FixImportPath(root, "path/to/app/file.k", "path.to.sub") => path.to.sub
// fix_import_path(root, "path/to/app/file.k", "path.to.sub") => path.to.sub

if !import_path.starts_with('.') {
return import_path.to_string();
Expand Down
Loading

0 comments on commit 857b90a

Please sign in to comment.