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
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ default-members = ["sysand", "core"]
[workspace.package]
version = "0.0.9"
edition = "2024"
rust-version = "1.85"
rust-version = "1.94"
publish = false
authors = ["Sensmetry <opensource@sensmetry.com>"]
license = "MIT OR Apache-2.0"
Expand Down
17 changes: 9 additions & 8 deletions bindings/java/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,25 +21,26 @@ There are currently multiple ways to wrap a Rust library for Java:
3. Since Java 22, there is a foreign function and memory API, which allows to
call Rust functions from Java without using JNI (see [Project
Panama](https://openjdk.org/projects/panama/)). Unfortunately, this approach
is not available on Java 21, which is used in the Pilot implementation.
is not available on Java 21, which is used in the [Pilot][pilot] implementation.

We have decided to use the first approach because it should be the easiest to
integrate for our end-users. We may want to migrate to the foreign function and
memory API once Pilot updates to Java 22 or newer.
memory API once [Pilot][pilot] updates to Java 22 or newer.

Note: From JDK 22, Java throws a warning when loading a native Java module. This
warning will become an error in JDK 24 and will require the user to explicitly
allow native modules as described in [JEP
472](https://openjdk.org/jeps/472#Description). Currently, the warning looks as
follows:
Note: From JDK 22, Java throws a warning when loading a native Java module, and
it will become an error in the future. To fix this, user has to explicitly allow
native modules as described in [JEP 472](https://openjdk.org/jeps/472#Description).
Currently, the warning looks as follows:

```text
WARNING: A restricted method in java.lang.System has been called
WARNING: java.lang.System::load has been called by com.sensmetry.sysand.NativeLoader in an unnamed module (file:.../sysand-0.0.4-SNAPSHOT.jar)
WARNING: java.lang.System::load has been called by com.sensmetry.sysand.NativeLoader in an unnamed module (file:.../sysand-X.Y.Z-SNAPSHOT.jar)
WARNING: Use --enable-native-access=ALL-UNNAMED to avoid a warning for callers in this module
WARNING: Restricted methods will be blocked in a future release unless native access is enabled
```

[pilot]: https://github.com/Systems-Modeling/SysML-v2-Pilot-Implementation

## Building and testing

Requirements:
Expand Down
26 changes: 22 additions & 4 deletions bindings/java/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ pub extern "system" fn Java_com_sensmetry_sysand_Sysand_init<'local>(
LocalSrcError::Serialize(subsuberror) => {
env.throw_exception(ExceptionKind::SerializationError, subsuberror.to_string())
}
LocalSrcError::ImpossibleRelativePath(_) => {
env.throw_exception(ExceptionKind::PathError, suberror.to_string())
}
},
},
}
Expand Down Expand Up @@ -147,6 +150,9 @@ pub extern "system" fn Java_com_sensmetry_sysand_Sysand_env<'local>(
LocalWriteError::LocalRead(subsuberror) => {
env.throw_exception(ExceptionKind::IOError, subsuberror.to_string())
}
LocalWriteError::ImpossibleRelativePath(_) => {
env.throw_exception(ExceptionKind::PathError, suberror.to_string())
}
},
},
}
Expand Down Expand Up @@ -315,8 +321,14 @@ fn handle_build_error(env: &mut JNIEnv<'_>, error: KParBuildError<LocalSrcError>
format!("Workspace read error: {}", error),
);
}
KParBuildError::InternalError(error) => {
env.throw_exception(ExceptionKind::SysandException, error);
KParBuildError::PathUsage(usage) => {
env.throw_exception(
ExceptionKind::SysandException,
format!(
"project includes a path usage `{usage}`,\n\
which is unlikely to be available on other computers at the same path"
),
);
}
}
}
Expand Down Expand Up @@ -358,8 +370,13 @@ pub extern "system" fn Java_com_sensmetry_sysand_Sysand_buildProject<'local>(
let Some(compression) = compression_from_java_string(&mut env, compression) else {
return;
};
let command_result =
sysand_core::commands::build::do_build_kpar(&project, &output_path, compression, true);
let command_result = sysand_core::commands::build::do_build_kpar(
&project,
&output_path,
compression,
true,
false,
);
match command_result {
Ok(_) => {}
Err(error) => handle_build_error(&mut env, error),
Expand Down Expand Up @@ -406,6 +423,7 @@ pub extern "system" fn Java_com_sensmetry_sysand_Sysand_buildWorkspace<'local>(
&output_path,
compression,
true,
false,
);
match command_result {
Ok(_) => {}
Expand Down
9 changes: 5 additions & 4 deletions bindings/js/src/io/local_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
// SPDX-License-Identifier: MIT OR Apache-2.0

use std::io::{Cursor, Read};
use sysand_core::context::ProjectContext;

use sysand_core::lock::Source;
use sysand_core::{
lock,
model::{InterchangeProjectInfoRaw, InterchangeProjectMetadataRaw},
project::{ProjectMut, ProjectRead, utils::FsIoError},
};
Expand Down Expand Up @@ -126,10 +127,10 @@ impl ProjectRead for ProjectLocalBrowserStorage {
))
}

