Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added kpm_update #1032

Closed
wants to merge 7 commits into from
Closed
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
67 changes: 2 additions & 65 deletions kclvm/driver/src/kpm_metadata.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use crate::{get_path_for_executable, kcl, lookup_the_nearest_file_dir};
use anyhow::{bail, Ok, Result};
use kclvm_parser::LoadProgramOptions;
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, env, iter, path::PathBuf, process::Command};
use std::{collections::HashMap, path::PathBuf, process::Command};

const MANIFEST_FILE: &str = "kcl.mod";

Expand Down Expand Up @@ -115,71 +116,7 @@ pub(crate) fn fetch_mod_metadata(manifest_path: PathBuf) -> Result<Metadata> {
}
}

/// [`lookup_the_nearest_file_dir`] will start from [`from`] and search for file [`the_nearest_file`] in the parent directories.
/// If found, it will return the [`Some`] of [`the_nearest_file`] file path. If not found, it will return [`None`]
pub(crate) fn lookup_the_nearest_file_dir(
from: PathBuf,
the_nearest_file: &str,
) -> Option<PathBuf> {
let mut current_dir = from;

loop {
let found_path = current_dir.join(the_nearest_file);
if found_path.is_file() {
return current_dir.canonicalize().ok();
}

match current_dir.parent() {
Some(parent) => current_dir = parent.to_path_buf(),
None => return None,
}
}
}

/// [`kcl`] will return the path for executable kcl binary.
pub fn kcl() -> PathBuf {
get_path_for_executable("kcl")
}

/// [`kpm`] will return the path for executable kpm binary.
pub fn kpm() -> PathBuf {
get_path_for_executable("kpm")
}

/// [`get_path_for_executable`] will return the path for [`executable_name`].
pub fn get_path_for_executable(executable_name: &'static str) -> PathBuf {
// The current implementation checks $PATH for an executable to use:
// `<executable_name>`
// example: for <executable_name>, this tries just <executable_name>, which will succeed if <executable_name> is on the $PATH

if lookup_in_path(executable_name) {
return executable_name.into();
}

executable_name.into()
}

/// [`lookup_in_path`] will search for an executable file [`exec`] in the environment variable ‘PATH’.
/// If found, return true, otherwise return false.
fn lookup_in_path(exec: &str) -> bool {
let paths = env::var_os("PATH").unwrap_or_default();
env::split_paths(&paths)
.map(|path| path.join(exec))
.find_map(probe)
.is_some()
}

/// [`probe`] check if the given path points to a file.
/// If it does, return [`Some`] of the path.
/// If not, check if adding the current operating system's executable file extension (if any) to the path points to a file.
/// If it does, return [`Some`] of the path with the extension added.
/// If neither, return [`None`].
fn probe(path: PathBuf) -> Option<PathBuf> {
let with_extension = match env::consts::EXE_EXTENSION {
"" => None,
it => Some(path.with_extension(it)),
};
iter::once(path)
.chain(with_extension)
.find(|it| it.is_file())
}
26 changes: 4 additions & 22 deletions kclvm/driver/src/kpm_update.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use crate::kpm_metadata::get_path_for_executable;
use crate::{kcl, lookup_the_nearest_file_dir};
use anyhow::{bail, Result};
use std::{path::PathBuf, process::Command};

const MANIFEST_FILE: &str = "kcl.mod";

/// Update the KCL module.
///
/// This function calls `kcl mod update` to update the KCL module.
pub(crate) fn update_kcl_module(manifest_path: PathBuf) -> Result<()> {
match lookup_the_nearest_file_dir(manifest_path.clone(), MANIFEST_FILE) {
octonawish-akcodes marked this conversation as resolved.
Show resolved Hide resolved
Some(mod_dir) => {
Expand Down Expand Up @@ -31,24 +34,3 @@ pub(crate) fn update_kcl_module(manifest_path: PathBuf) -> Result<()> {
),
}
}
pub fn kcl() -> PathBuf {
get_path_for_executable("kcl")
}

pub(crate) fn lookup_the_nearest_file_dir(
from: PathBuf,
the_nearest_file: &str,
) -> Option<PathBuf> {
let mut current_dir = from;

loop {
let found_path = current_dir.join(the_nearest_file);
if found_path.is_file() {
return current_dir.canonicalize().ok();
}
match current_dir.parent() {
Some(parent) => current_dir = parent.to_path_buf(),
None => return None,
}
}
}
65 changes: 65 additions & 0 deletions kclvm/driver/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use anyhow::Result;
use std::{env, iter};
pub mod arguments;
pub mod kpm_metadata;
pub mod kpm_update;
Expand Down Expand Up @@ -218,6 +219,70 @@ pub fn lookup_kcl_yaml(dir: &PathBuf) -> io::Result<PathBuf> {
}
}

/// Get the path for the KCL executable.
fn kcl() -> PathBuf {
get_path_for_executable("kcl")
}

