Skip to content

Commit 9e4ef23

Browse files
authored
refactor(c#): Consolidate cs project generation code (#762)
Signed-off-by: James Sturtevant <[email protected]>
1 parent d40ab04 commit 9e4ef23

File tree

4 files changed

+177
-208
lines changed

4 files changed

+177
-208
lines changed

crates/csharp/src/csproj.rs

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
use anyhow::Result;
2+
use std::{fs, path::PathBuf};
3+
4+
use heck::ToUpperCamelCase;
5+
6+
pub struct CSProject {
7+
name: String,
8+
dir: PathBuf,
9+
aot: bool,
10+
clean_targets: bool,
11+
world_name: String,
12+
wasm_imports: Vec<(String, String)>,
13+
}
14+
15+
impl CSProject {
16+
pub fn new(dir: PathBuf, name: &str, world_name: &str) -> CSProject {
17+
CSProject {
18+
name: name.to_string(),
19+
dir,
20+
aot: false,
21+
clean_targets: false,
22+
world_name: world_name.to_string(),
23+
wasm_imports: Vec::new(),
24+
}
25+
}
26+
27+
pub fn generate(&self) -> Result<()> {
28+
let name = &self.name;
29+
let world = &self.world_name.replace("-", "_");
30+
let snake_world = world.to_upper_camel_case();
31+
32+
fs::write(
33+
self.dir.join("rd.xml"),
34+
format!(
35+
r#"<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
36+
<Application>
37+
<Assembly Name="{name}">
38+
</Assembly>
39+
</Application>
40+
</Directives>"#
41+
),
42+
)?;
43+
44+
let mut csproj = format!(
45+
"<Project Sdk=\"Microsoft.NET.Sdk\">
46+
47+
<PropertyGroup>
48+
<TargetFramework>net8.0</TargetFramework>
49+
<LangVersion>preview</LangVersion>
50+
<RootNamespace>{name}</RootNamespace>
51+
<ImplicitUsings>enable</ImplicitUsings>
52+
<Nullable>enable</Nullable>
53+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
54+
</PropertyGroup>
55+
56+
<PropertyGroup>
57+
<PublishTrimmed>true</PublishTrimmed>
58+
<AssemblyName>{name}</AssemblyName>
59+
</PropertyGroup>
60+
<ItemGroup>
61+
<NativeLibrary Include=\"{world}_component_type.o\" />
62+
63+
</ItemGroup>
64+
65+
<ItemGroup>
66+
<RdXmlFile Include=\"rd.xml\" />
67+
</ItemGroup>
68+
"
69+
);
70+
71+
if self.aot {
72+
//TODO: Is this handled by the source generator? (Temporary just to test with numbers and strings)
73+
csproj.push_str(
74+
r#"
75+
<ItemGroup>
76+
<CustomLinkerArg Include="-Wl,--export,_initialize" />
77+
<CustomLinkerArg Include="-Wl,--no-entry" />
78+
<CustomLinkerArg Include="-mexec-model=reactor" />
79+
</ItemGroup>
80+
81+
<ItemGroup>
82+
<PackageReference Include="Microsoft.DotNet.ILCompiler.LLVM" Version="8.0.0-*" />
83+
<PackageReference Include="runtime.win-x64.Microsoft.DotNet.ILCompiler.LLVM" Version="8.0.0-*" />
84+
</ItemGroup>
85+
"#,
86+
);
87+
88+
fs::write(
89+
self.dir.join("nuget.config"),
90+
r#"<?xml version="1.0" encoding="utf-8"?>
91+
<configuration>
92+
<config>
93+
<add key="globalPackagesFolder" value=".packages" />
94+
</config>
95+
<packageSources>
96+
<!--To inherit the global NuGet package sources remove the <clear/> line below -->
97+
<clear />
98+
<add key="nuget" value="https://api.nuget.org/v3/index.json" />
99+
<add key="dotnet-experimental" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json" />
100+
<!--<add key="dotnet-experimental" value="C:\github\runtimelab\artifacts\packages\Debug\Shipping" />-->
101+
</packageSources>
102+
</configuration>"#,
103+
)?;
104+
}
105+
106+
if !&self.wasm_imports.is_empty() {
107+
csproj.push_str("\t<ItemGroup>\n");
108+
for (module_name, func_name) in &self.wasm_imports {
109+
csproj.push_str(&format!(
110+
r#"
111+
<WasmImport Include="{}!{}" />
112+
"#,
113+
module_name, func_name,
114+
));
115+
}
116+
csproj.push_str("\t</ItemGroup>\n\n");
117+
}
118+
119+
if self.clean_targets {
120+
let mut wasm_filename = self.dir.join(name);
121+
wasm_filename.set_extension("wasm");
122+
// In CI we run out of disk space if we don't clean up the files, we don't need to keep any of it around.
123+
csproj.push_str(&format!(
124+
"<Target Name=\"CleanAndDelete\" AfterTargets=\"Clean\">
125+
<!-- Remove obj folder -->
126+
<RemoveDir Directories=\"$(BaseIntermediateOutputPath)\" />
127+
<!-- Remove bin folder -->
128+
<RemoveDir Directories=\"$(BaseOutputPath)\" />
129+
<RemoveDir Directories=\"{}\" />
130+
<RemoveDir Directories=\".packages\" />
131+
</Target>",
132+
wasm_filename.display()
133+
));
134+
}
135+
136+
csproj.push_str(
137+
r#"</Project>
138+
"#,
139+
);
140+
141+
let camel = snake_world.to_upper_camel_case();
142+
fs::write(self.dir.join(format!("{camel}.csproj")), csproj)?;
143+
144+
Ok(())
145+
}
146+
147+
pub fn aot(&mut self) {
148+
self.aot = true;
149+
}
150+
151+
pub fn clean(&mut self) {
152+
self.clean_targets = true;
153+
}
154+
155+
pub fn add_import(&mut self, module_name: &str, func_name: &str) {
156+
self.wasm_imports
157+
.push((module_name.to_string(), func_name.to_string()));
158+
}
159+
}

crates/csharp/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ use wit_bindgen_core::{
2222
Files, InterfaceGenerator as _, Ns, WorldGenerator,
2323
};
2424
use wit_component::StringEncoding;
25+
mod csproj;
26+
pub use csproj::CSProject;
2527

2628
//cargo run c-sharp --out-dir testing-csharp tests/codegen/floats.wit
2729

crates/csharp/tests/codegen.rs

Lines changed: 8 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// TODO: Implement tests similar to the other generators.
22
// This requires that we have any dependencies either included here or published to NuGet or similar.
33
use std::{
4-
env, fs,
4+
env,
55
path::{Path, PathBuf},
66
process::{Command, Stdio},
77
};
@@ -89,109 +89,10 @@ fn verify(dir: &Path, name: &str) {
8989
}
9090

9191
fn aot_verify(dir: &Path, name: &str) {
92-
let mut wasm_filename = dir.join(name);
93-
wasm_filename.set_extension("wasm");
94-
95-
fs::write(
96-
dir.join("nuget.config"),
97-
r#"<?xml version="1.0" encoding="utf-8"?>
98-
<configuration>
99-
<config>
100-
<add key="globalPackagesFolder" value=".packages" />
101-
</config>
102-
<packageSources>
103-
<!--To inherit the global NuGet package sources remove the <clear/> line below -->
104-
<clear />
105-
<add key="nuget" value="https://api.nuget.org/v3/index.json" />
106-
<add key="dotnet-experimental" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-experimental/nuget/v3/index.json" />
107-
<!--<add key="dotnet-experimental" value="C:\github\runtimelab\artifacts\packages\Debug\Shipping" />-->
108-
</packageSources>
109-
</configuration>"#,
110-
).unwrap();
111-
112-
fs::write(
113-
dir.join("rd.xml"),
114-
format!(
115-
r#"<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
116-
<Application>
117-
<Assembly Name="{name}">
118-
</Assembly>
119-
</Application>
120-
</Directives>"#
121-
),
122-
)
123-
.unwrap();
124-
125-
let mut csproj = format!(
126-
"<Project Sdk=\"Microsoft.NET.Sdk\">
127-
128-
<PropertyGroup>
129-
<TargetFramework>net8.0</TargetFramework>
130-
<LangVersion>preview</LangVersion>
131-
<RootNamespace>{name}</RootNamespace>
132-
<ImplicitUsings>enable</ImplicitUsings>
133-
<Nullable>enable</Nullable>
134-
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
135-
</PropertyGroup>
136-
137-
<PropertyGroup>
138-
<PublishTrimmed>true</PublishTrimmed>
139-
<AssemblyName>{name}</AssemblyName>
140-
</PropertyGroup>
141-
"
142-
);
143-
144-
csproj.push_str(
145-
r#"
146-
<ItemGroup>
147-
<RdXmlFile Include="rd.xml" />
148-
</ItemGroup>
149-
150-
"#,
151-
);
152-
153-
csproj.push_str("\t<ItemGroup>\n");
154-
csproj.push_str(&format!(
155-
"\t\t<NativeLibrary Include=\"the_world_component_type.o\" />\n"
156-
));
157-
csproj.push_str("\t</ItemGroup>\n\n");
158-
159-
csproj.push_str(
160-
r#"
161-
<ItemGroup>
162-
<CustomLinkerArg Include="-Wl,--export,_initialize" />
163-
<CustomLinkerArg Include="-Wl,--no-entry" />
164-
<CustomLinkerArg Include="-mexec-model=reactor" />
165-
</ItemGroup>
166-
"#,
167-
);
168-
169-
// In CI we run out of disk space if we don't clean up the files, we don't need to keep any of it around.
170-
csproj.push_str(&format!(
171-
"<Target Name=\"CleanAndDelete\" AfterTargets=\"Clean\">
172-
<!-- Remove obj folder -->
173-
<RemoveDir Directories=\"$(BaseIntermediateOutputPath)\" />
174-
<!-- Remove bin folder -->
175-
<RemoveDir Directories=\"$(BaseOutputPath)\" />
176-
<RemoveDir Directories=\"{}\" />
177-
<RemoveDir Directories=\".packages\" />
178-
</Target>
179-
180-
",
181-
wasm_filename.display()
182-
));
183-
184-
csproj.push_str(
185-
r#"
186-
<ItemGroup>
187-
<PackageReference Include="Microsoft.DotNet.ILCompiler.LLVM" Version="8.0.0-*" />
188-
<PackageReference Include="runtime.win-x64.Microsoft.DotNet.ILCompiler.LLVM" Version="8.0.0-*" />
189-
</ItemGroup>
190-
</Project>
191-
"#,
192-
);
193-
194-
fs::write(dir.join(format!("{name}.csproj")), csproj).unwrap();
92+
let mut project = wit_bindgen_csharp::CSProject::new(dir.to_path_buf(), &name, "the_world");
93+
project.aot();
94+
project.clean();
95+
project.generate().unwrap();
19596

19697
let dotnet_root_env = "DOTNET_ROOT";
19798
let dotnet_cmd: PathBuf;
@@ -204,9 +105,11 @@ fn aot_verify(dir: &Path, name: &str) {
204105

205106
cmd.current_dir(&dir);
206107

108+
let mut wasm_filename = dir.join(name);
109+
wasm_filename.set_extension("wasm");
207110
// add .arg("/bl") to diagnose dotnet build problems
208111
cmd.arg("build")
209-
.arg(dir.join(format!("{name}.csproj")))
112+
.arg(dir.join(format!("TheWorld.csproj")))
210113
.arg("-r")
211114
.arg("wasi-wasm")
212115
.arg("-c")

0 commit comments

Comments
 (0)