fn sources(&self) -> Vec<lock::Source> {
vec![sysand_core::lock::Source::LocalSrc {
fn sources(&self, _ctx: &ProjectContext) -> Result<Vec<Source>, Self::Error> {
Ok(vec![sysand_core::lock::Source::LocalSrc {
src_path: self.root_path.as_str().into(),
}]
}])
}
}

Expand Down
15 changes: 10 additions & 5 deletions bindings/py/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ fn do_new_py_local_file(
LocalSrcError::Io(error) => PyIOError::new_err(error.to_string()),
LocalSrcError::Path(error) => PyIOError::new_err(error.to_string()),
LocalSrcError::Serialize(error) => PyValueError::new_err(error.to_string()),
LocalSrcError::ImpossibleRelativePath(error) => {
PyValueError::new_err(error.to_string())
}
},
},
)?;
Expand All @@ -98,6 +101,9 @@ fn do_env_py_local_dir(path: String) -> PyResult<()> {
LocalWriteError::Serialize(error) => PyValueError::new_err(error.to_string()),
LocalWriteError::TryMove(error) => PyIOError::new_err(error.to_string()),
LocalWriteError::LocalRead(error) => PyIOError::new_err(error.to_string()),
LocalWriteError::ImpossibleRelativePath(error) => {
PyValueError::new_err(error.to_string())
}
},
})?;

Expand Down Expand Up @@ -202,7 +208,7 @@ fn do_build_py(
None => KparCompressionMethod::default(),
};

do_build_kpar(&project, &output_path, compression, true)
do_build_kpar(&project, &output_path, compression, true, false)
.map(|_| ())
.map_err(|err| match err {
KParBuildError::ProjectRead(_) => PyRuntimeError::new_err(err.to_string()),
Expand All @@ -217,7 +223,7 @@ fn do_build_py(
KParBuildError::Zip(_) => PyIOError::new_err(err.to_string()),
KParBuildError::Serialize(..) => PyValueError::new_err(err.to_string()),
KParBuildError::WorkspaceRead(_) => PyRuntimeError::new_err(err.to_string()),
KParBuildError::InternalError(_) => PyRuntimeError::new_err(err.to_string()),
KParBuildError::PathUsage(_) => PyValueError::new_err(err.to_string()),
})
}