/// [`get_path_for_executable`] will return the path for [`executable_name`].
pub fn get_path_for_executable(executable_name: &'static str) -> PathBuf {
// The current implementation checks $PATH for an executable to use:
// `<executable_name>`
// example: for <executable_name>, this tries just <executable_name>, which will succeed if <executable_name> is on the $PATH

if lookup_in_path(executable_name) {
return executable_name.into();
}

executable_name.into()
}

/// [`lookup_in_path`] will search for an executable file [`exec`] in the environment variable ‘PATH’.
/// If found, return true, otherwise return false.
fn lookup_in_path(exec: &str) -> bool {
let paths = env::var_os("PATH").unwrap_or_default();
env::split_paths(&paths)
.map(|path| path.join(exec))
.find_map(probe)
.is_some()
}

/// [`probe`] check if the given path points to a file.
/// If it does, return [`Some`] of the path.
/// If not, check if adding the current operating system's executable file extension (if any) to the path points to a file.
/// If it does, return [`Some`] of the path with the extension added.
/// If neither, return [`None`].
fn probe(path: PathBuf) -> Option<PathBuf> {
let with_extension = match env::consts::EXE_EXTENSION {
"" => None,
it => Some(path.with_extension(it)),
};
iter::once(path)
.chain(with_extension)
.find(|it| it.is_file())
}

/// [`lookup_the_nearest_file_dir`] will start from [`from`] and search for file [`the_nearest_file`] in the parent directories.
/// If found, it will return the [`Some`] of [`the_nearest_file`] file path. If not found, it will return [`None`]
pub(crate) fn lookup_the_nearest_file_dir(
from: PathBuf,
the_nearest_file: &str,
) -> Option<PathBuf> {
let mut current_dir = from;

loop {
let found_path = current_dir.join(the_nearest_file);
if found_path.is_file() {
return current_dir.canonicalize().ok();
}

match current_dir.parent() {
Some(parent) => current_dir = parent.to_path_buf(),
None => return None,
}
}
}

/// For the KCL project, some definitions may be introduced through multi-file
/// compilation (kcl.yaml). This function is used to start from a single file and try
/// to find a `compile unit` that contains all definitions
Expand Down
7 changes: 7 additions & 0 deletions kclvm/driver/src/test_data/kpm_update/kcl.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "kpm_update"
edition = "0.0.1"
version = "0.0.1"

[dependencies]
k8s = "1.29"
Empty file.
57 changes: 36 additions & 21 deletions kclvm/driver/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ use kclvm_parser::LoadProgramOptions;
use walkdir::WalkDir;

use crate::arguments::parse_key_value_pair;
use crate::kpm_metadata::{fetch_metadata, fill_pkg_maps_for_k_file, lookup_the_nearest_file_dir};
use crate::kpm_metadata::fetch_metadata;
use crate::kpm_update::update_kcl_module;
use crate::{canonicalize_input_files, expand_input_files, get_pkg_list};
use crate::{fill_pkg_maps_for_k_file, lookup_the_nearest_file_dir};

#[test]
fn test_canonicalize_input_files() {
Expand Down Expand Up @@ -382,28 +383,42 @@ fn test_get_pkg_list() {
);
}

#[cfg(test)]
// Define a mock structure to simulate the behavior of Command::output
struct MockCommand {
output: Result<MockCommandOutput, std::io::Error>,
#[test]
fn test_lookup_the_nearest_file_dir_for_update() {
let path = PathBuf::from(".")
.join("src")
.join("test_data")
.join("kpm_update");
let result = lookup_the_nearest_file_dir(path.clone(), "kcl.mod");
assert!(result.is_some());
assert_eq!(
result.unwrap().display().to_string(),
path.canonicalize().unwrap().display().to_string()
);

let main_path = path.join("subdir").join("main.k");
let result = lookup_the_nearest_file_dir(main_path, "kcl.mod");
assert!(result.is_some());
assert_eq!(
result.unwrap().display().to_string(),
path.canonicalize().unwrap().display().to_string()
);
}
// Define a mock structure to represent the output of Command::output
struct MockCommandOutput {
status: std::process::ExitStatus,
stderr: Vec<u8>,

fn test_fetch_mod_metadata() {
let path = PathBuf::from(".")
.join("src")
.join("test_data")
.join("kpm_update");

let update_mod = update_kcl_module(path.clone());
// Show more information when the test fails.
println!("{:?}", update_mod);
assert!(!update_mod.is_err());
}

#[test]
fn test_update_kcl_module_failure() {
let manifest_path = PathBuf::from("path/to/manifest");
fn mock_command_new_failing(_command: &str) -> MockCommand {
MockCommand {
output: Err(std::io::Error::new(
std::io::ErrorKind::Other,
"Command failed",
)),
}
}
let result = update_kcl_module(manifest_path);
assert!(result.is_err());
fn test_update_module() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @octonawish-akcodes CI failed and would you please sign the DCO?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

im inspecting that, and i am having trouble signing dco due to conflicts :/

test_fetch_mod_metadata();
println!("test_update_module() passed");
}
Loading