use crate::{Compile, LanguageMethods, Runner, Verify};
use anyhow::Result;
use heck::*;
use std::env;
use std::fs;
use std::path::Path;
use std::process::Command;
pub struct Csharp;
fn dotnet() -> Command {
let dotnet_cmd = match env::var("DOTNET_ROOT") {
Ok(val) => Path::new(&val).join("dotnet"),
Err(_e) => "dotnet".into(),
};
Command::new(dotnet_cmd)
}
impl LanguageMethods for Csharp {
fn display(&self) -> &str {
"csharp"
}
fn comment_prefix_for_test_config(&self) -> Option<&str> {
Some("//@")
}
fn default_bindgen_args(&self) -> &[&str] {
&["--runtime=native-aot"]
}
fn default_bindgen_args_for_codegen(&self) -> &[&str] {
&["--generate-stub"]
}
fn should_fail_verify(
&self,
name: &str,
_config: &crate::config::WitConfig,
_args: &[String],
) -> bool {
matches!(
name,
"error-context.wit"
| "resource-fallible-constructor.wit"
| "async-resource-func.wit"
| "import-export-resource.wit"
| "issue-1433.wit"
| "named-fixed-length-list.wit"
)
}
fn prepare(&self, runner: &mut Runner) -> Result<()> {
runner.run_command(dotnet().arg("--version"))?;
Ok(())
}
fn compile(&self, runner: &Runner, compile: &Compile<'_>) -> Result<()> {
let world_name = &compile.component.bindgen.world;
let path = &compile.component.path;
let test_dir = &compile.bindings_dir;
let new_path = test_dir.join("testcase.cs");
fs::copy(path, &new_path)?;
let camel = format!("{}World", world_name.to_upper_camel_case());
let assembly_name = "csharp-testcase";
let out_wasm = test_dir.join(&assembly_name);
let mut csproj =
wit_bindgen_csharp::CSProject::new(test_dir.to_path_buf(), &assembly_name, world_name);
csproj.aot();
csproj.generate()?;
let mut cmd = dotnet();
let mut wasm_filename = out_wasm.join(assembly_name);
wasm_filename.set_extension("wasm");
cmd.current_dir(test_dir)
.arg("publish")
.arg(test_dir.join(format!("{camel}.csproj")))
.arg("-r")
.arg("wasi-wasm")
.arg("-c")
.arg("Debug")
.arg("/p:PlatformTarget=AnyCPU")
.arg("/p:MSBuildEnableWorkloadResolver=false")
.arg("--self-contained")
.arg("/p:UseAppHost=false")
.arg("-o")
.arg(&out_wasm);
let os = match std::env::consts::OS {
"windows" => "win",
"linux" => std::env::consts::OS,
other => todo!("OS {} not supported", other),
};
if os == "win" && std::env::consts::ARCH == "aarch64" {
cmd.arg("/p:_hostArchitecture=x64");
}
runner.run_command(&mut cmd)?;
fs::copy(&wasm_filename, &compile.output)?;
Ok(())
}
fn verify(&self, runner: &Runner, verify: &Verify<'_>) -> Result<()> {
let dir = verify.bindings_dir;
let name = verify.world;
let mut project = wit_bindgen_csharp::CSProject::new(dir.to_path_buf(), &name, "the_world");
project.aot();
project.clean();
project.generate().unwrap();
let mut cmd = dotnet();
cmd.current_dir(&dir);
let mut wasm_filename = dir.join(name);
wasm_filename.set_extension("wasm");
cmd.arg("build")
.arg(dir.join(format!("TheWorldWorld.csproj")))
.arg("-r")
.arg("wasi-wasm")
.arg("-c")
.arg("Debug")
.arg("/p:PlatformTarget=AnyCPU")
.arg("/p:MSBuildEnableWorkloadResolver=false")
.arg("--self-contained")
.arg("/p:UseAppHost=false")
.arg("-o")
.arg(&wasm_filename);
runner.run_command(&mut cmd)?;
runner.run_command(dotnet().current_dir(&dir).arg("clean"))
}
}