Expand Down Expand Up @@ -274,10 +280,9 @@ pub fn do_sources_env_py(
.version()
.map_err(|e| PyRuntimeError::new_err(e.to_string()))?
.and_then(|x| Version::parse(&x).ok())
&& vr.matches(&v)
{
if vr.matches(&v) {
break Some(candidate);
}
break Some(candidate);
}
} else {
break None;
Expand Down
37 changes: 34 additions & 3 deletions core/src/commands/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,11 @@ pub enum KParBuildError<ProjectReadError: ErrorBound> {
Zip(#[from] ZipArchiveError),
#[error("project serialization error: {0}: {1}")]
Serialize(&'static str, serde_json::Error),
#[error("internal error: {0}")]
InternalError(&'static str),
#[error(
"project includes a path usage `{0}`,\n\
which is unlikely to be available on other computers at the same path"
)]
PathUsage(String),
}

impl<ProjectReadError: ErrorBound> From<FsIoError> for KParBuildError<ProjectReadError> {
Expand Down Expand Up @@ -218,6 +221,7 @@ pub fn do_build_kpar<P: AsRef<Utf8Path>, Pr: ProjectRead>(
path: P,
compression: KparCompressionMethod,
canonicalise: bool,
allow_path_usage: bool,
) -> Result<LocalKParProject, KParBuildError<Pr::Error>> {
use crate::project::local_src::LocalSrcProject;

Expand All @@ -242,6 +246,26 @@ pub fn do_build_kpar<P: AsRef<Utf8Path>, Pr: ProjectRead>(
}
}

if let Some(u) = info.usage.iter().find(|x| {
// Case-insensitively match `file:` scheme
x.resource.len() >= 5
&& x.resource
.as_bytes()
.iter()
.zip(b"file:")
.all(|(c1, &c2)| c1.to_ascii_lowercase() == c2)
}) {
if allow_path_usage {
log::warn!(
"project includes a path usage `{}`,\n\
which is unlikely to be available on other computers at the same path",
u.resource
);
} else {
return Err(KParBuildError::PathUsage(u.resource.clone()));
}
}

if canonicalise {
for path in meta.validate()?.source_paths(true) {
use crate::include::do_include;
Expand All @@ -262,6 +286,7 @@ pub fn do_build_workspace_kpars<P: AsRef<Utf8Path>>(
path: P,
compression: KparCompressionMethod,
canonicalise: bool,
allow_path_usage: bool,
) -> Result<Vec<LocalKParProject>, KParBuildError<LocalSrcError>> {
let mut result = Vec::new();
for project in workspace.projects() {
Expand All @@ -271,7 +296,13 @@ pub fn do_build_workspace_kpars<P: AsRef<Utf8Path>>(
};
let file_name = default_kpar_file_name(&project)?;
let output_path = path.as_ref().join(file_name);
let kpar_project = do_build_kpar(&project, &output_path, compression, canonicalise)?;
let kpar_project = do_build_kpar(
&project,
&output_path,
compression,
canonicalise,
allow_path_usage,
)?;
result.push(kpar_project);
}
Ok(result)
Expand Down
34 changes: 17 additions & 17 deletions core/src/commands/env/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,23 +43,23 @@ fn check_install<S: AsRef<str>, P: ProjectRead, E: ReadEnvironment>(
return Ok(());
}

if let Some(version) = storage.version().map_err(CheckInstallError::ProjectRead)? {
if env.has(&uri).map_err(CheckInstallError::EnvRead)? {
let version_present = env
.has_version(&uri, &version)
.map_err(CheckInstallError::EnvRead)?;

if !allow_overwrite && version_present {
return Err(CheckInstallError::AlreadyInstalledVersion(
uri.as_ref().into(),
version,
));
}
if !allow_multiple && !version_present {
return Err(CheckInstallError::AlreadyInstalledUnknownVersion(
uri.as_ref().into(),
));
}
if let Some(version) = storage.version().map_err(CheckInstallError::ProjectRead)?
&& env.has(&uri).map_err(CheckInstallError::EnvRead)?
{
let version_present = env
.has_version(&uri, &version)
.map_err(CheckInstallError::EnvRead)?;

if !allow_overwrite && version_present {
return Err(CheckInstallError::AlreadyInstalledVersion(
uri.as_ref().into(),
version,
));
}
if !allow_multiple && !version_present {
return Err(CheckInstallError::AlreadyInstalledUnknownVersion(
uri.as_ref().into(),
));
}
}

Expand Down
Loading
Loading