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