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
5 changes: 4 additions & 1 deletion .cargo/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,8 @@
# crt-static reference: See https://rust-lang.github.io/rfcs/1721-crt-static.html
# dumpbin verification: https://github.com/sensmetry/sysand/pull/91#issuecomment-4081523020
#
# Increase the stack size to 8MiB, since Windows default 1 MiB causes a stack
# overflow on almost any CLI operation. Likely related to
# https://github.com/clap-rs/clap/issues/5134
[target.'cfg(windows)']
rustflags = ["-Ctarget-feature=+crt-static"]
rustflags = ["-Ctarget-feature=+crt-static", "-Clink-args=/STACK:8388608"]
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 6 additions & 9 deletions bindings/java/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -473,18 +473,15 @@ fn handle_build_error(env: &mut JNIEnv<'_>, error: KParBuildError<LocalSrcError>
format!("Workspace read error: {}", 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"
),
);
KParBuildError::PathUsage(_) => {
env.throw_exception(ExceptionKind::SysandException, error.to_string());
}
KParBuildError::WorkspaceMetamodelConflict { .. } => {
env.throw_exception(ExceptionKind::SysandException, error.to_string());
}
KParBuildError::MissingIndexSymbol(_, _) => {
env.throw_exception(ExceptionKind::InvalidValue, error.to_string())
}
}
}

