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 csproj.push_str(
95 r#"
96 <ItemGroup>
97 <PackageReference Include="Microsoft.DotNet.ILCompiler.LLVM" Version="10.0.0-*" />
98 <PackageReference Include="runtime.win-x64.Microsoft.DotNet.ILCompiler.LLVM" Version="10.0.0-*" />
99 </ItemGroup>
100 "#,
101 );
102
103 fs::write(
104 self.dir.join("nuget.config"),
105 r#"<?xml version="1.0" encoding="utf-8"?>
106 <configuration>
107 <config>
108 <add key="globalPackagesFolder" value=".packages" />
109 </config>
110 <packageSources>
111 <!--To inherit the global NuGet package sources remove the <clear/> line below -->
112 <clear />
113 <add key="nuget" value="https://api.nuget.org/v3/index.json" />
114 <add key="dotnet-experimental" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json" />
115 <!--<add key="dotnet-experimental" value="C:\github\runtimelab\artifacts\packages\Debug\Shipping" />-->
116 </packageSources>
117 </configuration>"#,
118 )?;
119 }
120
121 if self.clean_targets {
122 let mut wasm_filename = self.dir.join(name);
123 wasm_filename.set_extension("wasm");
124 csproj.push_str(&format!(
126 "<Target Name=\"CleanAndDelete\" AfterTargets=\"Clean\">
127 <!-- Remove obj folder -->
128 <RemoveDir Directories=\"$(BaseIntermediateOutputPath)\" />
129 <!-- Remove bin folder -->
130 <RemoveDir Directories=\"$(BaseOutputPath)\" />
131 <RemoveDir Directories=\"{}\" />
132 <RemoveDir Directories=\".packages\" />
133 </Target>",
134 wasm_filename.display()
135 ));
136 }
137
138 csproj.push_str(
139 r#"</Project>
140 "#,
141 );
142
143 fs::write(self.dir.join(format!("{camel}.csproj")), csproj)?;
144
145 Ok(())
146 }
147
148 pub fn aot(&mut self) {
149 self.aot = true;
150 }
151
152 pub fn clean(&mut self) -> &mut Self {
153 self.clean_targets = true;
154
155 self
156 }
157}
158
159impl CSProjectMonoBuilder {
160 pub fn generate(&self) -> Result<()> {
161 let name = &self.name;
162 let world = &self.world_name.replace("-", "_");
163 let camel = format!("{}World", world.to_upper_camel_case());
164
165 let aot = self.aot;
166
167 let maybe_aot = match aot {
168 true => format!("<WasmBuildNative>{aot}</WasmBuildNative>"),
169 false => String::new(),
170 };
171
172 let mut csproj = format!(
173 "<Project Sdk=\"Microsoft.NET.Sdk\">
174
175 <PropertyGroup>
176 <TargetFramework>net9.0</TargetFramework>
177 <RuntimeIdentifier>wasi-wasm</RuntimeIdentifier>
178 <OutputType>Library</OutputType>
179 {maybe_aot}
180 <RunAOTCompilation>{aot}</RunAOTCompilation>
181 <WasmNativeStrip>false</WasmNativeStrip>
182 <WasmSingleFileBundle>true</WasmSingleFileBundle>
183 <RootNamespace>{name}</RootNamespace>
184 <ImplicitUsings>enable</ImplicitUsings>
185 <Nullable>enable</Nullable>
186 <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
187 <!-- treat these are errors so they are caught during code generation tests -->
188 <WarningsAsErrors>CS0105</WarningsAsErrors>
189 </PropertyGroup>
190
191 <PropertyGroup>
192 <PublishTrimmed>true</PublishTrimmed>
193 <AssemblyName>{name}</AssemblyName>
194 </PropertyGroup>
195
196 <ItemGroup>
197 <NativeFileReference Include=\"{camel}_component_type.o\" Condition=\"Exists('{camel}_component_type.o')\"/>
198 </ItemGroup>
199
200 "
201 );
202
203 fs::write(
204 self.dir.join("nuget.config"),
205 r#"<?xml version="1.0" encoding="utf-8"?>
206 <configuration>
207 <config>
208 <add key="globalPackagesFolder" value=".packages" />
209 </config>
210 <packageSources>
211 <!--To inherit the global NuGet package sources remove the <clear/> line below -->
212 <clear />
213 <add key="nuget" value="https://api.nuget.org/v3/index.json" />
214 <add key="dotnet9" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet9/nuget/v3/index.json" />
215 </packageSources>
216 </configuration>"#,
217 )?;
218
219 if self.clean_targets {
220 let mut wasm_filename = self.dir.join(name);
221 wasm_filename.set_extension("wasm");
222 csproj.push_str(&format!(
224 "<Target Name=\"CleanAndDelete\" AfterTargets=\"Clean\">
225 <!-- Remove obj folder -->
226 <RemoveDir Directories=\"$(BaseIntermediateOutputPath)\" />
227 <!-- Remove bin folder -->
228 <RemoveDir Directories=\"$(BaseOutputPath)\" />
229 <RemoveDir Directories=\"{}\" />
230 <RemoveDir Directories=\".packages\" />
231 </Target>",
232 wasm_filename.display()
233 ));
234 }
235
236 csproj.push_str(
237 r#"</Project>
238 "#,
239 );
240
241 fs::write(self.dir.join(format!("{camel}.csproj")), csproj)?;
242
243 Ok(())
244 }
245
246 pub fn aot(&mut self) {
247 self.aot = true;
248 }
249
250 pub fn clean(&mut self) -> &mut Self {
251 self.clean_targets = true;
252
253 self
254 }
255}