wit_bindgen_csharp/
csproj.rs1use anyhow::Result;
2use std::{fs, path::PathBuf};
3
4use heck::ToUpperCamelCase;
5
6pub struct CSProject;
7
8pub struct CSProjectLLVMBuilder {
9 name: String,
10 dir: PathBuf,
11 aot: bool,
12 clean_targets: bool,
13 world_name: String,
14 binary: bool,
15}
16
17pub struct CSProjectMonoBuilder {
18 name: String,
19 dir: PathBuf,
20 aot: bool,
21 clean_targets: bool,
22 world_name: String,
23}
24
25impl CSProject {
26 pub fn new(dir: PathBuf, name: &str, world_name: &str) -> CSProjectLLVMBuilder {
27 CSProjectLLVMBuilder {
28 name: name.to_string(),
29 dir,
30 aot: false,
31 clean_targets: false,
32 world_name: world_name.to_string(),
33 binary: false,
34 }
35 }
36
37 pub fn new_mono(dir: PathBuf, name: &str, world_name: &str) -> CSProjectMonoBuilder {
38 CSProjectMonoBuilder {
39 name: name.to_string(),
40 dir,
41 aot: false,
42 clean_targets: false,
43 world_name: world_name.to_string(),
44 }
45 }
46}
47
48impl CSProjectLLVMBuilder {
49 pub fn generate(&self) -> Result<()> {
50 let name = &self.name;
51 let world = &self.world_name.replace("-", "_");
52 let camel = format!("{}World", world.to_upper_camel_case());
53
54 fs::write(
55 self.dir.join("rd.xml"),
56 format!(
57 r#"<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
58 <Application>
59 <Assembly Name="{name}">
60 </Assembly>
61 </Application>
62 </Directives>"#
63 ),
64 )?;
65
66 let output_type = if self.binary {
67 "<OutputType>Exe</OutputType>"
68 } else {
69 "<OutputType>Library</OutputType>"
70 };
71
72 let mut csproj = format!(
73 "<Project Sdk=\"Microsoft.NET.Sdk\">
74
75 <PropertyGroup>
76 <TargetFramework>net10.0</TargetFramework>
77 <LangVersion>preview</LangVersion>
78 <RootNamespace>{name}</RootNamespace>
79 <ImplicitUsings>enable</ImplicitUsings>
80 <Nullable>enable</Nullable>
81 <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
82 <!-- treat these are errors so they are caught during code generation tests -->
83 <WarningsAsErrors>CS0105</WarningsAsErrors>
84 {output_type}
85 </PropertyGroup>
86
87 <PropertyGroup>
88 <PublishTrimmed>true</PublishTrimmed>
89 <AssemblyName>{name}</AssemblyName>
90 </PropertyGroup>
91
92 <ItemGroup>
93 <RdXmlFile Include=\"rd.xml\" />
94 </ItemGroup>
95
96 <ItemGroup>
97 <CustomLinkerArg Include=\"-Wl,--component-type,{camel}_component_type.wit\" />
98 </ItemGroup>
99 "
100 );
101
102 if self.aot {
103 let os = match std::env::consts::OS {
104 "windows" => "win",
105 "linux" => std::env::consts::OS,
106 other => todo!("OS {} not supported", other),
107 };
108
109 let packages_path = if self.dir.to_str().unwrap().contains("codegen") {
111 "../../../../.packages"
112 } else {
113 "../../../.packages"
114 };
115 csproj.push_str(
116 &format!(
117 r#"
118 <ItemGroup>
119 <PackageReference Include="Microsoft.DotNet.ILCompiler.LLVM" Version="10.0.0-*" />
120 <PackageReference Include="runtime.{os}-x64.Microsoft.DotNet.ILCompiler.LLVM" Version="10.0.0-*" />
121 </ItemGroup>"#),
122 );
123
124 fs::write(
125 self.dir.join("nuget.config"),
126 format!(
127 r#"<?xml version="1.0" encoding="utf-8"?>
128 <configuration>
129 <config>
130 <!-- Store the packages where they can be shared between tests -->
131 <add key="globalPackagesFolder" value="{packages_path}" />
132 </config>
133 <packageSources>
134 <!--To inherit the global NuGet package sources remove the <clear/> line below -->
135 <clear />
136 <add key="nuget" value="https://api.nuget.org/v3/index.json" />
137 <add key="dotnet-experimental" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json" />
138 <!--<add key="dotnet-experimental" value="C:\github\runtimelab\artifacts\packages\Debug\Shipping" />-->
139 </packageSources>
140 </configuration>"#
141 ),
142 )?;
143 }
144
145 if self.clean_targets {
146 let mut wasm_filename = self.dir.join(name);
147 wasm_filename.set_extension("wasm");
148 csproj.push_str(&format!(
150 "<Target Name=\"CleanAndDelete\" AfterTargets=\"Clean\">
151 <!-- Remove obj folder -->
152 <RemoveDir Directories=\"$(BaseIntermediateOutputPath)\" />
153 <!-- Remove bin folder -->
154 <RemoveDir Directories=\"$(BaseOutputPath)\" />
155 <RemoveDir Directories=\"{}\" />
156 <RemoveDir Directories=\".packages\" />
157 </Target>",
158 wasm_filename.display()
159 ));
160 }
161
162 csproj.push_str(
163 r#"
164</Project>
165"#,
166 );
167
168 fs::write(self.dir.join(format!("{camel}.csproj")), csproj)?;
169
170 Ok(())
171 }
172
173 pub fn aot(&mut self) {
174 self.aot = true;
175 }
176
177 pub fn binary(&mut self) {
178 self.binary = true;
179 }
180
181 pub fn clean(&mut self) -> &mut Self {
182 self.clean_targets = true;
183
184 self
185 }
186}
187
188impl CSProjectMonoBuilder {
189 pub fn generate(&self) -> Result<()> {
190 let name = &self.name;
191 let world = &self.world_name.replace("-", "_");
192 let camel = format!("{}World", world.to_upper_camel_case());
193
194 let aot = self.aot;
195
196 let maybe_aot = match aot {
197 true => format!("<WasmBuildNative>{aot}</WasmBuildNative>"),
198 false => String::new(),
199 };
200
201 let mut csproj = format!(
202 "<Project Sdk=\"Microsoft.NET.Sdk\">
203
204 <PropertyGroup>
205 <TargetFramework>net10.0</TargetFramework>
206 <RuntimeIdentifier>wasi-wasm</RuntimeIdentifier>
207 <OutputType>Library</OutputType>
208 {maybe_aot}
209 <RunAOTCompilation>{aot}</RunAOTCompilation>
210 <WasmNativeStrip>false</WasmNativeStrip>
211 <WasmSingleFileBundle>true</WasmSingleFileBundle>
212 <RootNamespace>{name}</RootNamespace>
213 <ImplicitUsings>enable</ImplicitUsings>
214 <Nullable>enable</Nullable>
215 <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
216 <!-- treat these are errors so they are caught during code generation tests -->
217 <WarningsAsErrors>CS0105</WarningsAsErrors>
218 </PropertyGroup>
219
220 <PropertyGroup>
221 <PublishTrimmed>true</PublishTrimmed>
222 <AssemblyName>{name}</AssemblyName>
223 </PropertyGroup>
224
225 <ItemGroup>
226 <NativeFileReference Include=\"{camel}_component_type.o\" Condition=\"Exists('{camel}_component_type.o')\"/>
227 </ItemGroup>
228
229 "
230 );
231
232 fs::write(
233 self.dir.join("nuget.config"),
234 r#"<?xml version="1.0" encoding="utf-8"?>
235 <configuration>
236 <config>
237 <add key="globalPackagesFolder" value=".packages" />
238 </config>
239 <packageSources>
240 <!--To inherit the global NuGet package sources remove the <clear/> line below -->
241 <clear />
242 <add key="nuget" value="https://api.nuget.org/v3/index.json" />
243 <add key="dotnet9" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet9/nuget/v3/index.json" />
244 </packageSources>
245 </configuration>"#,
246 )?;
247
248 if self.clean_targets {
249 let mut wasm_filename = self.dir.join(name);
250 wasm_filename.set_extension("wasm");
251 csproj.push_str(&format!(
253 "<Target Name=\"CleanAndDelete\" AfterTargets=\"Clean\">
254 <!-- Remove obj folder -->
255 <RemoveDir Directories=\"$(BaseIntermediateOutputPath)\" />
256 <!-- Remove bin folder -->
257 <RemoveDir Directories=\"$(BaseOutputPath)\" />
258 <RemoveDir Directories=\"{}\" />
259 <RemoveDir Directories=\".packages\" />
260 </Target>",
261 wasm_filename.display()
262 ));
263 }
264
265 csproj.push_str(
266 r#"</Project>
267 "#,
268 );
269
270 fs::write(self.dir.join(format!("{camel}.csproj")), csproj)?;
271
272 Ok(())
273 }
274
275 pub fn aot(&mut self) {
276 self.aot = true;
277 }
278
279 pub fn clean(&mut self) -> &mut Self {
280 self.clean_targets = true;
281
282 self
283 }
284}