diff --git a/Cargo.lock b/Cargo.lock index 168fffc3df7..4e1bc20ce6e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -882,6 +882,7 @@ dependencies = [ "env_logger 0.10.2", "log", "regex", + "semver", "tempfile", ] diff --git a/sdks/csharp/.meta-check-ignore b/sdks/csharp/.meta-check-ignore new file mode 100644 index 00000000000..44b5aedf8d4 --- /dev/null +++ b/sdks/csharp/.meta-check-ignore @@ -0,0 +1,3 @@ +unity-meta-skeleton.meta +unity-meta-skeleton~ +unity-meta-skeleton~/** diff --git a/sdks/csharp/packages/.gitignore b/sdks/csharp/packages/.gitignore index 7d9dea737b9..6af724b3a55 100644 --- a/sdks/csharp/packages/.gitignore +++ b/sdks/csharp/packages/.gitignore @@ -1,11 +1,15 @@ # Ignore most of NuGet package structure, except DLLs which are required by Unity. +/*/* +!/*/*/ /*/*/* !/*/*/analyzers !/*/*/analyzers.meta +!/*/*/analyzers/** !/*/*/lib !/*/*/lib.meta +!/*/*/lib/** # Ignore XML documentation metadata from packages too. *.xml diff --git a/sdks/csharp/unity-meta-skeleton.meta b/sdks/csharp/unity-meta-skeleton.meta new file mode 100644 index 00000000000..7f5a052ac10 --- /dev/null +++ b/sdks/csharp/unity-meta-skeleton.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 204f4b1e72c6def40970d7871ecc1fad +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdks/csharp/unity-meta-skeleton~/spacetimedb.bsatn.runtime.meta b/sdks/csharp/unity-meta-skeleton~/spacetimedb.bsatn.runtime.meta new file mode 100644 index 00000000000..605fd02b9a5 --- /dev/null +++ b/sdks/csharp/unity-meta-skeleton~/spacetimedb.bsatn.runtime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7fd391d5cf1530247a9097f6572ccbd5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdks/csharp/unity-meta-skeleton~/spacetimedb.bsatn.runtime/analyzers.meta b/sdks/csharp/unity-meta-skeleton~/spacetimedb.bsatn.runtime/analyzers.meta new file mode 100644 index 00000000000..61e48372d2b --- /dev/null +++ b/sdks/csharp/unity-meta-skeleton~/spacetimedb.bsatn.runtime/analyzers.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4d72b75aab0ba43488cf618e31c1c907 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdks/csharp/unity-meta-skeleton~/spacetimedb.bsatn.runtime/analyzers/dotnet.meta b/sdks/csharp/unity-meta-skeleton~/spacetimedb.bsatn.runtime/analyzers/dotnet.meta new file mode 100644 index 00000000000..6c048c99da5 --- /dev/null +++ b/sdks/csharp/unity-meta-skeleton~/spacetimedb.bsatn.runtime/analyzers/dotnet.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dd06752b31474fe4c89bf4269aa940c2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdks/csharp/unity-meta-skeleton~/spacetimedb.bsatn.runtime/analyzers/dotnet/cs.meta b/sdks/csharp/unity-meta-skeleton~/spacetimedb.bsatn.runtime/analyzers/dotnet/cs.meta new file mode 100644 index 00000000000..73491e551c8 --- /dev/null +++ b/sdks/csharp/unity-meta-skeleton~/spacetimedb.bsatn.runtime/analyzers/dotnet/cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3a91a483175b52f49ae96615de55eca1 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdks/csharp/unity-meta-skeleton~/spacetimedb.bsatn.runtime/analyzers/dotnet/cs/SpacetimeDB.BSATN.Codegen.dll.meta b/sdks/csharp/unity-meta-skeleton~/spacetimedb.bsatn.runtime/analyzers/dotnet/cs/SpacetimeDB.BSATN.Codegen.dll.meta new file mode 100644 index 00000000000..6fc3330ca4c --- /dev/null +++ b/sdks/csharp/unity-meta-skeleton~/spacetimedb.bsatn.runtime/analyzers/dotnet/cs/SpacetimeDB.BSATN.Codegen.dll.meta @@ -0,0 +1,35 @@ +fileFormatVersion: 2 +guid: 7bf9b84a9abf4c2fbf4a76b30f4d1e0a +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 0 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: +labels: + - RoslynAnalyzer \ No newline at end of file diff --git a/sdks/csharp/unity-meta-skeleton~/spacetimedb.bsatn.runtime/lib.meta b/sdks/csharp/unity-meta-skeleton~/spacetimedb.bsatn.runtime/lib.meta new file mode 100644 index 00000000000..dd68bea6de9 --- /dev/null +++ b/sdks/csharp/unity-meta-skeleton~/spacetimedb.bsatn.runtime/lib.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 35d39a294253b304b9c696d513e31d5b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdks/csharp/unity-meta-skeleton~/spacetimedb.bsatn.runtime/lib/net8.0.meta b/sdks/csharp/unity-meta-skeleton~/spacetimedb.bsatn.runtime/lib/net8.0.meta new file mode 100644 index 00000000000..533462058a8 --- /dev/null +++ b/sdks/csharp/unity-meta-skeleton~/spacetimedb.bsatn.runtime/lib/net8.0.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 965f7ca77cc65f040ae5cc0eae81fd09 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdks/csharp/unity-meta-skeleton~/spacetimedb.bsatn.runtime/lib/net8.0/SpacetimeDB.BSATN.Runtime.dll.meta b/sdks/csharp/unity-meta-skeleton~/spacetimedb.bsatn.runtime/lib/net8.0/SpacetimeDB.BSATN.Runtime.dll.meta new file mode 100644 index 00000000000..4ef02633fe1 --- /dev/null +++ b/sdks/csharp/unity-meta-skeleton~/spacetimedb.bsatn.runtime/lib/net8.0/SpacetimeDB.BSATN.Runtime.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 92450a91ba82349489665f7d11a31029 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 0 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdks/csharp/unity-meta-skeleton~/spacetimedb.bsatn.runtime/lib/netstandard2.1.meta b/sdks/csharp/unity-meta-skeleton~/spacetimedb.bsatn.runtime/lib/netstandard2.1.meta new file mode 100644 index 00000000000..25fcce10d40 --- /dev/null +++ b/sdks/csharp/unity-meta-skeleton~/spacetimedb.bsatn.runtime/lib/netstandard2.1.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dcac8bfc0f49a004fac5aaad32502c6b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdks/csharp/unity-meta-skeleton~/spacetimedb.runtime.meta b/sdks/csharp/unity-meta-skeleton~/spacetimedb.runtime.meta new file mode 100644 index 00000000000..f990b8e6b43 --- /dev/null +++ b/sdks/csharp/unity-meta-skeleton~/spacetimedb.runtime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ed6383413f54042429fcbaef0423442e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdks/csharp/unity-meta-skeleton~/spacetimedb.runtime/analyzers.meta b/sdks/csharp/unity-meta-skeleton~/spacetimedb.runtime/analyzers.meta new file mode 100644 index 00000000000..119521b6d4d --- /dev/null +++ b/sdks/csharp/unity-meta-skeleton~/spacetimedb.runtime/analyzers.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6a0f09bc7ea54767a8d114ad57dbbb1c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdks/csharp/unity-meta-skeleton~/spacetimedb.runtime/analyzers/dotnet.meta b/sdks/csharp/unity-meta-skeleton~/spacetimedb.runtime/analyzers/dotnet.meta new file mode 100644 index 00000000000..ea82b5c585a --- /dev/null +++ b/sdks/csharp/unity-meta-skeleton~/spacetimedb.runtime/analyzers/dotnet.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3d3d1170b80c4d66b3fa18fbd93803f3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdks/csharp/unity-meta-skeleton~/spacetimedb.runtime/analyzers/dotnet/cs.meta b/sdks/csharp/unity-meta-skeleton~/spacetimedb.runtime/analyzers/dotnet/cs.meta new file mode 100644 index 00000000000..c3331ea0d2a --- /dev/null +++ b/sdks/csharp/unity-meta-skeleton~/spacetimedb.runtime/analyzers/dotnet/cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8db6bdb615d64f9da5c1c5c0ee54eec6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdks/csharp/unity-meta-skeleton~/spacetimedb.runtime/analyzers/dotnet/cs/SpacetimeDB.Codegen.dll.meta b/sdks/csharp/unity-meta-skeleton~/spacetimedb.runtime/analyzers/dotnet/cs/SpacetimeDB.Codegen.dll.meta new file mode 100644 index 00000000000..eb170b45e97 --- /dev/null +++ b/sdks/csharp/unity-meta-skeleton~/spacetimedb.runtime/analyzers/dotnet/cs/SpacetimeDB.Codegen.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: 1e346e9c545943cdb0b4a0e7f3c8dc45 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 0 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: \ No newline at end of file diff --git a/sdks/csharp/unity-meta-skeleton~/spacetimedb.runtime/lib.meta b/sdks/csharp/unity-meta-skeleton~/spacetimedb.runtime/lib.meta new file mode 100644 index 00000000000..79f1585827f --- /dev/null +++ b/sdks/csharp/unity-meta-skeleton~/spacetimedb.runtime/lib.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e7417505b8d7424fb318ccbb8efee09f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/sdks/csharp/unity-meta-skeleton~/spacetimedb.runtime/lib/net8.0.meta b/sdks/csharp/unity-meta-skeleton~/spacetimedb.runtime/lib/net8.0.meta new file mode 100644 index 00000000000..f3c3a590a21 --- /dev/null +++ b/sdks/csharp/unity-meta-skeleton~/spacetimedb.runtime/lib/net8.0.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1cb19fbc4cd9479fb94e83e3b1bcd0ac +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/tools/ci/Cargo.toml b/tools/ci/Cargo.toml index a79a4917ca7..c786d09d8f5 100644 --- a/tools/ci/Cargo.toml +++ b/tools/ci/Cargo.toml @@ -12,3 +12,4 @@ regex.workspace = true duct.workspace = true tempfile.workspace = true env_logger.workspace = true +semver.workspace = true diff --git a/tools/ci/README.md b/tools/ci/README.md index 080dc354f33..3e91f892390 100644 --- a/tools/ci/README.md +++ b/tools/ci/README.md @@ -70,6 +70,21 @@ Usage: wasm-bindings - `--help`: Print help (see a summary with '-h') +### `dlls` + +Builds and packs C# DLLs and NuGet packages for local Unity workflows + +Packs the in-repo C# NuGet packages and restores the C# SDK to populate `sdks/csharp/packages/**`. Then overlays Unity `.meta` skeleton files from `sdks/csharp/unity-meta-skeleton~/**` onto the restored versioned package directory, so Unity can associate stable meta files with the most recently built package. + +**Usage:** +```bash +Usage: dlls +``` + +**Options:** + +- `--help`: Print help (see a summary with '-h') + ### `smoketests` Runs smoketests diff --git a/tools/ci/src/main.rs b/tools/ci/src/main.rs index 036e8993033..d7c95e0cf15 100644 --- a/tools/ci/src/main.rs +++ b/tools/ci/src/main.rs @@ -1,7 +1,10 @@ use anyhow::{bail, Result}; use clap::{CommandFactory, Parser, Subcommand}; use duct::cmd; +use semver::Version; +use std::ffi::OsStr; use std::path::Path; +use std::path::PathBuf; use std::{env, fs}; const README_PATH: &str = "tools/ci/README.md"; @@ -30,6 +33,125 @@ struct Cli { skip: Vec, } +fn ensure_repo_root() -> Result<()> { + if !Path::new("Cargo.toml").exists() { + bail!("You must execute this command from the SpacetimeDB repository root (where Cargo.toml is located)"); + } + Ok(()) +} + +fn overlay_unity_meta_skeleton(pkg_id: &str) -> Result<()> { + let pkg_root = Path::new("sdks/csharp/packages").join(pkg_id); + if !pkg_root.exists() { + log::info!("Skipping skeleton overlay for {pkg_id}: {pkg_root:?} does not exist"); + return Ok(()); + } + + let skeleton_root = Path::new("sdks/csharp/unity-meta-skeleton~").join(pkg_id); + if !skeleton_root.exists() { + log::info!( + "Skipping skeleton overlay for {pkg_id}: {} does not exist", + skeleton_root.display() + ); + return Ok(()); + } + + let versioned_dir = match find_latest_semver_subdir(&pkg_root) { + Ok(dir) => dir, + Err(err) => { + log::info!( + "Skipping skeleton overlay for {pkg_id}: could not find a versioned directory under {} ({err})", + pkg_root.display() + ); + return Ok(()); + } + }; + + copy_overlay_dir(&skeleton_root, &versioned_dir) +} + +fn clear_restored_package_dirs(pkg_id: &str) -> Result<()> { + let pkg_root = Path::new("sdks/csharp/packages").join(pkg_id); + if !pkg_root.exists() { + return Ok(()); + } + + fs::remove_dir_all(&pkg_root)?; + + Ok(()) +} + +fn find_latest_semver_subdir(dir: &Path) -> Result { + let mut versioned_dirs: Vec<(Version, PathBuf)> = vec![]; + + for entry in fs::read_dir(dir)? { + let entry = entry?; + if !entry.file_type()?.is_dir() { + continue; + } + + let name = entry.file_name().to_string_lossy().to_string(); + let Ok(version) = Version::parse(&name) else { + continue; + }; + + versioned_dirs.push((version, entry.path())); + } + + versioned_dirs.sort_by(|(a, _), (b, _)| a.cmp(b)); + + match versioned_dirs.as_slice() { + [] => Err(anyhow::anyhow!( + "Could not find any versioned directories under {}", + dir.display() + )), + [(_, only)] => Ok(only.clone()), + _ => Err(anyhow::anyhow!( + "Expected at most one restored versioned directory under {}, found {}", + dir.display(), + versioned_dirs.len() + )), + } +} + +fn copy_overlay_dir(src: &Path, dst: &Path) -> Result<()> { + if !src.exists() { + bail!("Skeleton directory does not exist: {}", src.display()); + } + if !dst.exists() { + bail!("Destination directory does not exist: {}", dst.display()); + } + + for entry in fs::read_dir(src)? { + let entry = entry?; + let src_path = entry.path(); + let dst_path = dst.join(entry.file_name()); + if entry.file_type()?.is_dir() { + if dst_path.exists() { + copy_overlay_dir(&src_path, &dst_path)?; + } + } else { + if src_path.extension() == Some(OsStr::new("meta")) { + let asset_path = dst_path + .parent() + .expect("dst_path should have a parent") + .join(dst_path.file_stem().expect(".meta file should have a file stem")); + + if asset_path.exists() { + fs::copy(&src_path, &dst_path)?; + } else if dst_path.exists() { + fs::remove_file(&dst_path)?; + } + continue; + } + + fs::copy(&src_path, &dst_path)?; + } + } + + Ok(()) +} + #[derive(Subcommand)] enum CiCmd { /// Runs tests @@ -46,6 +168,12 @@ enum CiCmd { /// /// Runs tests for the codegen crate and builds a test module with the wasm bindings. WasmBindings, + /// Builds and packs C# DLLs and NuGet packages for local Unity workflows + /// + /// Packs the in-repo C# NuGet packages and restores the C# SDK to populate `sdks/csharp/packages/**`. + /// Then overlays Unity `.meta` skeleton files from `sdks/csharp/unity-meta-skeleton~/**` onto the restored + /// versioned package directory, so Unity can associate stable meta files with the most recently built package. + Dlls, /// Runs smoketests /// /// Executes the smoketests suite with some default exclusions. @@ -211,6 +339,62 @@ fn main() -> Result<()> { .run()?; } + Some(CiCmd::Dlls) => { + ensure_repo_root()?; + + cmd!( + "dotnet", + "pack", + "crates/bindings-csharp/BSATN.Runtime", + "-c", + "Release" + ) + .run()?; + cmd!("dotnet", "pack", "crates/bindings-csharp/Runtime", "-c", "Release").run()?; + + let repo_root = env::current_dir()?; + let bsatn_source = repo_root.join("crates/bindings-csharp/BSATN.Runtime/bin/Release"); + let runtime_source = repo_root.join("crates/bindings-csharp/Runtime/bin/Release"); + + let nuget_config_dir = tempfile::tempdir()?; + let nuget_config_path = nuget_config_dir.path().join("nuget.config"); + let nuget_config_contents = format!( + "\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n", + bsatn_source.to_string_lossy(), + runtime_source.to_string_lossy(), + ); + fs::write(&nuget_config_path, nuget_config_contents)?; + + let nuget_config_path_str = nuget_config_path.to_string_lossy().to_string(); + + clear_restored_package_dirs("spacetimedb.bsatn.runtime")?; + clear_restored_package_dirs("spacetimedb.runtime")?; + + cmd!( + "dotnet", + "restore", + "SpacetimeDB.ClientSDK.csproj", + "--configfile", + &nuget_config_path_str, + ) + .dir("sdks/csharp") + .run()?; + + overlay_unity_meta_skeleton("spacetimedb.bsatn.runtime")?; + overlay_unity_meta_skeleton("spacetimedb.runtime")?; + + cmd!( + "dotnet", + "pack", + "SpacetimeDB.ClientSDK.csproj", + "-c", + "Release", + "--no-restore" + ) + .dir("sdks/csharp") + .run()?; + } + Some(CiCmd::Smoketests { args: smoketest_args }) => { let python = infer_python(); cmd(