-
Notifications
You must be signed in to change notification settings - Fork 1k
Expand file tree
/
Copy pathcsharp.rs
More file actions
135 lines (126 loc) · 6.17 KB
/
Copy pathcsharp.rs
File metadata and controls
135 lines (126 loc) · 6.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
use anyhow::Context;
use itertools::Itertools;
use std::ffi::OsString;
use std::fs;
use std::path::{Path, PathBuf};
fn parse_major_version(version: &str) -> Option<u8> {
version.split('.').next()?.parse::<u8>().ok()
}
pub(crate) fn build_csharp(project_path: &Path, build_debug: bool) -> anyhow::Result<PathBuf> {
// All `dotnet` commands must execute in the project directory, otherwise
// global.json won't have any effect and wrong .NET SDK might be picked.
macro_rules! dotnet {
($($arg:expr),*) => {
duct::cmd!("dotnet", $($arg),*).dir(project_path)
};
}
// Check if the `wasi-experimental` workload is installed. Unfortunately, we
// have to do this by inspecting the human-readable output. There is a
// hidden `--machine-readable` flag but it also mixes in human-readable
// output as well as unnecessarily updates various unrelated manifests.
match dotnet!("workload", "list").read() {
Ok(workloads) if workloads.contains("wasi-experimental") => {}
Ok(_) => {
// If wasi-experimental is not found, first check if we're running
// on .NET SDK 8.0. We can't even install that workload on older
// versions, and we don't support .NET 9.0 yet, so this helps to
// provide a nicer message than "Workload ID wasi-experimental is not recognized.".
let version = dotnet!("--version").read().unwrap_or_default();
if parse_major_version(&version) != Some(8) {
anyhow::bail!(concat!(
".NET SDK 8.0 is required, but found {version}.\n",
"If you have multiple versions of .NET SDK installed, configure your project using https://learn.microsoft.com/en-us/dotnet/core/tools/global-json."
));
}
// Finally, try to install the workload ourselves. On some systems
// this might require elevated privileges, so print a nice error
// message if it fails.
dotnet!(
"workload",
"install",
"wasi-experimental",
"--skip-manifest-update"
)
.stderr_capture()
.run()
.context(concat!(
"Couldn't install the required wasi-experimental workload.\n",
"You might need to install it manually by running `dotnet workload install wasi-experimental` with privileged rights."
))?;
}
Err(error) if error.kind() == std::io::ErrorKind::NotFound => {
anyhow::bail!("dotnet not found in PATH. Please install .NET SDK 8.0.")
}
Err(error) => anyhow::bail!("{error}"),
};
let config_name = if build_debug { "Debug" } else { "Release" };
// Ensure the project path exists.
fs::metadata(project_path).with_context(|| {
format!(
"The provided project path '{}' does not exist.",
project_path.to_str().unwrap()
)
})?;
// run dotnet publish using cmd macro
dotnet!("publish", "-c", config_name, "-v", "quiet").run()?;
// check if file exists
let subdir = if std::env::var_os("EXPERIMENTAL_WASM_AOT").is_some_and(|v| v == "1") {
"publish"
} else {
"AppBundle"
};
// TODO: This code looks for build outputs in both `bin` and `bin~` as output directories. @bfops feels like we shouldn't have to look for `bin~`, since the `~` suffix is just intended to cause Unity to ignore directories, and that shouldn't be relevant here. We do think we've seen `bin~` appear though, and it's not harmful to do the extra checks, so we're merging for now due to imminent code freeze. At some point, it would be good to figure out if we do actually see `bin~` in module directories, and where that's coming from (which could suggest a bug).
// check for the old .NET 7 path for projects that haven't migrated yet
let bad_output_paths = [
project_path.join(format!("bin/{config_name}/net7.0/StdbModule.wasm")),
// for some reason there is sometimes a tilde here?
project_path.join(format!("bin~/{config_name}/net7.0/StdbModule.wasm")),
];
if bad_output_paths.iter().any(|p| p.exists()) {
anyhow::bail!(concat!(
"Looks like your project is using the deprecated .NET 7.0 WebAssembly bindings.\n",
"Please migrate your project to the new .NET 8.0 template and delete the folders: bin, bin~, obj, obj~"
));
}
let possible_output_paths = [
project_path.join(format!("bin/{config_name}/net8.0/wasi-wasm/{subdir}/StdbModule.wasm")),
project_path.join(format!("bin~/{config_name}/net8.0/wasi-wasm/{subdir}/StdbModule.wasm")),
];
if possible_output_paths.iter().all(|p| p.exists()) {
anyhow::bail!(concat!(
"For some reason, your project has both a `bin` and a `bin~` folder.\n",
"I don't know which to use, so please delete both and rerun this command so that we can see which is up-to-date."
));
}
for output_path in possible_output_paths {
if output_path.exists() {
return Ok(output_path);
}
}
anyhow::bail!("Built project successfully but couldn't find the output file.");
}
pub(crate) fn dotnet_format(project_dir: &PathBuf, files: impl IntoIterator<Item = PathBuf>) -> anyhow::Result<()> {
duct::cmd(
"dotnet",
itertools::chain(
[
"format",
// We can't guarantee that the output lives inside a valid project or solution,
// so to avoid crash we need to use the `dotnet whitespace --folder` mode instead
// of a full style-aware formatter. Still better than nothing though.
"whitespace",
"--folder",
// Our files are marked with <auto-generated /> and will be skipped without this option.
"--include-generated",
"--include",
]
.into_iter()
.map_into::<OsString>(),
files.into_iter().map_into(),
),
)
// This is important because we're running with `--folder`. We want to format the right folder!
.dir(project_dir)
.run()?;
Ok(())
}