Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions kclvm/cmd/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ pub fn app() -> clap::App<'static> {
(@arg path_selector: ... -S --path_selector "Specify the path selector")
(@arg overrides: ... -O --overrides +takes_value "Specify the configuration override path and value")
(@arg target: --target +takes_value "Specify the target type")
(@arg package_map: ... -E --external +takes_value "Mapping of package name and path where the package is located")
)
(@subcommand lint =>
(@arg input: ... "Sets the input file to use")
Expand Down
5 changes: 5 additions & 0 deletions kclvm/cmd/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use kclvm_config::settings::{build_settings_pathbuf, Config, SettingsFile, Setti
use kclvm_driver::arguments::parse_key_value_pair;
use kclvm_error::Handler;
use kclvm_runtime::PanicInfo;
use std::collections::HashMap;

/// Build settings from arg matches.
pub(crate) fn must_build_settings(matches: &ArgMatches) -> SettingsPathBuf {
Expand All @@ -30,11 +31,14 @@ pub(crate) fn build_settings(matches: &ArgMatches) -> Result<SettingsPathBuf> {
Some(files) => files.into_iter().collect::<Vec<&str>>(),
None => vec![],
};

let setting_files = matches
.values_of("setting")
.map(|files| files.into_iter().collect::<Vec<&str>>());
let arguments = strings_from_matches(matches, "arguments");

let package_maps = hashmaps_from_matches(matches, "package_map").transpose()?;

build_settings_pathbuf(
files.as_slice(),
setting_files,
Expand All @@ -47,6 +51,7 @@ pub(crate) fn build_settings(matches: &ArgMatches) -> Result<SettingsPathBuf> {
disable_none: bool_from_matches(matches, "disable_none"),
verbose: u32_from_matches(matches, "verbose"),
debug: bool_from_matches(matches, "debug"),
package_maps,
..Default::default()
}),
kcl_options: if arguments.is_some() {
Expand Down
22 changes: 22 additions & 0 deletions kclvm/cmd/src/util.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use anyhow::bail;
use anyhow::Result;
use clap::ArgMatches;
use kclvm_driver::arguments::parse_key_value_pair;
use std::collections::HashMap;

#[inline]
pub(crate) fn strings_from_matches(matches: &ArgMatches, key: &str) -> Option<Vec<String>> {
Expand All @@ -10,6 +14,24 @@ pub(crate) fn strings_from_matches(matches: &ArgMatches, key: &str) -> Option<Ve
})
}

#[inline]
pub(crate) fn hashmaps_from_matches(
matches: &ArgMatches,
key: &str,
) -> Option<Result<HashMap<String, String>>> {
matches.values_of(key).map(|files| {
files
.into_iter()
.map(|s| match parse_key_value_pair(s) {
Ok(pair) => Ok((pair.key, pair.value)),
Err(err) => {
bail!("Invalid arguments format '-E, --external', use'kclvm_cli run --help' for more help.")
}
})
.collect::<Result<HashMap<String, String>>>()
})
}

#[inline]
pub(crate) fn string_from_matches(matches: &ArgMatches, key: &str) -> Option<String> {
matches.value_of(key).map(|v| v.to_string())
Expand Down
7 changes: 6 additions & 1 deletion kclvm/config/src/settings.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright 2021 The KCL Authors. All rights reserved.
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use std::{collections::HashMap, path::PathBuf};

/// Default settings file `kcl.yaml`
pub const DEFAULT_SETTING_FILE: &str = "kcl.yaml";
Expand Down Expand Up @@ -56,6 +56,9 @@ pub struct Config {
pub disable_none: Option<bool>,
pub verbose: Option<u32>,
pub debug: Option<bool>,
// kclvm needs a mapping between the package name and the package path
// to determine the source code path corresponding to different version package.
pub package_maps: Option<HashMap<String, String>>,
}

impl SettingsFile {
Expand All @@ -71,6 +74,7 @@ impl SettingsFile {
disable_none: Some(false),
verbose: Some(0),
debug: Some(false),
package_maps: Some(HashMap::default()),
}),
kcl_options: Some(vec![]),
}
Expand Down Expand Up @@ -157,6 +161,7 @@ pub fn merge_settings(settings: &[SettingsFile]) -> SettingsFile {
set_if!(result_kcl_cli_configs, disable_none, kcl_cli_configs);
set_if!(result_kcl_cli_configs, verbose, kcl_cli_configs);
set_if!(result_kcl_cli_configs, debug, kcl_cli_configs);
set_if!(result_kcl_cli_configs, package_maps, kcl_cli_configs);
}
}
if let Some(kcl_options) = &setting.kcl_options {
Expand Down
19 changes: 11 additions & 8 deletions kclvm/parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ pub struct LoadProgramOptions {
pub work_dir: String,
pub k_code_list: Vec<String>,
pub vendor_dirs: Vec<String>,
pub package_maps: HashMap<String, String>,

pub cmd_args: Vec<ast::CmdArgSpec>,
pub cmd_overrides: Vec<ast::OverrideSpec>,
Expand All @@ -199,6 +200,7 @@ impl Default for LoadProgramOptions {
work_dir: Default::default(),
k_code_list: Default::default(),
vendor_dirs: vec![get_vendor_home()],
package_maps: Default::default(),
cmd_args: Default::default(),
cmd_overrides: Default::default(),
mode: ParseMode::ParseComments,
Expand Down Expand Up @@ -576,15 +578,16 @@ impl Loader {
/// Look for [`pkgpath`] in the external package's home.
/// If found, return to the [`pkgroot`], else return [`None`]
fn is_external_pkg(&self, pkgpath: &str) -> Result<Option<String>, String> {
let root_path = match self.pkg_exists(self.opts.vendor_dirs.clone(), pkgpath) {
Some(path) => path,
None => return Ok(None),
};
let pkg_name = self.parse_external_pkg_name(pkgpath)?;

let pathbuf = PathBuf::from(root_path);
let rootpkg = pathbuf
.join(self.parse_external_pkg_name(pkgpath)?)
.join(KCL_MOD_FILE);
let rootpkg = if let Some(root) = self.opts.package_maps.get(&pkg_name) {
PathBuf::from(root).join(KCL_MOD_FILE)
} else {
match self.pkg_exists(self.opts.vendor_dirs.clone(), pkgpath) {
Some(path) => PathBuf::from(path).join(pkg_name).join(KCL_MOD_FILE),
None => return Ok(None),
}
};

if rootpkg.exists() {
return Ok(Some(
Expand Down
87 changes: 87 additions & 0 deletions kclvm/parser/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ fn set_vendor_home() -> String {
/// The testing will set environment variables,
/// so can not to execute test cases concurrently.
fn test_in_order() {
test_import_vendor_by_external_arguments();
println!("{:?} PASS", "test_import_vendor_by_external_arguments");
test_import_vendor_without_vendor_home();
println!("{:?} PASS", "test_import_vendor_without_vendor_home");
test_import_vendor_without_kclmod();
Expand Down Expand Up @@ -329,3 +331,88 @@ fn test_import_vendor_without_kclmod_and_same_name() {
}
}
}

fn test_import_vendor_by_external_arguments() {
let vendor = set_vendor_home();
let sm = SourceMap::new(FilePathMapping::empty());
let sess = Arc::new(ParseSession::with_source_map(Arc::new(sm)));

let external_dir = &PathBuf::from(".")
.join("testdata")
.join("test_vendor")
.canonicalize()
.unwrap();

let test_cases = vec![
(
"import_by_external_assign.k",
"assign",
vec!["__main__", "assign"],
),
(
"import_by_external_config_expr.k",
"config_expr",
vec!["__main__", "config_expr"],
),
(
"import_by_external_nested_vendor.k",
"nested_vendor",
vec![
"__main__",
"nested_vendor",
"vendor_subpkg",
"sub.sub2",
"sub.sub1",
"sub.sub",
"sub",
],
),
(
"import_by_external_vendor_subpkg.k",
"vendor_subpkg",
vec![
"__main__",
"vendor_subpkg",
"sub.sub1",
"sub.sub2",
"sub.sub",
"sub",
],
),
];

let dir = &PathBuf::from(".")
.join("testdata_without_kclmod")
.canonicalize()
.unwrap();

test_cases
.into_iter()
.for_each(|(test_case_name, dep_name, pkgs)| {
let mut opts = LoadProgramOptions::default();
opts.package_maps.insert(
dep_name.to_string(),
external_dir.join(dep_name).display().to_string(),
);
let test_case_path = dir.join(test_case_name).display().to_string();
let m = load_program(sess.clone(), &[&test_case_path], None).unwrap();

assert_eq!(m.pkgs.len(), pkgs.len());
m.pkgs.into_iter().for_each(|(name, modules)| {
assert!(pkgs.contains(&name.as_str()));
for pkg in pkgs.clone() {
if name == pkg {
if name == "__main__" {
assert_eq!(modules.len(), 1);
assert_eq!(modules.get(0).unwrap().filename, test_case_path);
} else {
modules.into_iter().for_each(|module| {
assert!(module.filename.contains(&vendor));
});
}
break;
}
}
});
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import assign as a

t1 = a.a
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import config_expr as ce

t = ce.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import nested_vendor as nv

t = sv.ub_in_subpkg
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import vendor_subpkg as vs

t1 = vs.sub_in_subpkg
7 changes: 7 additions & 0 deletions kclvm/runner/src/runner.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::collections::HashMap;

use kclvm_ast::ast;
use kclvm_config::{
modfile::get_vendor_home,
Expand All @@ -23,6 +25,9 @@ pub type kclvm_value_ref_t = std::ffi::c_void;
pub struct ExecProgramArgs {
pub work_dir: Option<String>,
pub k_filename_list: Vec<String>,
// -E key=value
#[serde(skip)]
pub package_maps: HashMap<String, String>,
pub k_code_list: Vec<String>,
// -D key=value
pub args: Vec<ast::CmdArgSpec>,
Expand Down Expand Up @@ -80,6 +85,7 @@ impl ExecProgramArgs {
kclvm_parser::LoadProgramOptions {
work_dir: self.work_dir.clone().unwrap_or_default(),
vendor_dirs: vec![get_vendor_home()],
package_maps: self.package_maps.clone(),
k_code_list: self.k_code_list.clone(),
cmd_args: self.args.clone(),
cmd_overrides: self.overrides.clone(),
Expand All @@ -106,6 +112,7 @@ impl TryFrom<SettingsFile> for ExecProgramArgs {
args.overrides.push(parse_override_spec(override_str)?);
}
args.path_selector = cli_configs.path_selector.unwrap_or_default();
args.package_maps = cli_configs.package_maps.unwrap_or(HashMap::default())
}
if let Some(options) = settings.kcl_options {
args.args = options
Expand Down