Expand Down Expand Up @@ -530,7 +527,7 @@ pub extern "system" fn Java_com_sensmetry_sysand_Sysand_buildProject<'local>(
&project,
&output_path,
compression,
true,
false,
false,
);
match command_result {
Expand Down
3 changes: 2 additions & 1 deletion bindings/py/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ fn do_build_py(
None => KparCompressionMethod::default(),
};

do_build_kpar(&project, &output_path, compression, true, false)
do_build_kpar(&project, &output_path, compression, false, false)
.map(|_| ())
.map_err(|err| match err {
KParBuildError::ProjectRead(_) => PyRuntimeError::new_err(err.to_string()),
Expand All @@ -244,6 +244,7 @@ fn do_build_py(
KParBuildError::WorkspaceMetamodelConflict { .. } => {
PyValueError::new_err(err.to_string())
}
KParBuildError::MissingIndexSymbol(_, _) => PyValueError::new_err(err.to_string()),
})
}

Expand Down
2 changes: 1 addition & 1 deletion core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ serde_json = { version = "1.0.149", default-features = false, features = ["prese
sysand-macros = { path = "../macros"}
spdx = "0.13.4"
thiserror = { version = "2.0.18", default-features = false }
toml = "1.0.6"
toml = { version = "1.0.6", features = ["fast_hash"] }
typed-path = { version = "0.12.3", default-features = false }
walkdir = "2.5.0"
# unicode-normalization = { version = "0.1.24", default-features = false }
Expand Down
5 changes: 3 additions & 2 deletions core/scripts/run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ PACKAGE_DIR=$(dirname "$SCRIPT_DIR")
cd "$PACKAGE_DIR"

cargo test --features filesystem,networking,alltests $@
cargo test --features js $@
cargo test --features python $@
# Currently these features don't enable any tests
# cargo test --features js $@
# cargo test --features python $@
50 changes: 37 additions & 13 deletions core/src/commands/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
use camino::{Utf8Path, Utf8PathBuf};
use thiserror::Error;

use std::collections::HashSet;

use crate::{
env::utils::{CloneError, ErrorBound},
include::IncludeError,
include::{IncludeError, do_include, extract_symbols},
model::InterchangeProjectValidationError,
project::{
ProjectRead,
Expand Down Expand Up @@ -164,6 +166,8 @@ pub enum KParBuildError<ProjectReadError: ErrorBound> {
project_metamodel: String,
project_path: String,
},
#[error("file `{0}` is missing symbol `{1}` found in index")]
MissingIndexSymbol(Box<str>, String),
}

impl<ProjectReadError: ErrorBound> From<FsIoError> for KParBuildError<ProjectReadError> {
Expand Down Expand Up @@ -243,18 +247,20 @@ pub fn default_kpar_file_name<Pr: ProjectRead>(
))
}

/// `update_index_checksum` controls whether to parse symbols from current
/// file to update index and also update file checksum
pub fn do_build_kpar<P: AsRef<Utf8Path>, Pr: ProjectRead>(
project: &Pr,
path: P,
compression: KparCompressionMethod,
canonicalise: bool,
update_index_checksum: bool,
allow_path_usage: bool,
) -> Result<LocalKParProjectRaw, KParBuildError<Pr::Error>> {
do_build_kpar_inner(
project,
path,
compression,
canonicalise,
update_index_checksum,
allow_path_usage,
None,
)
Expand All @@ -264,12 +270,10 @@ fn do_build_kpar_inner<P: AsRef<Utf8Path>, Pr: ProjectRead>(
project: &Pr,
path: P,
compression: KparCompressionMethod,
canonicalise: bool,
update_index_checksum: bool,
allow_path_usage: bool,
workspace_metamodel: Option<&str>,
) -> Result<LocalKParProjectRaw, KParBuildError<Pr::Error>> {
use crate::project::local_src::LocalSrcProject;

let building = "Building";
let header = crate::style::get_style_config().header;
log::info!("{header}{building:>12}{header:#} kpar `{}`", path.as_ref());
Expand Down Expand Up @@ -334,11 +338,31 @@ fn do_build_kpar_inner<P: AsRef<Utf8Path>, Pr: ProjectRead>(
}
}

if canonicalise {
for path in meta.validate()?.source_paths(true) {
use crate::include::do_include;

do_include(&mut local_project, &path, true, true, None)?;
let meta = meta.validate()?;
// Check whether index symbols are up to date
if update_index_checksum {
for p in meta.source_paths(true) {
do_include(&mut local_project, p, true, true, None)?
}
} else {
for p in meta.source_paths(true) {
let new_symbols = extract_symbols(&mut local_project, &p, None)?;
let new_symbols: HashSet<String> = new_symbols.into_iter().collect();
let old_symbols = meta.file_index_symbols(&p);
if let Some(only_in_old) = old_symbols.difference(&new_symbols).next() {
return Err(KParBuildError::MissingIndexSymbol(
p.as_str().into(),
only_in_old.clone(),
));
}
for only_in_new in new_symbols.difference(&old_symbols) {
// TODO: figure out a way to only print suggestions when running the CLI
log::warn!(
"index is missing symbol `{only_in_new}` found in file `{p}`;\n\
if this is not intentional, include the file again to update its\n\
exported symbols, or pass `--update-meta` to do so for all files"
);
}
}
}

Expand Down Expand Up @@ -395,7 +419,7 @@ pub fn do_build_workspace_kpars<P: AsRef<Utf8Path>>(
workspace: &Workspace,
path: P,
compression: KparCompressionMethod,
canonicalise: bool,
update_index_checksum: bool,
allow_path_usage: bool,
) -> Result<Vec<LocalKParProjectRaw>, KParBuildError<LocalSrcError>> {
let ws_metamodel = workspace.metamodel().map(|iri| iri.as_str());
Expand All @@ -414,7 +438,7 @@ pub fn do_build_workspace_kpars<P: AsRef<Utf8Path>>(
&project,
&output_path,
compression,
canonicalise,
update_index_checksum,
allow_path_usage,
ws_metamodel,
)?;
Expand Down
42 changes: 21 additions & 21 deletions core/src/commands/include.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,29 +52,29 @@ pub fn do_include<Pr: ProjectMut, P: AsRef<Utf8UnixPath>>(
log::info!("{header}{including:>12}{header:#} file `{}`", path.as_ref());

if index_symbols {
match force_format.or_else(|| Language::guess_from_path(&path)) {
Some(Language::SysML) => {
let new_symbols = crate::symbols::top_level_sysml(
project.read_source(&path).map_err(IncludeError::Project)?,
)
.map_err(|e| IncludeError::Extract(path.as_ref().as_str().into(), e))?;

project.replace_index_for_file(new_symbols.into_iter(), &path, true)?;
}
Some(Language::KerML) => {
let new_symbols = crate::symbols::top_level_kerml(
project.read_source(&path).map_err(IncludeError::Project)?,
)
.map_err(|e| IncludeError::Extract(path.as_ref().as_str().into(), e))?;

project.replace_index_for_file(new_symbols.into_iter(), &path, true)?;
}
_ => {
return Err(IncludeError::UnknownFormat(path.as_ref().as_str().into()));
}
}
let new_symbols = extract_symbols(project, &path, force_format)?;
project.replace_index_for_file(new_symbols.into_iter(), &path, true)?;
}

project.include_source(&path, compute_checksum, true)?;
Ok(())
}

/// Extract top level symbols from file at `path` belonging to `project`
pub fn extract_symbols<Pr: ProjectMut, P: AsRef<Utf8UnixPath>>(
project: &mut Pr,
path: &P,
force_format: Option<Language>,
) -> Result<Vec<String>, IncludeError<Pr::Error>> {
match force_format.or_else(|| Language::guess_from_path(path)) {
Some(Language::SysML) => crate::symbols::top_level_sysml(
project.read_source(path).map_err(IncludeError::Project)?,
)
.map_err(|e| IncludeError::Extract(path.as_ref().as_str().into(), e)),
Some(Language::KerML) => crate::symbols::top_level_kerml(
project.read_source(path).map_err(IncludeError::Project)?,
)
.map_err(|e| IncludeError::Extract(path.as_ref().as_str().into(), e)),
_ => Err(IncludeError::UnknownFormat(path.as_ref().as_str().into())),
}
}
16 changes: 16 additions & 0 deletions core/src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,22 @@ pub enum InterchangeProjectValidationError {
},
}

impl InterchangeProjectMetadata {
/// Get symbols recorded in `index` for file at `path`
pub fn file_index_symbols<P: AsRef<str>>(&self, path: P) -> HashSet<String> {
self.index
.iter()
.filter_map(|(k, v)| {
if v == path.as_ref() {
Some(k.clone())
} else {
None
}
})
.collect()
}
}

impl Default for InterchangeProjectMetadataRaw {
fn default() -> Self {
InterchangeProjectMetadataRaw {
Expand Down
12 changes: 8 additions & 4 deletions docs/src/commands/build.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# `sysand build`

Build a KerML Project Archive (KPAR). If executed in a workspace outside of a
project, builds all projects in the workspace.
Build a KerML Project Archive (KPAR). If executed in a workspace
outside of a project, builds all projects in the workspace.

## Usage

Expand All @@ -13,8 +13,8 @@ sysand build [OPTIONS] [PATH]

Creates a KPAR file from the current project.

Current project is determined as in [sysand print-root](root.md) and
if none is found uses the current directory instead.
Current project is determined as in [`sysand print-root`](root.md) and
if none is found, defaults to current directory.

If a `README.md` file exist at the project root, it is included in the
`.kpar` archive.
Expand Down Expand Up @@ -57,4 +57,8 @@ warning; the build still succeeds.
computers, as `file://` URL always contains an absolute path.
For multiple related projects, consider using a workspace instead

- `-u`, `--update-meta`: Update project metadata before building. This
includes updating project symbol index and adding/updating source
file checksums.

{{#include ./partials/global_opts.md}}
2 changes: 1 addition & 1 deletion sysand/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ env_logger = "0.11.9"
log = { version = "0.4.29", default-features = false }
sysand-core = { path = "../core", features = ["std", "filesystem", "networking"] }
thiserror = "2.0.18"
toml = { version = "1.0.6", default-features = false }
toml = { version = "1.0.6", features = ["fast_hash"] }
semver = "1.0.27"
serde_json = { version = "1.0.149", default-features = false }
spdx = "0.13.4"
Expand Down
5 changes: 5 additions & 0 deletions sysand/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,11 @@ pub enum Command {
/// For multiple related projects, consider using a workspace instead
#[arg(long, short, default_value_t = false, verbatim_doc_comment)]
allow_path_usage: bool,
/// Update project metadata to be included in the build artifacts. This
/// includes updating project symbol index and adding/updating source file
/// checksums. Original project(s) will not be affected
#[arg(long, short, default_value_t = false, verbatim_doc_comment)]
update_meta: bool,
},
/// Publish a KPAR to a sysand package index
Publish {
Expand Down
18 changes: 16 additions & 2 deletions sysand/src/commands/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,16 @@ pub fn command_build_for_project<P: AsRef<Utf8Path>>(
path: P,
compression: KparCompressionMethod,
current_project: LocalSrcProject,
update_index_checksum: bool,
allow_path_usage: bool,
) -> Result<()> {
match do_build_kpar(&current_project, &path, compression, true, allow_path_usage) {
match do_build_kpar(
&current_project,
&path,
compression,
update_index_checksum,
allow_path_usage,
) {
Ok(_) => Ok(()),
Err(err) => match err {
KParBuildError::PathUsage(_) => bail!(
Expand All @@ -31,6 +38,7 @@ pub fn command_build_for_workspace<P: AsRef<Utf8Path>>(
path: P,
compression: KparCompressionMethod,
workspace: Workspace,
update_index_checksum: bool,
allow_path_usage: bool,
) -> Result<()> {
log::warn!(
Expand All @@ -39,7 +47,13 @@ pub fn command_build_for_workspace<P: AsRef<Utf8Path>>(
releases. For the status of this feature, see\n\
https://github.com/sensmetry/sysand/issues/101."
);
do_build_workspace_kpars(&workspace, &path, compression, true, allow_path_usage)?;
do_build_workspace_kpars(
&workspace,
&path,
compression,
update_index_checksum,
allow_path_usage,
)?;

Ok(())
}
3 changes: 3 additions & 0 deletions sysand/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,7 @@ pub fn run_cli(args: cli::Args) -> Result<()> {
Command::Build {
path,
compression,
update_meta,
allow_path_usage,
} => {
if let Some(current_project) = ctx.current_project {
Expand All @@ -690,6 +691,7 @@ pub fn run_cli(args: cli::Args) -> Result<()> {
path,
compression.into(),
current_project,
update_meta,
allow_path_usage,
)
} else {
Expand All @@ -708,6 +710,7 @@ pub fn run_cli(args: cli::Args) -> Result<()> {
output_dir,
compression.into(),
current_workspace,
update_meta,
allow_path_usage,
)
}
Expand Down
Loading
Loading