From 9946d1814f4af9ef67e5accd9404e0fc69290f6f Mon Sep 17 00:00:00 2001
From: JassJam <0139ne@gmail.com>
Date: Mon, 23 Feb 2026 12:28:50 +0000
Subject: feat: add initial test files
---
tests/projects/csharp/console/.gitignore | 6 ++++++
tests/projects/csharp/console/src/Program.cs | 2 ++
tests/projects/csharp/console/src/test.csproj | 9 +++++++++
tests/projects/csharp/console/test.lua | 10 ++++++++++
tests/projects/csharp/console/xmake.lua | 6 ++++++
.../csharp/console_with_runtime_json/.gitignore | 7 +++++++
.../csharp/console_with_runtime_json/src/Program.cs | 17 +++++++++++++++++
.../csharp/console_with_runtime_json/src/app.csproj | 14 ++++++++++++++
.../csharp/console_with_runtime_json/src/runtime.json | 7 +++++++
.../projects/csharp/console_with_runtime_json/test.lua | 10 ++++++++++
.../csharp/console_with_runtime_json/xmake.lua | 6 ++++++
tests/projects/csharp/exe_with_library/.gitignore | 6 ++++++
.../csharp/exe_with_library/src/app/Program.cs | 4 ++++
.../csharp/exe_with_library/src/app/app.csproj | 12 ++++++++++++
.../csharp/exe_with_library/src/lib/Greeter.cs | 10 ++++++++++
.../csharp/exe_with_library/src/lib/lib.csproj | 8 ++++++++
tests/projects/csharp/exe_with_library/test.lua | 10 ++++++++++
tests/projects/csharp/exe_with_library/xmake.lua | 12 ++++++++++++
tests/projects/csharp/exe_with_package/.gitignore | 6 ++++++
tests/projects/csharp/exe_with_package/src/Program.cs | 4 ++++
tests/projects/csharp/exe_with_package/src/app.csproj | 12 ++++++++++++
tests/projects/csharp/exe_with_package/test.lua | 10 ++++++++++
tests/projects/csharp/exe_with_package/xmake.lua | 6 ++++++
tests/projects/csharp/multiple_library/.gitignore | 6 ++++++
.../csharp/multiple_library/src/libalpha/Alpha.cs | 10 ++++++++++
.../multiple_library/src/libalpha/libalpha.csproj | 8 ++++++++
.../csharp/multiple_library/src/libbeta/Beta.cs | 12 ++++++++++++
.../csharp/multiple_library/src/libbeta/libbeta.csproj | 11 +++++++++++
.../csharp/multiple_library/src/sample/Program.cs | 3 +++
.../csharp/multiple_library/src/sample/sample.csproj | 14 ++++++++++++++
tests/projects/csharp/multiple_library/test.lua | 10 ++++++++++
tests/projects/csharp/multiple_library/xmake.lua | 18 ++++++++++++++++++
tests/projects/csharp/web_project/.gitignore | 7 +++++++
tests/projects/csharp/web_project/src/Program.cs | 8 ++++++++
tests/projects/csharp/web_project/src/webapp.csproj | 8 ++++++++
tests/projects/csharp/web_project/test.lua | 10 ++++++++++
tests/projects/csharp/web_project/xmake.lua | 6 ++++++
37 files changed, 325 insertions(+)
create mode 100644 tests/projects/csharp/console/.gitignore
create mode 100644 tests/projects/csharp/console/src/Program.cs
create mode 100644 tests/projects/csharp/console/src/test.csproj
create mode 100644 tests/projects/csharp/console/test.lua
create mode 100644 tests/projects/csharp/console/xmake.lua
create mode 100644 tests/projects/csharp/console_with_runtime_json/.gitignore
create mode 100644 tests/projects/csharp/console_with_runtime_json/src/Program.cs
create mode 100644 tests/projects/csharp/console_with_runtime_json/src/app.csproj
create mode 100644 tests/projects/csharp/console_with_runtime_json/src/runtime.json
create mode 100644 tests/projects/csharp/console_with_runtime_json/test.lua
create mode 100644 tests/projects/csharp/console_with_runtime_json/xmake.lua
create mode 100644 tests/projects/csharp/exe_with_library/.gitignore
create mode 100644 tests/projects/csharp/exe_with_library/src/app/Program.cs
create mode 100644 tests/projects/csharp/exe_with_library/src/app/app.csproj
create mode 100644 tests/projects/csharp/exe_with_library/src/lib/Greeter.cs
create mode 100644 tests/projects/csharp/exe_with_library/src/lib/lib.csproj
create mode 100644 tests/projects/csharp/exe_with_library/test.lua
create mode 100644 tests/projects/csharp/exe_with_library/xmake.lua
create mode 100644 tests/projects/csharp/exe_with_package/.gitignore
create mode 100644 tests/projects/csharp/exe_with_package/src/Program.cs
create mode 100644 tests/projects/csharp/exe_with_package/src/app.csproj
create mode 100644 tests/projects/csharp/exe_with_package/test.lua
create mode 100644 tests/projects/csharp/exe_with_package/xmake.lua
create mode 100644 tests/projects/csharp/multiple_library/.gitignore
create mode 100644 tests/projects/csharp/multiple_library/src/libalpha/Alpha.cs
create mode 100644 tests/projects/csharp/multiple_library/src/libalpha/libalpha.csproj
create mode 100644 tests/projects/csharp/multiple_library/src/libbeta/Beta.cs
create mode 100644 tests/projects/csharp/multiple_library/src/libbeta/libbeta.csproj
create mode 100644 tests/projects/csharp/multiple_library/src/sample/Program.cs
create mode 100644 tests/projects/csharp/multiple_library/src/sample/sample.csproj
create mode 100644 tests/projects/csharp/multiple_library/test.lua
create mode 100644 tests/projects/csharp/multiple_library/xmake.lua
create mode 100644 tests/projects/csharp/web_project/.gitignore
create mode 100644 tests/projects/csharp/web_project/src/Program.cs
create mode 100644 tests/projects/csharp/web_project/src/webapp.csproj
create mode 100644 tests/projects/csharp/web_project/test.lua
create mode 100644 tests/projects/csharp/web_project/xmake.lua
diff --git a/tests/projects/csharp/console/.gitignore b/tests/projects/csharp/console/.gitignore
new file mode 100644
index 000000000..6b8d3af38
--- /dev/null
+++ b/tests/projects/csharp/console/.gitignore
@@ -0,0 +1,6 @@
+.xmake/
+build/
+bin/
+obj/
+vsxmake*/
+.vs/
diff --git a/tests/projects/csharp/console/src/Program.cs b/tests/projects/csharp/console/src/Program.cs
new file mode 100644
index 000000000..c7d5272d9
--- /dev/null
+++ b/tests/projects/csharp/console/src/Program.cs
@@ -0,0 +1,2 @@
+Console.WriteLine("hello xmake!");
+
diff --git a/tests/projects/csharp/console/src/test.csproj b/tests/projects/csharp/console/src/test.csproj
new file mode 100644
index 000000000..9dbab4973
--- /dev/null
+++ b/tests/projects/csharp/console/src/test.csproj
@@ -0,0 +1,9 @@
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
diff --git a/tests/projects/csharp/console/test.lua b/tests/projects/csharp/console/test.lua
new file mode 100644
index 000000000..5ab8adc69
--- /dev/null
+++ b/tests/projects/csharp/console/test.lua
@@ -0,0 +1,10 @@
+import("lib.detect.find_tool")
+
+function test_build(t)
+ local dotnet = find_tool("dotnet")
+ if dotnet then
+ t:build()
+ else
+ return t:skip("dotnet not found")
+ end
+end
diff --git a/tests/projects/csharp/console/xmake.lua b/tests/projects/csharp/console/xmake.lua
new file mode 100644
index 000000000..25cd801f0
--- /dev/null
+++ b/tests/projects/csharp/console/xmake.lua
@@ -0,0 +1,6 @@
+add_rules("mode.debug", "mode.release")
+
+target("test")
+ set_kind("binary")
+ add_rules("csharp")
+ add_files("src/Program.cs", "src/test.csproj")
diff --git a/tests/projects/csharp/console_with_runtime_json/.gitignore b/tests/projects/csharp/console_with_runtime_json/.gitignore
new file mode 100644
index 000000000..53e577525
--- /dev/null
+++ b/tests/projects/csharp/console_with_runtime_json/.gitignore
@@ -0,0 +1,7 @@
+.xmake/
+build/
+bin/
+obj/
+vsxmake*/
+.vs/
+
diff --git a/tests/projects/csharp/console_with_runtime_json/src/Program.cs b/tests/projects/csharp/console_with_runtime_json/src/Program.cs
new file mode 100644
index 000000000..2c9b0783b
--- /dev/null
+++ b/tests/projects/csharp/console_with_runtime_json/src/Program.cs
@@ -0,0 +1,17 @@
+using System.Text.Json;
+
+var runtimeFile = Path.Combine(AppContext.BaseDirectory, "runtime.json");
+if (!File.Exists(runtimeFile))
+{
+ Console.WriteLine("runtime.json missing");
+ return;
+}
+
+var json = File.ReadAllText(runtimeFile);
+using var doc = JsonDocument.Parse(json);
+var runtime = doc.RootElement.TryGetProperty("runtime", out var value)
+ ? value.GetString()
+ : "unknown";
+
+Console.WriteLine($"runtime={runtime}");
+
diff --git a/tests/projects/csharp/console_with_runtime_json/src/app.csproj b/tests/projects/csharp/console_with_runtime_json/src/app.csproj
new file mode 100644
index 000000000..4e754bb15
--- /dev/null
+++ b/tests/projects/csharp/console_with_runtime_json/src/app.csproj
@@ -0,0 +1,14 @@
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+ PreserveNewest
+
+
+
+
diff --git a/tests/projects/csharp/console_with_runtime_json/src/runtime.json b/tests/projects/csharp/console_with_runtime_json/src/runtime.json
new file mode 100644
index 000000000..15bec810d
--- /dev/null
+++ b/tests/projects/csharp/console_with_runtime_json/src/runtime.json
@@ -0,0 +1,7 @@
+{
+ "runtime": "xmake",
+ "featureFlags": {
+ "sample": true
+ }
+}
+
diff --git a/tests/projects/csharp/console_with_runtime_json/test.lua b/tests/projects/csharp/console_with_runtime_json/test.lua
new file mode 100644
index 000000000..5ab8adc69
--- /dev/null
+++ b/tests/projects/csharp/console_with_runtime_json/test.lua
@@ -0,0 +1,10 @@
+import("lib.detect.find_tool")
+
+function test_build(t)
+ local dotnet = find_tool("dotnet")
+ if dotnet then
+ t:build()
+ else
+ return t:skip("dotnet not found")
+ end
+end
diff --git a/tests/projects/csharp/console_with_runtime_json/xmake.lua b/tests/projects/csharp/console_with_runtime_json/xmake.lua
new file mode 100644
index 000000000..8d8539b26
--- /dev/null
+++ b/tests/projects/csharp/console_with_runtime_json/xmake.lua
@@ -0,0 +1,6 @@
+add_rules("mode.debug", "mode.release")
+
+target("app")
+ set_kind("binary")
+ add_rules("csharp")
+ add_files("src/Program.cs", "src/app.csproj")
diff --git a/tests/projects/csharp/exe_with_library/.gitignore b/tests/projects/csharp/exe_with_library/.gitignore
new file mode 100644
index 000000000..6b8d3af38
--- /dev/null
+++ b/tests/projects/csharp/exe_with_library/.gitignore
@@ -0,0 +1,6 @@
+.xmake/
+build/
+bin/
+obj/
+vsxmake*/
+.vs/
diff --git a/tests/projects/csharp/exe_with_library/src/app/Program.cs b/tests/projects/csharp/exe_with_library/src/app/Program.cs
new file mode 100644
index 000000000..3cf7cb84a
--- /dev/null
+++ b/tests/projects/csharp/exe_with_library/src/app/Program.cs
@@ -0,0 +1,4 @@
+using MyLib;
+
+Console.WriteLine(Greeter.Greet("xmake"));
+
diff --git a/tests/projects/csharp/exe_with_library/src/app/app.csproj b/tests/projects/csharp/exe_with_library/src/app/app.csproj
new file mode 100644
index 000000000..e5e49a685
--- /dev/null
+++ b/tests/projects/csharp/exe_with_library/src/app/app.csproj
@@ -0,0 +1,12 @@
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
diff --git a/tests/projects/csharp/exe_with_library/src/lib/Greeter.cs b/tests/projects/csharp/exe_with_library/src/lib/Greeter.cs
new file mode 100644
index 000000000..546dd4b6f
--- /dev/null
+++ b/tests/projects/csharp/exe_with_library/src/lib/Greeter.cs
@@ -0,0 +1,10 @@
+namespace MyLib;
+
+public static class Greeter
+{
+ public static string Greet(string name)
+ {
+ return $"hello {name}!";
+ }
+}
+
diff --git a/tests/projects/csharp/exe_with_library/src/lib/lib.csproj b/tests/projects/csharp/exe_with_library/src/lib/lib.csproj
new file mode 100644
index 000000000..30c56ecb5
--- /dev/null
+++ b/tests/projects/csharp/exe_with_library/src/lib/lib.csproj
@@ -0,0 +1,8 @@
+
+
+ net8.0
+ enable
+ enable
+
+
+
diff --git a/tests/projects/csharp/exe_with_library/test.lua b/tests/projects/csharp/exe_with_library/test.lua
new file mode 100644
index 000000000..5ab8adc69
--- /dev/null
+++ b/tests/projects/csharp/exe_with_library/test.lua
@@ -0,0 +1,10 @@
+import("lib.detect.find_tool")
+
+function test_build(t)
+ local dotnet = find_tool("dotnet")
+ if dotnet then
+ t:build()
+ else
+ return t:skip("dotnet not found")
+ end
+end
diff --git a/tests/projects/csharp/exe_with_library/xmake.lua b/tests/projects/csharp/exe_with_library/xmake.lua
new file mode 100644
index 000000000..063e051db
--- /dev/null
+++ b/tests/projects/csharp/exe_with_library/xmake.lua
@@ -0,0 +1,12 @@
+add_rules("mode.debug", "mode.release")
+
+target("mylib")
+ set_kind("shared")
+ add_rules("csharp")
+ add_files("src/lib/*.cs", "src/lib/lib.csproj")
+
+target("app")
+ set_kind("binary")
+ add_rules("csharp")
+ add_deps("mylib")
+ add_files("src/app/*.cs", "src/app/app.csproj")
diff --git a/tests/projects/csharp/exe_with_package/.gitignore b/tests/projects/csharp/exe_with_package/.gitignore
new file mode 100644
index 000000000..6b8d3af38
--- /dev/null
+++ b/tests/projects/csharp/exe_with_package/.gitignore
@@ -0,0 +1,6 @@
+.xmake/
+build/
+bin/
+obj/
+vsxmake*/
+.vs/
diff --git a/tests/projects/csharp/exe_with_package/src/Program.cs b/tests/projects/csharp/exe_with_package/src/Program.cs
new file mode 100644
index 000000000..518015e2a
--- /dev/null
+++ b/tests/projects/csharp/exe_with_package/src/Program.cs
@@ -0,0 +1,4 @@
+using Humanizer;
+
+Console.WriteLine(1234.ToWords());
+
diff --git a/tests/projects/csharp/exe_with_package/src/app.csproj b/tests/projects/csharp/exe_with_package/src/app.csproj
new file mode 100644
index 000000000..0521e2a0c
--- /dev/null
+++ b/tests/projects/csharp/exe_with_package/src/app.csproj
@@ -0,0 +1,12 @@
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
diff --git a/tests/projects/csharp/exe_with_package/test.lua b/tests/projects/csharp/exe_with_package/test.lua
new file mode 100644
index 000000000..5ab8adc69
--- /dev/null
+++ b/tests/projects/csharp/exe_with_package/test.lua
@@ -0,0 +1,10 @@
+import("lib.detect.find_tool")
+
+function test_build(t)
+ local dotnet = find_tool("dotnet")
+ if dotnet then
+ t:build()
+ else
+ return t:skip("dotnet not found")
+ end
+end
diff --git a/tests/projects/csharp/exe_with_package/xmake.lua b/tests/projects/csharp/exe_with_package/xmake.lua
new file mode 100644
index 000000000..8d8539b26
--- /dev/null
+++ b/tests/projects/csharp/exe_with_package/xmake.lua
@@ -0,0 +1,6 @@
+add_rules("mode.debug", "mode.release")
+
+target("app")
+ set_kind("binary")
+ add_rules("csharp")
+ add_files("src/Program.cs", "src/app.csproj")
diff --git a/tests/projects/csharp/multiple_library/.gitignore b/tests/projects/csharp/multiple_library/.gitignore
new file mode 100644
index 000000000..6b8d3af38
--- /dev/null
+++ b/tests/projects/csharp/multiple_library/.gitignore
@@ -0,0 +1,6 @@
+.xmake/
+build/
+bin/
+obj/
+vsxmake*/
+.vs/
diff --git a/tests/projects/csharp/multiple_library/src/libalpha/Alpha.cs b/tests/projects/csharp/multiple_library/src/libalpha/Alpha.cs
new file mode 100644
index 000000000..f203d9c9e
--- /dev/null
+++ b/tests/projects/csharp/multiple_library/src/libalpha/Alpha.cs
@@ -0,0 +1,10 @@
+namespace LibAlpha;
+
+public static class Alpha
+{
+ public static string Message()
+ {
+ return "alpha";
+ }
+}
+
diff --git a/tests/projects/csharp/multiple_library/src/libalpha/libalpha.csproj b/tests/projects/csharp/multiple_library/src/libalpha/libalpha.csproj
new file mode 100644
index 000000000..30c56ecb5
--- /dev/null
+++ b/tests/projects/csharp/multiple_library/src/libalpha/libalpha.csproj
@@ -0,0 +1,8 @@
+
+
+ net8.0
+ enable
+ enable
+
+
+
diff --git a/tests/projects/csharp/multiple_library/src/libbeta/Beta.cs b/tests/projects/csharp/multiple_library/src/libbeta/Beta.cs
new file mode 100644
index 000000000..5e431039f
--- /dev/null
+++ b/tests/projects/csharp/multiple_library/src/libbeta/Beta.cs
@@ -0,0 +1,12 @@
+using LibAlpha;
+
+namespace LibBeta;
+
+public static class Beta
+{
+ public static string Message()
+ {
+ return Alpha.Message() + "+beta";
+ }
+}
+
diff --git a/tests/projects/csharp/multiple_library/src/libbeta/libbeta.csproj b/tests/projects/csharp/multiple_library/src/libbeta/libbeta.csproj
new file mode 100644
index 000000000..15502d321
--- /dev/null
+++ b/tests/projects/csharp/multiple_library/src/libbeta/libbeta.csproj
@@ -0,0 +1,11 @@
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
diff --git a/tests/projects/csharp/multiple_library/src/sample/Program.cs b/tests/projects/csharp/multiple_library/src/sample/Program.cs
new file mode 100644
index 000000000..2292a8af9
--- /dev/null
+++ b/tests/projects/csharp/multiple_library/src/sample/Program.cs
@@ -0,0 +1,3 @@
+using LibBeta;
+
+Console.WriteLine(Beta.Message());
\ No newline at end of file
diff --git a/tests/projects/csharp/multiple_library/src/sample/sample.csproj b/tests/projects/csharp/multiple_library/src/sample/sample.csproj
new file mode 100644
index 000000000..459440d0e
--- /dev/null
+++ b/tests/projects/csharp/multiple_library/src/sample/sample.csproj
@@ -0,0 +1,14 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/tests/projects/csharp/multiple_library/test.lua b/tests/projects/csharp/multiple_library/test.lua
new file mode 100644
index 000000000..5ab8adc69
--- /dev/null
+++ b/tests/projects/csharp/multiple_library/test.lua
@@ -0,0 +1,10 @@
+import("lib.detect.find_tool")
+
+function test_build(t)
+ local dotnet = find_tool("dotnet")
+ if dotnet then
+ t:build()
+ else
+ return t:skip("dotnet not found")
+ end
+end
diff --git a/tests/projects/csharp/multiple_library/xmake.lua b/tests/projects/csharp/multiple_library/xmake.lua
new file mode 100644
index 000000000..b4be98c99
--- /dev/null
+++ b/tests/projects/csharp/multiple_library/xmake.lua
@@ -0,0 +1,18 @@
+add_rules("mode.debug", "mode.release")
+
+target("libalpha")
+ set_kind("shared")
+ add_rules("csharp")
+ add_files("src/libalpha/*.cs", "src/libalpha/libalpha.csproj")
+
+target("libbeta")
+ set_kind("shared")
+ add_rules("csharp")
+ add_deps("libalpha")
+ add_files("src/libbeta/*.cs", "src/libbeta/libbeta.csproj")
+
+target("sample")
+ set_kind("binary")
+ add_rules("csharp")
+ add_deps("libbeta")
+ add_files("src/sample/*.cs", "src/sample/sample.csproj")
diff --git a/tests/projects/csharp/web_project/.gitignore b/tests/projects/csharp/web_project/.gitignore
new file mode 100644
index 000000000..53e577525
--- /dev/null
+++ b/tests/projects/csharp/web_project/.gitignore
@@ -0,0 +1,7 @@
+.xmake/
+build/
+bin/
+obj/
+vsxmake*/
+.vs/
+
diff --git a/tests/projects/csharp/web_project/src/Program.cs b/tests/projects/csharp/web_project/src/Program.cs
new file mode 100644
index 000000000..7e432aaf0
--- /dev/null
+++ b/tests/projects/csharp/web_project/src/Program.cs
@@ -0,0 +1,8 @@
+var builder = WebApplication.CreateBuilder(args);
+var app = builder.Build();
+
+app.MapGet("/", () => "hello web xmake!");
+app.MapGet("/health", () => Results.Ok(new { status = "ok" }));
+
+app.Run();
+
diff --git a/tests/projects/csharp/web_project/src/webapp.csproj b/tests/projects/csharp/web_project/src/webapp.csproj
new file mode 100644
index 000000000..4715481ee
--- /dev/null
+++ b/tests/projects/csharp/web_project/src/webapp.csproj
@@ -0,0 +1,8 @@
+
+
+ net8.0
+ enable
+ enable
+
+
+
diff --git a/tests/projects/csharp/web_project/test.lua b/tests/projects/csharp/web_project/test.lua
new file mode 100644
index 000000000..5ab8adc69
--- /dev/null
+++ b/tests/projects/csharp/web_project/test.lua
@@ -0,0 +1,10 @@
+import("lib.detect.find_tool")
+
+function test_build(t)
+ local dotnet = find_tool("dotnet")
+ if dotnet then
+ t:build()
+ else
+ return t:skip("dotnet not found")
+ end
+end
diff --git a/tests/projects/csharp/web_project/xmake.lua b/tests/projects/csharp/web_project/xmake.lua
new file mode 100644
index 000000000..d1e5504c3
--- /dev/null
+++ b/tests/projects/csharp/web_project/xmake.lua
@@ -0,0 +1,6 @@
+add_rules("mode.debug", "mode.release")
+
+target("webapp")
+ set_kind("binary")
+ add_rules("csharp")
+ add_files("src/Program.cs", "src/webapp.csproj")
--
cgit v1.3.1
From be16d7acfedb694e7d9e429b389aa52e06db041f Mon Sep 17 00:00:00 2001
From: JassJam <0139ne@gmail.com>
Date: Mon, 23 Feb 2026 16:56:49 +0000
Subject: feat: add csharp language
---
xmake/languages/csharp/load.lua | 49 +++++++++++++++++++++++++
xmake/languages/csharp/xmake.lua | 78 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 127 insertions(+)
create mode 100644 xmake/languages/csharp/load.lua
create mode 100644 xmake/languages/csharp/xmake.lua
diff --git a/xmake/languages/csharp/load.lua b/xmake/languages/csharp/load.lua
new file mode 100644
index 000000000..afff5c468
--- /dev/null
+++ b/xmake/languages/csharp/load.lua
@@ -0,0 +1,49 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author JassJam
+-- @file load.lua
+--
+
+function _get_apis()
+ local apis = {}
+ apis.values = {
+ -- target.add_xxx
+ "target.add_csflags"
+ , "target.add_ldflags"
+ , "target.add_shflags"
+ -- option.add_xxx
+ , "option.add_csflags"
+ , "option.add_ldflags"
+ , "option.add_shflags"
+ -- toolchain.add_xxx
+ , "toolchain.add_csflags"
+ , "toolchain.add_ldflags"
+ , "toolchain.add_shflags"
+ }
+ apis.paths = {
+ -- target.add_xxx
+ "target.add_linkdirs"
+ -- option.add_xxx
+ , "option.add_linkdirs"
+ }
+ return apis
+end
+
+function main()
+ return {apis = _get_apis()}
+end
+
diff --git a/xmake/languages/csharp/xmake.lua b/xmake/languages/csharp/xmake.lua
new file mode 100644
index 000000000..c4bc5aa30
--- /dev/null
+++ b/xmake/languages/csharp/xmake.lua
@@ -0,0 +1,78 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author JassJam
+-- @file xmake.lua
+--
+
+language("csharp")
+ add_rules("csharp")
+ set_sourcekinds {cs = ".cs", csproj = ".csproj"}
+ set_sourceflags {cs = "csflags"}
+ set_targetkinds {binary = "cs", shared = "cs"}
+ set_targetflags {binary = "ldflags", shared = "shflags"}
+ set_langkinds {csharp = "cs"}
+ set_mixingkinds("cs")
+
+ on_load("load")
+
+ set_nameflags {
+ object = {
+ "target.symbols"
+ , "target.warnings"
+ , "target.optimize:check"
+ }
+ , binary = {
+ "target.strip"
+ , "target.symbols"
+ , "config.linkdirs"
+ , "target.linkdirs"
+ , "config.links"
+ , "target.links"
+ , "config.syslinks"
+ , "target.syslinks"
+ }
+ , shared = {
+ "target.strip"
+ , "target.symbols"
+ , "config.linkdirs"
+ , "target.linkdirs"
+ , "config.links"
+ , "target.links"
+ , "config.syslinks"
+ , "target.syslinks"
+ }
+ }
+
+ set_menu {
+ config =
+ {
+ {category = "Cross Complation Configuration/Compiler Configuration" }
+ , {nil, "cs", "kv", nil, "The C# Compiler" }
+
+ , {category = "Cross Complation Configuration/Linker Configuration" }
+ , {nil, "ld", "kv", nil, "The Linker" }
+ , {nil, "sh", "kv", nil, "The Shared Library Linker" }
+
+ , {category = "Cross Complation Configuration/Compiler Flags Configuration" }
+ , {nil, "csflags", "kv", nil, "The C# Compiler Flags" }
+
+ , {category = "Cross Complation Configuration/Linker Flags Configuration" }
+ , {nil, "ldflags", "kv", nil, "The Binary Linker Flags" }
+ , {nil, "shflags", "kv", nil, "The Shared Library Linker Flags" }
+ }
+ }
+
--
cgit v1.3.1
From 426746d3e909887b55918508b7ee3f2024466257 Mon Sep 17 00:00:00 2001
From: JassJam <0139ne@gmail.com>
Date: Mon, 23 Feb 2026 16:57:05 +0000
Subject: feat: add csharp rule
---
xmake/rules/csharp/xmake.lua | 341 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 341 insertions(+)
create mode 100644 xmake/rules/csharp/xmake.lua
diff --git a/xmake/rules/csharp/xmake.lua b/xmake/rules/csharp/xmake.lua
new file mode 100644
index 000000000..6bc0eef74
--- /dev/null
+++ b/xmake/rules/csharp/xmake.lua
@@ -0,0 +1,341 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author JassJam
+-- @file xmake.lua
+--
+
+rule("csharp.build")
+ set_extensions(".cs", ".csproj")
+ set_sourcekinds("cs", "csproj", {objectfiles = false})
+
+ local function _find_csproj(target)
+ local csproj = target:data("csharp.csproj")
+ if csproj then
+ return csproj
+ end
+ for _, sourcefile in ipairs(target:sourcefiles()) do
+ if path.extension(sourcefile):lower() == ".csproj" then
+ local csprojabs = path.is_absolute(sourcefile) and sourcefile or path.absolute(sourcefile, os.projectdir())
+ if os.isfile(csprojabs) then
+ return csprojabs
+ end
+ end
+ end
+ return nil
+ end
+
+ local function _build_mode_to_configuration()
+ local mode
+ if type(get_config) == "function" then
+ mode = get_config("mode")
+ end
+ if not mode and type(is_mode) == "function" then
+ if is_mode("debug") then
+ mode = "debug"
+ elseif is_mode("release") then
+ mode = "release"
+ end
+ end
+ mode = mode or "release"
+ local mode_lower = mode:lower()
+ if mode_lower == "debug" then
+ return "Debug"
+ elseif mode_lower == "release" then
+ return "Release"
+ end
+ return mode:sub(1, 1):upper() .. mode:sub(2)
+ end
+
+ local function _map_rid_arch(arch)
+ arch = (arch or ""):lower()
+ if arch == "x64" or arch == "x86_64" or arch == "amd64" then
+ return "x64"
+ elseif arch == "x86" or arch == "i386" then
+ return "x86"
+ elseif arch == "arm64" then
+ return "arm64"
+ elseif arch == "arm" or arch == "armv7" then
+ return "arm"
+ elseif arch == "riscv64" then
+ return "riscv64"
+ end
+ return nil
+ end
+
+ local function _get_runtime_identifier(target)
+ local rid = target:values("csharp.runtime_identifier")
+ if type(rid) == "table" then
+ rid = rid[1]
+ end
+ if rid and #rid > 0 then
+ return rid
+ end
+ local arch = _map_rid_arch(target:arch())
+ if not arch then
+ return nil
+ end
+ local plat = target:plat()
+ if plat == "windows" or plat == "mingw" or plat == "msys" or plat == "cygwin" then
+ return "win-" .. arch
+ elseif plat == "linux" then
+ return "linux-" .. arch
+ elseif plat == "macosx" then
+ return "osx-" .. arch
+ end
+ return nil
+ end
+
+ local function _append_target_flags(target, argv)
+ local flags = {}
+ table.join2(flags, table.wrap(target:get("csflags")))
+ table.join2(flags, table.wrap(target:get("ldflags")))
+ table.join2(flags, table.wrap(target:get("arflags")))
+ table.join2(flags, table.wrap(target:get("shflags")))
+ for _, flag in ipairs(flags) do
+ if flag and #flag > 0 then
+ table.insert(argv, flag)
+ end
+ end
+ end
+
+ local function _get_dotnet_program(target)
+ local function _get_configured_program(toolkind)
+ if target:get("toolset." .. toolkind) then
+ local program = target:tool(toolkind)
+ if program and #program > 0 then
+ return program
+ end
+ end
+ end
+
+ local program = nil
+ if target:is_binary() then
+ program = _get_configured_program("ld")
+ elseif target:is_shared() then
+ program = _get_configured_program("sh")
+ else
+ program = _get_configured_program("cs")
+ end
+ if program then
+ return program
+ end
+
+ program = target:tool("cs")
+ if program and #program > 0 then
+ return program
+ end
+ return "dotnet"
+ end
+
+ on_load(function (target)
+ local targetdir = target:targetdir()
+
+ if targetdir and path.filename(targetdir) ~= target:name() then
+ target:set("targetdir", path.join(targetdir, target:name()))
+ end
+
+ if target:is_static() or target:is_shared() then
+ if not target:get("extension") then
+ target:set("extension", ".dll")
+ end
+ if target:get("prefixname") == nil then
+ target:set("prefixname", "")
+ end
+ end
+
+ -- find and cache the .csproj file for later use in build/install
+ local csprojfiles = {}
+ for _, sourcefile in ipairs(target:sourcefiles()) do
+ if path.extension(sourcefile):lower() == ".csproj" then
+ table.insert(csprojfiles, sourcefile)
+ end
+ end
+ assert(#csprojfiles > 0, "target(%s): csharp requires one .csproj in add_files()!", target:name())
+ assert(#csprojfiles == 1, "target(%s): csharp only supports one .csproj file!", target:name())
+
+ local csprojfile = csprojfiles[1]
+ local csprojabs = path.is_absolute(csprojfile) and csprojfile or path.absolute(csprojfile, os.projectdir())
+ assert(os.isfile(csprojabs), "target(%s): csharp .csproj not found: %s", target:name(), csprojfile)
+ target:data_set("csharp.csproj", csprojabs)
+ end)
+
+ on_buildcmd(function (target, batchcmds, opt)
+ local csprojfile = assert(_find_csproj(target), "target(%s): missing csharp .csproj file!", target:name())
+ local dotnet = _get_dotnet_program(target)
+ local configuration = _build_mode_to_configuration()
+ local command = target:is_binary() and "publish" or "build"
+ local argv = {
+ command, csprojfile,
+ "--nologo",
+ "--configuration", configuration,
+ "--verbosity", "minimal"
+ }
+
+ local targetdir = target:targetdir()
+ local targetdirabs
+ if targetdir then
+ targetdirabs = path.is_absolute(targetdir) and targetdir or path.absolute(targetdir, os.projectdir())
+ table.join2(argv, {"--output", targetdirabs})
+ end
+
+ local rid = _get_runtime_identifier(target)
+ if rid and target:is_binary() then
+ table.join2(argv, {"--runtime", rid})
+ end
+ _append_target_flags(target, argv)
+
+ batchcmds:show_progress(opt.progress, "${color.build.target}building.csharp.$(mode) %s", target:name())
+ if targetdirabs then
+ batchcmds:mkdir(targetdirabs)
+ end
+
+ batchcmds:vrunv(dotnet, argv, {curdir = path.directory(csprojfile)})
+
+ local targetfile = target:targetfile()
+ if targetfile then
+ batchcmds:set_depmtime(os.mtime(targetfile))
+ batchcmds:set_depcache(target:dependfile(targetfile))
+ end
+ end)
+
+ on_clean(function (target, opt)
+ local csprojfile = _find_csproj(target)
+ if csprojfile then
+ local csprojdir = path.directory(csprojfile)
+ os.tryrm(path.join(csprojdir, "bin"))
+ os.tryrm(path.join(csprojdir, "obj"))
+ end
+ end)
+
+ on_install(function (target, opt)
+ local function _q(arg)
+ arg = tostring(arg)
+ if arg:find("[%s\"]") then
+ arg = "\"" .. arg:gsub("\"", "\\\"") .. "\""
+ end
+ return arg
+ end
+
+ local csprojfile = assert(_find_csproj(target), "target(%s): missing csharp .csproj file!", target:name())
+ local dotnet = _get_dotnet_program(target)
+ local configuration = _build_mode_to_configuration()
+
+ local install_path = target:installdir()
+ if target:is_binary() then
+ install_path = target:installdir("bin")
+ elseif target:is_static() or target:is_shared() then
+ install_path = target:installdir("lib")
+ end
+
+ if not install_path or #install_path == 0 then
+ return
+ end
+
+ local install_abs = path.is_absolute(install_path) and install_path or path.absolute(install_path, os.projectdir())
+ os.mkdir(install_abs)
+
+ local rid = _get_runtime_identifier(target)
+ local argv = {
+ "publish", csprojfile,
+ "--nologo",
+ "--configuration", configuration,
+ "--verbosity", "minimal",
+ "--output", install_abs
+ }
+
+ if rid and target:is_binary() then
+ table.join2(argv, {"--runtime", rid})
+ end
+ _append_target_flags(target, argv)
+
+ local runopt = {curdir = path.directory(csprojfile)}
+ if os.vrunv then
+ os.vrunv(dotnet, argv, runopt)
+ elseif os.runv then
+ os.runv(dotnet, argv, runopt)
+ elseif os.execv then
+ os.execv(dotnet, argv, runopt)
+ elseif os.vrun then
+ local cmd = _q(dotnet)
+ for _, arg in ipairs(argv) do
+ cmd = cmd .. " " .. _q(arg)
+ end
+ os.vrun(
+ cmd,
+ runopt
+ )
+ elseif os.run then
+ local cmd = _q(dotnet)
+ for _, arg in ipairs(argv) do
+ cmd = cmd .. " " .. _q(arg)
+ end
+ os.run(cmd, runopt)
+ else
+ local targetdir = target:targetdir()
+ if targetdir and os.isdir(targetdir) then
+ os.cp(path.join(targetdir, "**"), install_abs, {rootdir = targetdir})
+ end
+ end
+
+ if target:is_binary() then
+ local targetdir = target:targetdir()
+ if targetdir and os.isdir(targetdir) then
+ os.cp(path.join(targetdir, "**"), install_abs, {rootdir = targetdir})
+ end
+ end
+ end)
+
+ on_installcmd(function (target, batchcmds, opt)
+ local csprojfile = assert(_find_csproj(target), "target(%s): missing csharp .csproj file!", target:name())
+ local dotnet = _get_dotnet_program(target)
+ local configuration = _build_mode_to_configuration()
+
+ local install_path = target:installdir()
+ if target:is_binary() then
+ install_path = target:installdir("bin")
+ elseif target:is_static() or target:is_shared() then
+ install_path = target:installdir("lib")
+ end
+
+ local install_abs
+ local argv = {
+ "publish", csprojfile,
+ "--nologo",
+ "--configuration", configuration,
+ "--verbosity", "minimal"
+ }
+ if install_path and #install_path > 0 then
+ install_abs = path.is_absolute(install_path) and install_path or path.absolute(install_path, os.projectdir())
+ table.join2(argv, {"--output", install_abs})
+ end
+
+ local rid = _get_runtime_identifier(target)
+ if rid and target:is_binary() then
+ table.join2(argv, {"--runtime", rid})
+ end
+ _append_target_flags(target, argv)
+
+ batchcmds:show_progress(opt.progress, "${color.build.target}publishing.csharp.$(mode) %s", target:name())
+ if install_abs then
+ batchcmds:mkdir(install_abs)
+ end
+
+ batchcmds:vrunv(dotnet, argv, {curdir = path.directory(csprojfile)})
+ end)
+
+rule("csharp")
+ add_deps("csharp.build")
--
cgit v1.3.1
From 6fdb4e159d23995e01a676ee9bf754ad171f5afd Mon Sep 17 00:00:00 2001
From: JassJam <0139ne@gmail.com>
Date: Tue, 24 Feb 2026 10:48:41 +0000
Subject: feat: add dotnet verbosity option for csharp rule
---
xmake/core/project/policy.lua | 2 ++
xmake/rules/csharp/xmake.lua | 46 +++++++++++++++++++++++++++++++++++++------
2 files changed, 42 insertions(+), 6 deletions(-)
diff --git a/xmake/core/project/policy.lua b/xmake/core/project/policy.lua
index 0cf184d91..09751ba72 100644
--- a/xmake/core/project/policy.lua
+++ b/xmake/core/project/policy.lua
@@ -125,6 +125,8 @@ function policy.policies()
["build.progress_style"] = {description = "Set the build progress output style.", type = "string", values = {"scroll", "singlerow", "multirow"}},
-- Show target name in build progress output
["build.progress_show_target"] = {description = "Show target name in build progress output.", default = true, type = "boolean"},
+ -- Set dotnet verbosity for csharp rule, e.g. quiet|minimal|normal|detailed|diagnostic
+ ["build.csharp.dotnet_verbosity"] = {description = "Set dotnet verbosity for csharp rule.", type = "string", values = {"quiet", "minimal", "normal", "detailed", "diagnostic"}},
-- Enable windows UAC and set level, e.g. invoker, admin, highest
["windows.manifest.uac"] = {description = "Enable windows manifest UAC.", type = "string"},
-- Enable ui access for windows UAC
diff --git a/xmake/rules/csharp/xmake.lua b/xmake/rules/csharp/xmake.lua
index 6bc0eef74..88f235ae1 100644
--- a/xmake/rules/csharp/xmake.lua
+++ b/xmake/rules/csharp/xmake.lua
@@ -112,6 +112,37 @@ rule("csharp.build")
end
end
+ local function _get_dotnet_runopt(csprojfile)
+ return {
+ curdir = path.directory(csprojfile),
+ envs = {
+ DOTNET_NOLOGO = "1",
+ DOTNET_CLI_TELEMETRY_OPTOUT = "1",
+ DOTNET_SKIP_FIRST_TIME_EXPERIENCE = "1",
+ DOTNET_GENERATE_ASPNET_CERTIFICATE = "0",
+ DOTNET_ADD_GLOBAL_TOOLS_TO_PATH = "0"
+ }
+ }
+ end
+
+ local function _get_dotnet_verbosity(target)
+ local verbosity = target:values("csharp.dotnet_verbosity")
+ if type(verbosity) == "table" then
+ verbosity = verbosity[1]
+ end
+ if not (verbosity and #verbosity > 0) then
+ verbosity = target:policy("build.csharp.dotnet_verbosity")
+ end
+ if verbosity and #verbosity > 0 then
+ verbosity = verbosity:lower()
+ if table.contains({"quiet", "minimal", "normal", "detailed", "diagnostic"}, verbosity) then
+ return verbosity
+ end
+ raise("target(%s): invalid csharp.dotnet_verbosity(%s), expected one of: quiet|minimal|normal|detailed|diagnostic", target:name(), verbosity)
+ end
+ return "quiet"
+ end
+
local function _get_dotnet_program(target)
local function _get_configured_program(toolkind)
if target:get("toolset." .. toolkind) then
@@ -177,12 +208,13 @@ rule("csharp.build")
local csprojfile = assert(_find_csproj(target), "target(%s): missing csharp .csproj file!", target:name())
local dotnet = _get_dotnet_program(target)
local configuration = _build_mode_to_configuration()
+ local verbosity = _get_dotnet_verbosity(target)
local command = target:is_binary() and "publish" or "build"
local argv = {
command, csprojfile,
"--nologo",
"--configuration", configuration,
- "--verbosity", "minimal"
+ "--verbosity", verbosity
}
local targetdir = target:targetdir()
@@ -203,7 +235,7 @@ rule("csharp.build")
batchcmds:mkdir(targetdirabs)
end
- batchcmds:vrunv(dotnet, argv, {curdir = path.directory(csprojfile)})
+ batchcmds:vrunv(dotnet, argv, _get_dotnet_runopt(csprojfile))
local targetfile = target:targetfile()
if targetfile then
@@ -233,6 +265,7 @@ rule("csharp.build")
local csprojfile = assert(_find_csproj(target), "target(%s): missing csharp .csproj file!", target:name())
local dotnet = _get_dotnet_program(target)
local configuration = _build_mode_to_configuration()
+ local verbosity = _get_dotnet_verbosity(target)
local install_path = target:installdir()
if target:is_binary() then
@@ -253,7 +286,7 @@ rule("csharp.build")
"publish", csprojfile,
"--nologo",
"--configuration", configuration,
- "--verbosity", "minimal",
+ "--verbosity", verbosity,
"--output", install_abs
}
@@ -262,7 +295,7 @@ rule("csharp.build")
end
_append_target_flags(target, argv)
- local runopt = {curdir = path.directory(csprojfile)}
+ local runopt = _get_dotnet_runopt(csprojfile)
if os.vrunv then
os.vrunv(dotnet, argv, runopt)
elseif os.runv then
@@ -303,6 +336,7 @@ rule("csharp.build")
local csprojfile = assert(_find_csproj(target), "target(%s): missing csharp .csproj file!", target:name())
local dotnet = _get_dotnet_program(target)
local configuration = _build_mode_to_configuration()
+ local verbosity = _get_dotnet_verbosity(target)
local install_path = target:installdir()
if target:is_binary() then
@@ -316,7 +350,7 @@ rule("csharp.build")
"publish", csprojfile,
"--nologo",
"--configuration", configuration,
- "--verbosity", "minimal"
+ "--verbosity", verbosity
}
if install_path and #install_path > 0 then
install_abs = path.is_absolute(install_path) and install_path or path.absolute(install_path, os.projectdir())
@@ -334,7 +368,7 @@ rule("csharp.build")
batchcmds:mkdir(install_abs)
end
- batchcmds:vrunv(dotnet, argv, {curdir = path.directory(csprojfile)})
+ batchcmds:vrunv(dotnet, argv, _get_dotnet_runopt(csprojfile))
end)
rule("csharp")
--
cgit v1.3.1
From be2de5598c80f9b9960909aa876d4a4e533b9710 Mon Sep 17 00:00:00 2001
From: JassJam <0139ne@gmail.com>
Date: Tue, 24 Feb 2026 11:06:13 +0000
Subject: fix: fix incremental compilation in C#
---
xmake/rules/csharp/xmake.lua | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/xmake/rules/csharp/xmake.lua b/xmake/rules/csharp/xmake.lua
index 88f235ae1..11aa82347 100644
--- a/xmake/rules/csharp/xmake.lua
+++ b/xmake/rules/csharp/xmake.lua
@@ -201,6 +201,10 @@ rule("csharp.build")
local csprojfile = csprojfiles[1]
local csprojabs = path.is_absolute(csprojfile) and csprojfile or path.absolute(csprojfile, os.projectdir())
assert(os.isfile(csprojabs), "target(%s): csharp .csproj not found: %s", target:name(), csprojfile)
+
+ if not target:get("filename") and not target:get("basename") then
+ target:set("basename", path.basename(csprojabs))
+ end
target:data_set("csharp.csproj", csprojabs)
end)
@@ -239,6 +243,8 @@ rule("csharp.build")
local targetfile = target:targetfile()
if targetfile then
+ local sourcefiles = target:sourcefiles()
+ batchcmds:add_depfiles(sourcefiles)
batchcmds:set_depmtime(os.mtime(targetfile))
batchcmds:set_depcache(target:dependfile(targetfile))
end
--
cgit v1.3.1
From 454997b0d4e7775ea70f81986528e7fc21dc50f9 Mon Sep 17 00:00:00 2001
From: JassJam <0139ne@gmail.com>
Date: Tue, 24 Feb 2026 18:04:11 +0000
Subject: feat: split C# rule into multiple modules
---
xmake/rules/csharp/buildcmd.lua | 62 ++++++
xmake/rules/csharp/clean.lua | 40 ++++
xmake/rules/csharp/csharp_common.lua | 169 ++++++++++++++++
xmake/rules/csharp/install.lua | 95 +++++++++
xmake/rules/csharp/installcmd.lua | 59 ++++++
xmake/rules/csharp/load.lua | 54 ++++++
xmake/rules/csharp/xmake.lua | 365 +----------------------------------
7 files changed, 485 insertions(+), 359 deletions(-)
create mode 100644 xmake/rules/csharp/buildcmd.lua
create mode 100644 xmake/rules/csharp/clean.lua
create mode 100644 xmake/rules/csharp/csharp_common.lua
create mode 100644 xmake/rules/csharp/install.lua
create mode 100644 xmake/rules/csharp/installcmd.lua
create mode 100644 xmake/rules/csharp/load.lua
diff --git a/xmake/rules/csharp/buildcmd.lua b/xmake/rules/csharp/buildcmd.lua
new file mode 100644
index 000000000..c79ae65f4
--- /dev/null
+++ b/xmake/rules/csharp/buildcmd.lua
@@ -0,0 +1,62 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author JassJam
+-- @file buildcmd.lua
+--
+
+local csharp_common = import("csharp_common", {anonymous = true})
+
+function main(target, batchcmds, opt)
+ local csprojfile = assert(csharp_common.find_csproj(target), "target(%s): missing csharp .csproj file!", target:name())
+ local dotnet = csharp_common.get_dotnet_program(target)
+ local configuration = csharp_common.build_mode_to_configuration()
+ local verbosity = csharp_common.get_dotnet_verbosity(target)
+ local command = target:is_binary() and "publish" or "build"
+ local argv = {
+ command, csprojfile,
+ "--nologo",
+ "--configuration", configuration,
+ "--verbosity", verbosity
+ }
+
+ local targetdir = target:targetdir()
+ local targetdirabs
+ if targetdir then
+ targetdirabs = path.is_absolute(targetdir) and targetdir or path.absolute(targetdir, os.projectdir())
+ table.join2(argv, {"--output", targetdirabs})
+ end
+
+ local rid = csharp_common.get_runtime_identifier(target)
+ if rid and target:is_binary() then
+ table.join2(argv, {"--runtime", rid})
+ end
+ csharp_common.append_target_flags(target, argv)
+
+ batchcmds:show_progress(opt.progress, "${color.build.target}building.csharp.$(mode) %s", target:name())
+ if targetdirabs then
+ batchcmds:mkdir(targetdirabs)
+ end
+ batchcmds:vrunv(dotnet, argv, csharp_common.get_dotnet_runopt(csprojfile))
+
+ local targetfile = target:targetfile()
+ if targetfile then
+ local sourcefiles = target:sourcefiles()
+ batchcmds:add_depfiles(sourcefiles)
+ batchcmds:set_depmtime(os.mtime(targetfile))
+ batchcmds:set_depcache(target:dependfile(targetfile))
+ end
+end
diff --git a/xmake/rules/csharp/clean.lua b/xmake/rules/csharp/clean.lua
new file mode 100644
index 000000000..6de720709
--- /dev/null
+++ b/xmake/rules/csharp/clean.lua
@@ -0,0 +1,40 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author JassJam
+-- @file clean.lua
+--
+
+import("target.action.clean", {alias = "_do_clean_target"})
+local csharp_common = import("csharp_common", {anonymous = true})
+
+function main(target, opt)
+ _do_clean_target(target)
+
+ local targetfile = target:targetfile()
+ if targetfile then
+ os.tryrm(target:dependfile(targetfile))
+ end
+ os.tryrm(target:targetdir())
+
+ -- also remove the bin/obj folders next to the .csproj file
+ local csprojfile = csharp_common.find_csproj(target)
+ if csprojfile then
+ local csprojdir = path.directory(csprojfile)
+ os.tryrm(path.join(csprojdir, "bin"))
+ os.tryrm(path.join(csprojdir, "obj"))
+ end
+end
diff --git a/xmake/rules/csharp/csharp_common.lua b/xmake/rules/csharp/csharp_common.lua
new file mode 100644
index 000000000..e6f2bc7c3
--- /dev/null
+++ b/xmake/rules/csharp/csharp_common.lua
@@ -0,0 +1,169 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author JassJam
+-- @file csharp_common.lua
+--
+
+local function _map_rid_arch(arch)
+ arch = (arch or ""):lower()
+ if arch == "x64" or arch == "x86_64" or arch == "amd64" then
+ return "x64"
+ elseif arch == "x86" or arch == "i386" then
+ return "x86"
+ elseif arch == "arm64" then
+ return "arm64"
+ elseif arch == "arm" or arch == "armv7" then
+ return "arm"
+ elseif arch == "riscv64" then
+ return "riscv64"
+ end
+ return nil
+end
+
+function find_csproj(target)
+ local csproj = target:data("csharp.csproj")
+ if csproj then
+ return csproj
+ end
+ for _, sourcefile in ipairs(target:sourcefiles()) do
+ if path.extension(sourcefile):lower() == ".csproj" then
+ local csprojabs = path.is_absolute(sourcefile) and sourcefile or path.absolute(sourcefile, os.projectdir())
+ if os.isfile(csprojabs) then
+ return csprojabs
+ end
+ end
+ end
+ return nil
+end
+
+function build_mode_to_configuration()
+ local mode
+ if type(get_config) == "function" then
+ mode = get_config("mode")
+ end
+ if not mode and type(is_mode) == "function" then
+ if is_mode("debug") then
+ mode = "debug"
+ elseif is_mode("release") then
+ mode = "release"
+ end
+ end
+ mode = mode or "release"
+ local mode_lower = mode:lower()
+ if mode_lower == "debug" then
+ return "Debug"
+ elseif mode_lower == "release" then
+ return "Release"
+ end
+ return mode:sub(1, 1):upper() .. mode:sub(2)
+end
+
+function get_runtime_identifier(target)
+ local rid = target:values("csharp.runtime_identifier")
+ if type(rid) == "table" then
+ rid = rid[1]
+ end
+ if rid and #rid > 0 then
+ return rid
+ end
+ local arch = _map_rid_arch(target:arch())
+ if not arch then
+ return nil
+ end
+ local plat = target:plat()
+ if plat == "windows" or plat == "mingw" or plat == "msys" or plat == "cygwin" then
+ return "win-" .. arch
+ elseif plat == "linux" then
+ return "linux-" .. arch
+ elseif plat == "macosx" then
+ return "osx-" .. arch
+ end
+ return nil
+end
+
+function append_target_flags(target, argv)
+ local flags = {}
+ table.join2(flags, table.wrap(target:get("csflags")))
+ table.join2(flags, table.wrap(target:get("ldflags")))
+ table.join2(flags, table.wrap(target:get("arflags")))
+ table.join2(flags, table.wrap(target:get("shflags")))
+ for _, flag in ipairs(flags) do
+ if flag and #flag > 0 then
+ table.insert(argv, flag)
+ end
+ end
+end
+
+function get_dotnet_runopt(csprojfile)
+ return {
+ curdir = path.directory(csprojfile),
+ envs = {
+ DOTNET_NOLOGO = "1",
+ DOTNET_CLI_TELEMETRY_OPTOUT = "1",
+ DOTNET_SKIP_FIRST_TIME_EXPERIENCE = "1",
+ DOTNET_GENERATE_ASPNET_CERTIFICATE = "0",
+ DOTNET_ADD_GLOBAL_TOOLS_TO_PATH = "0"
+ }
+ }
+end
+
+function get_dotnet_verbosity(target)
+ local verbosity = target:values("csharp.dotnet_verbosity")
+ if type(verbosity) == "table" then
+ verbosity = verbosity[1]
+ end
+ if not (verbosity and #verbosity > 0) then
+ verbosity = target:policy("build.csharp.dotnet_verbosity")
+ end
+ if verbosity and #verbosity > 0 then
+ verbosity = verbosity:lower()
+ if table.contains({"quiet", "minimal", "normal", "detailed", "diagnostic"}, verbosity) then
+ return verbosity
+ end
+ raise("target(%s): invalid csharp.dotnet_verbosity(%s), expected one of: quiet|minimal|normal|detailed|diagnostic", target:name(), verbosity)
+ end
+ return "quiet"
+end
+
+function get_dotnet_program(target)
+ local function _get_configured_program(toolkind)
+ if target:get("toolset." .. toolkind) then
+ local program = target:tool(toolkind)
+ if program and #program > 0 then
+ return program
+ end
+ end
+ end
+
+ local program = nil
+ if target:is_binary() then
+ program = _get_configured_program("ld")
+ elseif target:is_shared() then
+ program = _get_configured_program("sh")
+ else
+ program = _get_configured_program("cs")
+ end
+ if program then
+ return program
+ end
+
+ program = target:tool("cs")
+ if program and #program > 0 then
+ return program
+ end
+ return "dotnet"
+end
diff --git a/xmake/rules/csharp/install.lua b/xmake/rules/csharp/install.lua
new file mode 100644
index 000000000..97b3b1ee8
--- /dev/null
+++ b/xmake/rules/csharp/install.lua
@@ -0,0 +1,95 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author JassJam
+-- @file install.lua
+--
+
+local csharp_common = import("csharp_common", {anonymous = true})
+
+function main(target, opt)
+ local function _q(arg)
+ arg = tostring(arg)
+ if arg:find("[%s\"]") then
+ arg = "\"" .. arg:gsub("\"", "\\\"") .. "\""
+ end
+ return arg
+ end
+
+ local csprojfile = assert(csharp_common.find_csproj(target), "target(%s): missing csharp .csproj file!", target:name())
+ local dotnet = csharp_common.get_dotnet_program(target)
+ local configuration = csharp_common.build_mode_to_configuration()
+ local verbosity = csharp_common.get_dotnet_verbosity(target)
+
+ local install_path = target:installdir()
+ if target:is_binary() then
+ install_path = target:installdir("bin")
+ elseif target:is_static() or target:is_shared() then
+ install_path = target:installdir("lib")
+ end
+ if not install_path or #install_path == 0 then
+ return
+ end
+
+ local install_abs = path.is_absolute(install_path) and install_path or path.absolute(install_path, os.projectdir())
+ os.mkdir(install_abs)
+
+ local rid = csharp_common.get_runtime_identifier(target)
+ local argv = {
+ "publish", csprojfile,
+ "--nologo",
+ "--configuration", configuration,
+ "--verbosity", verbosity,
+ "--output", install_abs
+ }
+ if rid and target:is_binary() then
+ table.join2(argv, {"--runtime", rid})
+ end
+ csharp_common.append_target_flags(target, argv)
+
+ local runopt = csharp_common.get_dotnet_runopt(csprojfile)
+ if os.vrunv then
+ os.vrunv(dotnet, argv, runopt)
+ elseif os.runv then
+ os.runv(dotnet, argv, runopt)
+ elseif os.execv then
+ os.execv(dotnet, argv, runopt)
+ elseif os.vrun then
+ local cmd = _q(dotnet)
+ for _, arg in ipairs(argv) do
+ cmd = cmd .. " " .. _q(arg)
+ end
+ os.vrun(cmd, runopt)
+ elseif os.run then
+ local cmd = _q(dotnet)
+ for _, arg in ipairs(argv) do
+ cmd = cmd .. " " .. _q(arg)
+ end
+ os.run(cmd, runopt)
+ else
+ local targetdir = target:targetdir()
+ if targetdir and os.isdir(targetdir) then
+ os.cp(path.join(targetdir, "**"), install_abs, {rootdir = targetdir})
+ end
+ end
+
+ if target:is_binary() then
+ local targetdir = target:targetdir()
+ if targetdir and os.isdir(targetdir) then
+ os.cp(path.join(targetdir, "**"), install_abs, {rootdir = targetdir})
+ end
+ end
+end
diff --git a/xmake/rules/csharp/installcmd.lua b/xmake/rules/csharp/installcmd.lua
new file mode 100644
index 000000000..42733bfb0
--- /dev/null
+++ b/xmake/rules/csharp/installcmd.lua
@@ -0,0 +1,59 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author JassJam
+-- @file installcmd.lua
+--
+
+local csharp_common = import("csharp_common", {anonymous = true})
+
+function main(target, batchcmds, opt)
+ local csprojfile = assert(csharp_common.find_csproj(target), "target(%s): missing csharp .csproj file!", target:name())
+ local dotnet = csharp_common.get_dotnet_program(target)
+ local configuration = csharp_common.build_mode_to_configuration()
+ local verbosity = csharp_common.get_dotnet_verbosity(target)
+
+ local install_path = target:installdir()
+ if target:is_binary() then
+ install_path = target:installdir("bin")
+ elseif target:is_static() or target:is_shared() then
+ install_path = target:installdir("lib")
+ end
+
+ local install_abs
+ local argv = {
+ "publish", csprojfile,
+ "--nologo",
+ "--configuration", configuration,
+ "--verbosity", verbosity
+ }
+ if install_path and #install_path > 0 then
+ install_abs = path.is_absolute(install_path) and install_path or path.absolute(install_path, os.projectdir())
+ table.join2(argv, {"--output", install_abs})
+ end
+
+ local rid = csharp_common.get_runtime_identifier(target)
+ if rid and target:is_binary() then
+ table.join2(argv, {"--runtime", rid})
+ end
+ csharp_common.append_target_flags(target, argv)
+
+ batchcmds:show_progress(opt.progress, "${color.build.target}publishing.csharp.$(mode) %s", target:name())
+ if install_abs then
+ batchcmds:mkdir(install_abs)
+ end
+ batchcmds:vrunv(dotnet, argv, csharp_common.get_dotnet_runopt(csprojfile))
+end
diff --git a/xmake/rules/csharp/load.lua b/xmake/rules/csharp/load.lua
new file mode 100644
index 000000000..0811cf8d0
--- /dev/null
+++ b/xmake/rules/csharp/load.lua
@@ -0,0 +1,54 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author JassJam
+-- @file load.lua
+--
+
+function main(target)
+ local targetdir = target:targetdir()
+ if targetdir and path.filename(targetdir) ~= target:name() then
+ target:set("targetdir", path.join(targetdir, target:name()))
+ end
+
+ if target:is_static() or target:is_shared() then
+ if not target:get("extension") then
+ target:set("extension", ".dll")
+ end
+ if target:get("prefixname") == nil then
+ target:set("prefixname", "")
+ end
+ end
+
+ -- find and cache the .csproj file for later use in build/install
+ local csprojfiles = {}
+ for _, sourcefile in ipairs(target:sourcefiles()) do
+ if path.extension(sourcefile):lower() == ".csproj" then
+ table.insert(csprojfiles, sourcefile)
+ end
+ end
+ assert(#csprojfiles > 0, "target(%s): csharp requires one .csproj in add_files()!", target:name())
+ assert(#csprojfiles == 1, "target(%s): csharp only supports one .csproj file!", target:name())
+
+ local csprojfile = csprojfiles[1]
+ local csprojabs = path.is_absolute(csprojfile) and csprojfile or path.absolute(csprojfile, os.projectdir())
+ assert(os.isfile(csprojabs), "target(%s): csharp .csproj not found: %s", target:name(), csprojfile)
+
+ if not target:get("filename") and not target:get("basename") then
+ target:set("basename", path.basename(csprojabs))
+ end
+ target:data_set("csharp.csproj", csprojabs)
+end
diff --git a/xmake/rules/csharp/xmake.lua b/xmake/rules/csharp/xmake.lua
index 11aa82347..bc8a98023 100644
--- a/xmake/rules/csharp/xmake.lua
+++ b/xmake/rules/csharp/xmake.lua
@@ -18,364 +18,11 @@
-- @file xmake.lua
--
-rule("csharp.build")
+rule("csharp")
set_extensions(".cs", ".csproj")
set_sourcekinds("cs", "csproj", {objectfiles = false})
-
- local function _find_csproj(target)
- local csproj = target:data("csharp.csproj")
- if csproj then
- return csproj
- end
- for _, sourcefile in ipairs(target:sourcefiles()) do
- if path.extension(sourcefile):lower() == ".csproj" then
- local csprojabs = path.is_absolute(sourcefile) and sourcefile or path.absolute(sourcefile, os.projectdir())
- if os.isfile(csprojabs) then
- return csprojabs
- end
- end
- end
- return nil
- end
-
- local function _build_mode_to_configuration()
- local mode
- if type(get_config) == "function" then
- mode = get_config("mode")
- end
- if not mode and type(is_mode) == "function" then
- if is_mode("debug") then
- mode = "debug"
- elseif is_mode("release") then
- mode = "release"
- end
- end
- mode = mode or "release"
- local mode_lower = mode:lower()
- if mode_lower == "debug" then
- return "Debug"
- elseif mode_lower == "release" then
- return "Release"
- end
- return mode:sub(1, 1):upper() .. mode:sub(2)
- end
-
- local function _map_rid_arch(arch)
- arch = (arch or ""):lower()
- if arch == "x64" or arch == "x86_64" or arch == "amd64" then
- return "x64"
- elseif arch == "x86" or arch == "i386" then
- return "x86"
- elseif arch == "arm64" then
- return "arm64"
- elseif arch == "arm" or arch == "armv7" then
- return "arm"
- elseif arch == "riscv64" then
- return "riscv64"
- end
- return nil
- end
-
- local function _get_runtime_identifier(target)
- local rid = target:values("csharp.runtime_identifier")
- if type(rid) == "table" then
- rid = rid[1]
- end
- if rid and #rid > 0 then
- return rid
- end
- local arch = _map_rid_arch(target:arch())
- if not arch then
- return nil
- end
- local plat = target:plat()
- if plat == "windows" or plat == "mingw" or plat == "msys" or plat == "cygwin" then
- return "win-" .. arch
- elseif plat == "linux" then
- return "linux-" .. arch
- elseif plat == "macosx" then
- return "osx-" .. arch
- end
- return nil
- end
-
- local function _append_target_flags(target, argv)
- local flags = {}
- table.join2(flags, table.wrap(target:get("csflags")))
- table.join2(flags, table.wrap(target:get("ldflags")))
- table.join2(flags, table.wrap(target:get("arflags")))
- table.join2(flags, table.wrap(target:get("shflags")))
- for _, flag in ipairs(flags) do
- if flag and #flag > 0 then
- table.insert(argv, flag)
- end
- end
- end
-
- local function _get_dotnet_runopt(csprojfile)
- return {
- curdir = path.directory(csprojfile),
- envs = {
- DOTNET_NOLOGO = "1",
- DOTNET_CLI_TELEMETRY_OPTOUT = "1",
- DOTNET_SKIP_FIRST_TIME_EXPERIENCE = "1",
- DOTNET_GENERATE_ASPNET_CERTIFICATE = "0",
- DOTNET_ADD_GLOBAL_TOOLS_TO_PATH = "0"
- }
- }
- end
-
- local function _get_dotnet_verbosity(target)
- local verbosity = target:values("csharp.dotnet_verbosity")
- if type(verbosity) == "table" then
- verbosity = verbosity[1]
- end
- if not (verbosity and #verbosity > 0) then
- verbosity = target:policy("build.csharp.dotnet_verbosity")
- end
- if verbosity and #verbosity > 0 then
- verbosity = verbosity:lower()
- if table.contains({"quiet", "minimal", "normal", "detailed", "diagnostic"}, verbosity) then
- return verbosity
- end
- raise("target(%s): invalid csharp.dotnet_verbosity(%s), expected one of: quiet|minimal|normal|detailed|diagnostic", target:name(), verbosity)
- end
- return "quiet"
- end
-
- local function _get_dotnet_program(target)
- local function _get_configured_program(toolkind)
- if target:get("toolset." .. toolkind) then
- local program = target:tool(toolkind)
- if program and #program > 0 then
- return program
- end
- end
- end
-
- local program = nil
- if target:is_binary() then
- program = _get_configured_program("ld")
- elseif target:is_shared() then
- program = _get_configured_program("sh")
- else
- program = _get_configured_program("cs")
- end
- if program then
- return program
- end
-
- program = target:tool("cs")
- if program and #program > 0 then
- return program
- end
- return "dotnet"
- end
-
- on_load(function (target)
- local targetdir = target:targetdir()
-
- if targetdir and path.filename(targetdir) ~= target:name() then
- target:set("targetdir", path.join(targetdir, target:name()))
- end
-
- if target:is_static() or target:is_shared() then
- if not target:get("extension") then
- target:set("extension", ".dll")
- end
- if target:get("prefixname") == nil then
- target:set("prefixname", "")
- end
- end
-
- -- find and cache the .csproj file for later use in build/install
- local csprojfiles = {}
- for _, sourcefile in ipairs(target:sourcefiles()) do
- if path.extension(sourcefile):lower() == ".csproj" then
- table.insert(csprojfiles, sourcefile)
- end
- end
- assert(#csprojfiles > 0, "target(%s): csharp requires one .csproj in add_files()!", target:name())
- assert(#csprojfiles == 1, "target(%s): csharp only supports one .csproj file!", target:name())
-
- local csprojfile = csprojfiles[1]
- local csprojabs = path.is_absolute(csprojfile) and csprojfile or path.absolute(csprojfile, os.projectdir())
- assert(os.isfile(csprojabs), "target(%s): csharp .csproj not found: %s", target:name(), csprojfile)
-
- if not target:get("filename") and not target:get("basename") then
- target:set("basename", path.basename(csprojabs))
- end
- target:data_set("csharp.csproj", csprojabs)
- end)
-
- on_buildcmd(function (target, batchcmds, opt)
- local csprojfile = assert(_find_csproj(target), "target(%s): missing csharp .csproj file!", target:name())
- local dotnet = _get_dotnet_program(target)
- local configuration = _build_mode_to_configuration()
- local verbosity = _get_dotnet_verbosity(target)
- local command = target:is_binary() and "publish" or "build"
- local argv = {
- command, csprojfile,
- "--nologo",
- "--configuration", configuration,
- "--verbosity", verbosity
- }
-
- local targetdir = target:targetdir()
- local targetdirabs
- if targetdir then
- targetdirabs = path.is_absolute(targetdir) and targetdir or path.absolute(targetdir, os.projectdir())
- table.join2(argv, {"--output", targetdirabs})
- end
-
- local rid = _get_runtime_identifier(target)
- if rid and target:is_binary() then
- table.join2(argv, {"--runtime", rid})
- end
- _append_target_flags(target, argv)
-
- batchcmds:show_progress(opt.progress, "${color.build.target}building.csharp.$(mode) %s", target:name())
- if targetdirabs then
- batchcmds:mkdir(targetdirabs)
- end
-
- batchcmds:vrunv(dotnet, argv, _get_dotnet_runopt(csprojfile))
-
- local targetfile = target:targetfile()
- if targetfile then
- local sourcefiles = target:sourcefiles()
- batchcmds:add_depfiles(sourcefiles)
- batchcmds:set_depmtime(os.mtime(targetfile))
- batchcmds:set_depcache(target:dependfile(targetfile))
- end
- end)
-
- on_clean(function (target, opt)
- local csprojfile = _find_csproj(target)
- if csprojfile then
- local csprojdir = path.directory(csprojfile)
- os.tryrm(path.join(csprojdir, "bin"))
- os.tryrm(path.join(csprojdir, "obj"))
- end
- end)
-
- on_install(function (target, opt)
- local function _q(arg)
- arg = tostring(arg)
- if arg:find("[%s\"]") then
- arg = "\"" .. arg:gsub("\"", "\\\"") .. "\""
- end
- return arg
- end
-
- local csprojfile = assert(_find_csproj(target), "target(%s): missing csharp .csproj file!", target:name())
- local dotnet = _get_dotnet_program(target)
- local configuration = _build_mode_to_configuration()
- local verbosity = _get_dotnet_verbosity(target)
-
- local install_path = target:installdir()
- if target:is_binary() then
- install_path = target:installdir("bin")
- elseif target:is_static() or target:is_shared() then
- install_path = target:installdir("lib")
- end
-
- if not install_path or #install_path == 0 then
- return
- end
-
- local install_abs = path.is_absolute(install_path) and install_path or path.absolute(install_path, os.projectdir())
- os.mkdir(install_abs)
-
- local rid = _get_runtime_identifier(target)
- local argv = {
- "publish", csprojfile,
- "--nologo",
- "--configuration", configuration,
- "--verbosity", verbosity,
- "--output", install_abs
- }
-
- if rid and target:is_binary() then
- table.join2(argv, {"--runtime", rid})
- end
- _append_target_flags(target, argv)
-
- local runopt = _get_dotnet_runopt(csprojfile)
- if os.vrunv then
- os.vrunv(dotnet, argv, runopt)
- elseif os.runv then
- os.runv(dotnet, argv, runopt)
- elseif os.execv then
- os.execv(dotnet, argv, runopt)
- elseif os.vrun then
- local cmd = _q(dotnet)
- for _, arg in ipairs(argv) do
- cmd = cmd .. " " .. _q(arg)
- end
- os.vrun(
- cmd,
- runopt
- )
- elseif os.run then
- local cmd = _q(dotnet)
- for _, arg in ipairs(argv) do
- cmd = cmd .. " " .. _q(arg)
- end
- os.run(cmd, runopt)
- else
- local targetdir = target:targetdir()
- if targetdir and os.isdir(targetdir) then
- os.cp(path.join(targetdir, "**"), install_abs, {rootdir = targetdir})
- end
- end
-
- if target:is_binary() then
- local targetdir = target:targetdir()
- if targetdir and os.isdir(targetdir) then
- os.cp(path.join(targetdir, "**"), install_abs, {rootdir = targetdir})
- end
- end
- end)
-
- on_installcmd(function (target, batchcmds, opt)
- local csprojfile = assert(_find_csproj(target), "target(%s): missing csharp .csproj file!", target:name())
- local dotnet = _get_dotnet_program(target)
- local configuration = _build_mode_to_configuration()
- local verbosity = _get_dotnet_verbosity(target)
-
- local install_path = target:installdir()
- if target:is_binary() then
- install_path = target:installdir("bin")
- elseif target:is_static() or target:is_shared() then
- install_path = target:installdir("lib")
- end
-
- local install_abs
- local argv = {
- "publish", csprojfile,
- "--nologo",
- "--configuration", configuration,
- "--verbosity", verbosity
- }
- if install_path and #install_path > 0 then
- install_abs = path.is_absolute(install_path) and install_path or path.absolute(install_path, os.projectdir())
- table.join2(argv, {"--output", install_abs})
- end
-
- local rid = _get_runtime_identifier(target)
- if rid and target:is_binary() then
- table.join2(argv, {"--runtime", rid})
- end
- _append_target_flags(target, argv)
-
- batchcmds:show_progress(opt.progress, "${color.build.target}publishing.csharp.$(mode) %s", target:name())
- if install_abs then
- batchcmds:mkdir(install_abs)
- end
-
- batchcmds:vrunv(dotnet, argv, _get_dotnet_runopt(csprojfile))
- end)
-
-rule("csharp")
- add_deps("csharp.build")
+ on_load("load")
+ on_buildcmd("buildcmd")
+ on_clean("clean")
+ on_install("install")
+ on_installcmd("installcmd")
--
cgit v1.3.1
From f50b335260956986a7dc9e476d72e2ab95a70002 Mon Sep 17 00:00:00 2001
From: JassJam <0139ne@gmail.com>
Date: Tue, 24 Feb 2026 18:07:52 +0000
Subject: feat: add documentation for C# build rule usage
---
xmake/rules/csharp/xmake.lua | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/xmake/rules/csharp/xmake.lua b/xmake/rules/csharp/xmake.lua
index bc8a98023..778ea4fc4 100644
--- a/xmake/rules/csharp/xmake.lua
+++ b/xmake/rules/csharp/xmake.lua
@@ -18,6 +18,21 @@
-- @file xmake.lua
--
+-- define rule: build csharp target with dotnet
+--
+-- @code
+-- target("app")
+-- set_kind("binary") -- or "shared"
+-- add_rules("csharp")
+-- add_files("src/*.cs", "src/app.csproj")
+--
+-- -- optional: override runtime identifier (arch-specific) for self-contained publish (e.g. "win-x64", "linux-x64", "osx-x64", "osx-arm64")
+-- -- add_values("csharp.runtime_identifier", "win-x64")
+--
+-- -- optional: dotnet verbosity (quiet|minimal|normal|detailed|diagnostic)
+-- -- set_policy("build.csharp.dotnet_verbosity", "minimal")
+-- @endcode
+--
rule("csharp")
set_extensions(".cs", ".csproj")
set_sourcekinds("cs", "csproj", {objectfiles = false})
--
cgit v1.3.1
From 3cf64d9220b3c581becd99a8f6cb316462f1bad5 Mon Sep 17 00:00:00 2001
From: JassJam <0139ne@gmail.com>
Date: Wed, 25 Feb 2026 09:52:12 +0000
Subject: refactor: update import statements
---
xmake/rules/csharp/buildcmd.lua | 2 +-
xmake/rules/csharp/clean.lua | 2 +-
xmake/rules/csharp/csharp_common.lua | 2 +-
xmake/rules/csharp/install.lua | 2 +-
xmake/rules/csharp/installcmd.lua | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/xmake/rules/csharp/buildcmd.lua b/xmake/rules/csharp/buildcmd.lua
index c79ae65f4..209c76027 100644
--- a/xmake/rules/csharp/buildcmd.lua
+++ b/xmake/rules/csharp/buildcmd.lua
@@ -18,7 +18,7 @@
-- @file buildcmd.lua
--
-local csharp_common = import("csharp_common", {anonymous = true})
+import("csharp_common")
function main(target, batchcmds, opt)
local csprojfile = assert(csharp_common.find_csproj(target), "target(%s): missing csharp .csproj file!", target:name())
diff --git a/xmake/rules/csharp/clean.lua b/xmake/rules/csharp/clean.lua
index 6de720709..863e97b50 100644
--- a/xmake/rules/csharp/clean.lua
+++ b/xmake/rules/csharp/clean.lua
@@ -19,7 +19,7 @@
--
import("target.action.clean", {alias = "_do_clean_target"})
-local csharp_common = import("csharp_common", {anonymous = true})
+import("csharp_common")
function main(target, opt)
_do_clean_target(target)
diff --git a/xmake/rules/csharp/csharp_common.lua b/xmake/rules/csharp/csharp_common.lua
index e6f2bc7c3..ddfa241bc 100644
--- a/xmake/rules/csharp/csharp_common.lua
+++ b/xmake/rules/csharp/csharp_common.lua
@@ -18,7 +18,7 @@
-- @file csharp_common.lua
--
-local function _map_rid_arch(arch)
+function _map_rid_arch(arch)
arch = (arch or ""):lower()
if arch == "x64" or arch == "x86_64" or arch == "amd64" then
return "x64"
diff --git a/xmake/rules/csharp/install.lua b/xmake/rules/csharp/install.lua
index 97b3b1ee8..05d48e2c5 100644
--- a/xmake/rules/csharp/install.lua
+++ b/xmake/rules/csharp/install.lua
@@ -18,7 +18,7 @@
-- @file install.lua
--
-local csharp_common = import("csharp_common", {anonymous = true})
+import("csharp_common")
function main(target, opt)
local function _q(arg)
diff --git a/xmake/rules/csharp/installcmd.lua b/xmake/rules/csharp/installcmd.lua
index 42733bfb0..c483eb432 100644
--- a/xmake/rules/csharp/installcmd.lua
+++ b/xmake/rules/csharp/installcmd.lua
@@ -18,7 +18,7 @@
-- @file installcmd.lua
--
-local csharp_common = import("csharp_common", {anonymous = true})
+import("csharp_common")
function main(target, batchcmds, opt)
local csprojfile = assert(csharp_common.find_csproj(target), "target(%s): missing csharp .csproj file!", target:name())
--
cgit v1.3.1
From a35ec2dcea9ca5c639ca9d1332a46ede16b959c0 Mon Sep 17 00:00:00 2001
From: JassJam <0139ne@gmail.com>
Date: Wed, 25 Feb 2026 09:55:53 +0000
Subject: refactor: update dotnet verbosity handling in C# rules, dont add a
new policy
---
xmake/core/project/policy.lua | 2 --
xmake/rules/csharp/buildcmd.lua | 2 +-
xmake/rules/csharp/csharp_common.lua | 19 +++++--------------
xmake/rules/csharp/install.lua | 2 +-
xmake/rules/csharp/installcmd.lua | 2 +-
xmake/rules/csharp/xmake.lua | 3 ---
6 files changed, 8 insertions(+), 22 deletions(-)
diff --git a/xmake/core/project/policy.lua b/xmake/core/project/policy.lua
index 09751ba72..0cf184d91 100644
--- a/xmake/core/project/policy.lua
+++ b/xmake/core/project/policy.lua
@@ -125,8 +125,6 @@ function policy.policies()
["build.progress_style"] = {description = "Set the build progress output style.", type = "string", values = {"scroll", "singlerow", "multirow"}},
-- Show target name in build progress output
["build.progress_show_target"] = {description = "Show target name in build progress output.", default = true, type = "boolean"},
- -- Set dotnet verbosity for csharp rule, e.g. quiet|minimal|normal|detailed|diagnostic
- ["build.csharp.dotnet_verbosity"] = {description = "Set dotnet verbosity for csharp rule.", type = "string", values = {"quiet", "minimal", "normal", "detailed", "diagnostic"}},
-- Enable windows UAC and set level, e.g. invoker, admin, highest
["windows.manifest.uac"] = {description = "Enable windows manifest UAC.", type = "string"},
-- Enable ui access for windows UAC
diff --git a/xmake/rules/csharp/buildcmd.lua b/xmake/rules/csharp/buildcmd.lua
index 209c76027..e3c45ace8 100644
--- a/xmake/rules/csharp/buildcmd.lua
+++ b/xmake/rules/csharp/buildcmd.lua
@@ -24,7 +24,7 @@ function main(target, batchcmds, opt)
local csprojfile = assert(csharp_common.find_csproj(target), "target(%s): missing csharp .csproj file!", target:name())
local dotnet = csharp_common.get_dotnet_program(target)
local configuration = csharp_common.build_mode_to_configuration()
- local verbosity = csharp_common.get_dotnet_verbosity(target)
+ local verbosity = csharp_common.get_dotnet_verbosity()
local command = target:is_binary() and "publish" or "build"
local argv = {
command, csprojfile,
diff --git a/xmake/rules/csharp/csharp_common.lua b/xmake/rules/csharp/csharp_common.lua
index ddfa241bc..1df548c60 100644
--- a/xmake/rules/csharp/csharp_common.lua
+++ b/xmake/rules/csharp/csharp_common.lua
@@ -18,6 +18,8 @@
-- @file csharp_common.lua
--
+import("core.base.option")
+
function _map_rid_arch(arch)
arch = (arch or ""):lower()
if arch == "x64" or arch == "x86_64" or arch == "amd64" then
@@ -121,20 +123,9 @@ function get_dotnet_runopt(csprojfile)
}
end
-function get_dotnet_verbosity(target)
- local verbosity = target:values("csharp.dotnet_verbosity")
- if type(verbosity) == "table" then
- verbosity = verbosity[1]
- end
- if not (verbosity and #verbosity > 0) then
- verbosity = target:policy("build.csharp.dotnet_verbosity")
- end
- if verbosity and #verbosity > 0 then
- verbosity = verbosity:lower()
- if table.contains({"quiet", "minimal", "normal", "detailed", "diagnostic"}, verbosity) then
- return verbosity
- end
- raise("target(%s): invalid csharp.dotnet_verbosity(%s), expected one of: quiet|minimal|normal|detailed|diagnostic", target:name(), verbosity)
+function get_dotnet_verbosity()
+ if option.get("diagnosis") then
+ return "diagnostic"
end
return "quiet"
end
diff --git a/xmake/rules/csharp/install.lua b/xmake/rules/csharp/install.lua
index 05d48e2c5..34109a054 100644
--- a/xmake/rules/csharp/install.lua
+++ b/xmake/rules/csharp/install.lua
@@ -32,7 +32,7 @@ function main(target, opt)
local csprojfile = assert(csharp_common.find_csproj(target), "target(%s): missing csharp .csproj file!", target:name())
local dotnet = csharp_common.get_dotnet_program(target)
local configuration = csharp_common.build_mode_to_configuration()
- local verbosity = csharp_common.get_dotnet_verbosity(target)
+ local verbosity = csharp_common.get_dotnet_verbosity()
local install_path = target:installdir()
if target:is_binary() then
diff --git a/xmake/rules/csharp/installcmd.lua b/xmake/rules/csharp/installcmd.lua
index c483eb432..76c051a59 100644
--- a/xmake/rules/csharp/installcmd.lua
+++ b/xmake/rules/csharp/installcmd.lua
@@ -24,7 +24,7 @@ function main(target, batchcmds, opt)
local csprojfile = assert(csharp_common.find_csproj(target), "target(%s): missing csharp .csproj file!", target:name())
local dotnet = csharp_common.get_dotnet_program(target)
local configuration = csharp_common.build_mode_to_configuration()
- local verbosity = csharp_common.get_dotnet_verbosity(target)
+ local verbosity = csharp_common.get_dotnet_verbosity()
local install_path = target:installdir()
if target:is_binary() then
diff --git a/xmake/rules/csharp/xmake.lua b/xmake/rules/csharp/xmake.lua
index 778ea4fc4..5d358e099 100644
--- a/xmake/rules/csharp/xmake.lua
+++ b/xmake/rules/csharp/xmake.lua
@@ -28,9 +28,6 @@
--
-- -- optional: override runtime identifier (arch-specific) for self-contained publish (e.g. "win-x64", "linux-x64", "osx-x64", "osx-arm64")
-- -- add_values("csharp.runtime_identifier", "win-x64")
---
--- -- optional: dotnet verbosity (quiet|minimal|normal|detailed|diagnostic)
--- -- set_policy("build.csharp.dotnet_verbosity", "minimal")
-- @endcode
--
rule("csharp")
--
cgit v1.3.1
From a92e7c4087443cd91dafe14d2a5aa316a288b7c5 Mon Sep 17 00:00:00 2001
From: JassJam <0139ne@gmail.com>
Date: Sat, 28 Feb 2026 15:37:56 +0000
Subject: feat: generate csproj on demand
---
tests/projects/csharp/console/src/test.csproj | 9 -
tests/projects/csharp/console/xmake.lua | 2 +-
.../console_with_runtime_json/src/app.csproj | 14 -
.../csharp/console_with_runtime_json/xmake.lua | 2 +-
.../csharp/exe_with_library/src/app/app.csproj | 12 -
.../csharp/exe_with_library/src/lib/lib.csproj | 8 -
tests/projects/csharp/exe_with_library/xmake.lua | 4 +-
.../csharp/exe_with_package/src/app.csproj | 12 -
tests/projects/csharp/exe_with_package/xmake.lua | 4 +-
tests/projects/csharp/existing_csproj/.gitignore | 6 +
tests/projects/csharp/existing_csproj/Program.cs | 2 +
.../csharp/existing_csproj/existing_csproj.csproj | 10 +
tests/projects/csharp/existing_csproj/test.lua | 10 +
tests/projects/csharp/existing_csproj/xmake.lua | 6 +
.../multiple_library/src/libalpha/libalpha.csproj | 8 -
.../multiple_library/src/libbeta/libbeta.csproj | 11 -
.../multiple_library/src/sample/sample.csproj | 14 -
tests/projects/csharp/multiple_library/xmake.lua | 6 +-
.../projects/csharp/web_project/src/webapp.csproj | 8 -
tests/projects/csharp/web_project/xmake.lua | 3 +-
xmake/rules/csharp/buildcmd.lua | 36 ++-
xmake/rules/csharp/clean.lua | 2 +-
xmake/rules/csharp/csharp_common.lua | 160 -----------
xmake/rules/csharp/install.lua | 4 +-
xmake/rules/csharp/installcmd.lua | 4 +-
xmake/rules/csharp/load.lua | 21 +-
xmake/rules/csharp/modules/csharp_common.lua | 232 ++++++++++++++++
xmake/rules/csharp/modules/csproj_generator.lua | 301 ++++++++++++++++++++
xmake/rules/csharp/modules/itemgroups.lua | 166 +++++++++++
xmake/rules/csharp/modules/properties.lua | 307 +++++++++++++++++++++
xmake/rules/csharp/xmake.lua | 83 +++++-
31 files changed, 1184 insertions(+), 283 deletions(-)
delete mode 100644 tests/projects/csharp/console/src/test.csproj
delete mode 100644 tests/projects/csharp/console_with_runtime_json/src/app.csproj
delete mode 100644 tests/projects/csharp/exe_with_library/src/app/app.csproj
delete mode 100644 tests/projects/csharp/exe_with_library/src/lib/lib.csproj
delete mode 100644 tests/projects/csharp/exe_with_package/src/app.csproj
create mode 100644 tests/projects/csharp/existing_csproj/.gitignore
create mode 100644 tests/projects/csharp/existing_csproj/Program.cs
create mode 100644 tests/projects/csharp/existing_csproj/existing_csproj.csproj
create mode 100644 tests/projects/csharp/existing_csproj/test.lua
create mode 100644 tests/projects/csharp/existing_csproj/xmake.lua
delete mode 100644 tests/projects/csharp/multiple_library/src/libalpha/libalpha.csproj
delete mode 100644 tests/projects/csharp/multiple_library/src/libbeta/libbeta.csproj
delete mode 100644 tests/projects/csharp/multiple_library/src/sample/sample.csproj
delete mode 100644 tests/projects/csharp/web_project/src/webapp.csproj
delete mode 100644 xmake/rules/csharp/csharp_common.lua
create mode 100644 xmake/rules/csharp/modules/csharp_common.lua
create mode 100644 xmake/rules/csharp/modules/csproj_generator.lua
create mode 100644 xmake/rules/csharp/modules/itemgroups.lua
create mode 100644 xmake/rules/csharp/modules/properties.lua
diff --git a/tests/projects/csharp/console/src/test.csproj b/tests/projects/csharp/console/src/test.csproj
deleted file mode 100644
index 9dbab4973..000000000
--- a/tests/projects/csharp/console/src/test.csproj
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
- Exe
- net8.0
- enable
- enable
-
-
-
diff --git a/tests/projects/csharp/console/xmake.lua b/tests/projects/csharp/console/xmake.lua
index 25cd801f0..3288c96a8 100644
--- a/tests/projects/csharp/console/xmake.lua
+++ b/tests/projects/csharp/console/xmake.lua
@@ -3,4 +3,4 @@ add_rules("mode.debug", "mode.release")
target("test")
set_kind("binary")
add_rules("csharp")
- add_files("src/Program.cs", "src/test.csproj")
+ add_files("src/Program.cs")
diff --git a/tests/projects/csharp/console_with_runtime_json/src/app.csproj b/tests/projects/csharp/console_with_runtime_json/src/app.csproj
deleted file mode 100644
index 4e754bb15..000000000
--- a/tests/projects/csharp/console_with_runtime_json/src/app.csproj
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
- Exe
- net8.0
- enable
- enable
-
-
-
- PreserveNewest
-
-
-
-
diff --git a/tests/projects/csharp/console_with_runtime_json/xmake.lua b/tests/projects/csharp/console_with_runtime_json/xmake.lua
index 8d8539b26..474eda548 100644
--- a/tests/projects/csharp/console_with_runtime_json/xmake.lua
+++ b/tests/projects/csharp/console_with_runtime_json/xmake.lua
@@ -3,4 +3,4 @@ add_rules("mode.debug", "mode.release")
target("app")
set_kind("binary")
add_rules("csharp")
- add_files("src/Program.cs", "src/app.csproj")
+ add_files("src/Program.cs")
diff --git a/tests/projects/csharp/exe_with_library/src/app/app.csproj b/tests/projects/csharp/exe_with_library/src/app/app.csproj
deleted file mode 100644
index e5e49a685..000000000
--- a/tests/projects/csharp/exe_with_library/src/app/app.csproj
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
- Exe
- net8.0
- enable
- enable
-
-
-
-
-
-
diff --git a/tests/projects/csharp/exe_with_library/src/lib/lib.csproj b/tests/projects/csharp/exe_with_library/src/lib/lib.csproj
deleted file mode 100644
index 30c56ecb5..000000000
--- a/tests/projects/csharp/exe_with_library/src/lib/lib.csproj
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
- net8.0
- enable
- enable
-
-
-
diff --git a/tests/projects/csharp/exe_with_library/xmake.lua b/tests/projects/csharp/exe_with_library/xmake.lua
index 063e051db..f8549444e 100644
--- a/tests/projects/csharp/exe_with_library/xmake.lua
+++ b/tests/projects/csharp/exe_with_library/xmake.lua
@@ -3,10 +3,10 @@ add_rules("mode.debug", "mode.release")
target("mylib")
set_kind("shared")
add_rules("csharp")
- add_files("src/lib/*.cs", "src/lib/lib.csproj")
+ add_files("src/lib/*.cs")
target("app")
set_kind("binary")
add_rules("csharp")
add_deps("mylib")
- add_files("src/app/*.cs", "src/app/app.csproj")
+ add_files("src/app/*.cs")
diff --git a/tests/projects/csharp/exe_with_package/src/app.csproj b/tests/projects/csharp/exe_with_package/src/app.csproj
deleted file mode 100644
index 0521e2a0c..000000000
--- a/tests/projects/csharp/exe_with_package/src/app.csproj
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
- Exe
- net8.0
- enable
- enable
-
-
-
-
-
-
diff --git a/tests/projects/csharp/exe_with_package/xmake.lua b/tests/projects/csharp/exe_with_package/xmake.lua
index 8d8539b26..6ec474822 100644
--- a/tests/projects/csharp/exe_with_package/xmake.lua
+++ b/tests/projects/csharp/exe_with_package/xmake.lua
@@ -1,6 +1,8 @@
add_rules("mode.debug", "mode.release")
+add_requires("nuget::Humanizer.Core 2.14.1")
target("app")
set_kind("binary")
add_rules("csharp")
- add_files("src/Program.cs", "src/app.csproj")
+ add_files("src/Program.cs")
+ add_packages("nuget::Humanizer.Core")
diff --git a/tests/projects/csharp/existing_csproj/.gitignore b/tests/projects/csharp/existing_csproj/.gitignore
new file mode 100644
index 000000000..6b8d3af38
--- /dev/null
+++ b/tests/projects/csharp/existing_csproj/.gitignore
@@ -0,0 +1,6 @@
+.xmake/
+build/
+bin/
+obj/
+vsxmake*/
+.vs/
diff --git a/tests/projects/csharp/existing_csproj/Program.cs b/tests/projects/csharp/existing_csproj/Program.cs
new file mode 100644
index 000000000..3751555cb
--- /dev/null
+++ b/tests/projects/csharp/existing_csproj/Program.cs
@@ -0,0 +1,2 @@
+// See https://aka.ms/new-console-template for more information
+Console.WriteLine("Hello, World!");
diff --git a/tests/projects/csharp/existing_csproj/existing_csproj.csproj b/tests/projects/csharp/existing_csproj/existing_csproj.csproj
new file mode 100644
index 000000000..ed9781c22
--- /dev/null
+++ b/tests/projects/csharp/existing_csproj/existing_csproj.csproj
@@ -0,0 +1,10 @@
+
+
+
+ Exe
+ net10.0
+ enable
+ enable
+
+
+
diff --git a/tests/projects/csharp/existing_csproj/test.lua b/tests/projects/csharp/existing_csproj/test.lua
new file mode 100644
index 000000000..5ab8adc69
--- /dev/null
+++ b/tests/projects/csharp/existing_csproj/test.lua
@@ -0,0 +1,10 @@
+import("lib.detect.find_tool")
+
+function test_build(t)
+ local dotnet = find_tool("dotnet")
+ if dotnet then
+ t:build()
+ else
+ return t:skip("dotnet not found")
+ end
+end
diff --git a/tests/projects/csharp/existing_csproj/xmake.lua b/tests/projects/csharp/existing_csproj/xmake.lua
new file mode 100644
index 000000000..95e5e45d9
--- /dev/null
+++ b/tests/projects/csharp/existing_csproj/xmake.lua
@@ -0,0 +1,6 @@
+add_rules("mode.debug", "mode.release")
+
+target("test")
+ set_kind("binary")
+ add_rules("csharp")
+ add_files("existing_csproj.csproj")
diff --git a/tests/projects/csharp/multiple_library/src/libalpha/libalpha.csproj b/tests/projects/csharp/multiple_library/src/libalpha/libalpha.csproj
deleted file mode 100644
index 30c56ecb5..000000000
--- a/tests/projects/csharp/multiple_library/src/libalpha/libalpha.csproj
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
- net8.0
- enable
- enable
-
-
-
diff --git a/tests/projects/csharp/multiple_library/src/libbeta/libbeta.csproj b/tests/projects/csharp/multiple_library/src/libbeta/libbeta.csproj
deleted file mode 100644
index 15502d321..000000000
--- a/tests/projects/csharp/multiple_library/src/libbeta/libbeta.csproj
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
- net8.0
- enable
- enable
-
-
-
-
-
-
diff --git a/tests/projects/csharp/multiple_library/src/sample/sample.csproj b/tests/projects/csharp/multiple_library/src/sample/sample.csproj
deleted file mode 100644
index 459440d0e..000000000
--- a/tests/projects/csharp/multiple_library/src/sample/sample.csproj
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
- Exe
- net8.0
- enable
- enable
-
-
-
-
-
-
-
diff --git a/tests/projects/csharp/multiple_library/xmake.lua b/tests/projects/csharp/multiple_library/xmake.lua
index b4be98c99..db3f65811 100644
--- a/tests/projects/csharp/multiple_library/xmake.lua
+++ b/tests/projects/csharp/multiple_library/xmake.lua
@@ -3,16 +3,16 @@ add_rules("mode.debug", "mode.release")
target("libalpha")
set_kind("shared")
add_rules("csharp")
- add_files("src/libalpha/*.cs", "src/libalpha/libalpha.csproj")
+ add_files("src/libalpha/*.cs")
target("libbeta")
set_kind("shared")
add_rules("csharp")
add_deps("libalpha")
- add_files("src/libbeta/*.cs", "src/libbeta/libbeta.csproj")
+ add_files("src/libbeta/*.cs")
target("sample")
set_kind("binary")
add_rules("csharp")
add_deps("libbeta")
- add_files("src/sample/*.cs", "src/sample/sample.csproj")
+ add_files("src/sample/*.cs")
diff --git a/tests/projects/csharp/web_project/src/webapp.csproj b/tests/projects/csharp/web_project/src/webapp.csproj
deleted file mode 100644
index 4715481ee..000000000
--- a/tests/projects/csharp/web_project/src/webapp.csproj
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
- net8.0
- enable
- enable
-
-
-
diff --git a/tests/projects/csharp/web_project/xmake.lua b/tests/projects/csharp/web_project/xmake.lua
index d1e5504c3..56bcad79c 100644
--- a/tests/projects/csharp/web_project/xmake.lua
+++ b/tests/projects/csharp/web_project/xmake.lua
@@ -3,4 +3,5 @@ add_rules("mode.debug", "mode.release")
target("webapp")
set_kind("binary")
add_rules("csharp")
- add_files("src/Program.cs", "src/webapp.csproj")
+ add_values("csharp.sdk", "Microsoft.NET.Sdk.Web")
+ add_files("src/Program.cs")
diff --git a/xmake/rules/csharp/buildcmd.lua b/xmake/rules/csharp/buildcmd.lua
index e3c45ace8..b8f85360a 100644
--- a/xmake/rules/csharp/buildcmd.lua
+++ b/xmake/rules/csharp/buildcmd.lua
@@ -18,10 +18,35 @@
-- @file buildcmd.lua
--
-import("csharp_common")
+import("modules.csharp_common", {rootdir = os.scriptdir(), alias = "csharp_common"})
+
+local function _get_output_mtime(target, targetfile, targetdirabs)
+ local mtime = targetfile and os.mtime(targetfile) or nil
+ if mtime then
+ return mtime
+ end
+
+ if targetdirabs and os.isdir(targetdirabs) then
+ -- dotnet can place outputs under tfm/rid subdirectories depending on
+ -- project properties, so scan recursively for a matching assembly name.
+ local basename = target:basename() or target:name()
+ for _, ext in ipairs({".exe", ".dll", ".so", ".dylib"}) do
+ for _, file in ipairs(os.files(path.join(targetdirabs, "**", basename .. ext))) do
+ local filemtime = os.mtime(file)
+ if filemtime and (not mtime or filemtime > mtime) then
+ mtime = filemtime
+ end
+ end
+ end
+ if not mtime then
+ mtime = os.mtime(targetdirabs)
+ end
+ end
+ return mtime
+end
function main(target, batchcmds, opt)
- local csprojfile = assert(csharp_common.find_csproj(target), "target(%s): missing csharp .csproj file!", target:name())
+ local csprojfile = assert(csharp_common.find_or_generate_csproj(target), "target(%s): missing csharp .csproj file!", target:name())
local dotnet = csharp_common.get_dotnet_program(target)
local configuration = csharp_common.build_mode_to_configuration()
local verbosity = csharp_common.get_dotnet_verbosity()
@@ -54,9 +79,10 @@ function main(target, batchcmds, opt)
local targetfile = target:targetfile()
if targetfile then
- local sourcefiles = target:sourcefiles()
- batchcmds:add_depfiles(sourcefiles)
- batchcmds:set_depmtime(os.mtime(targetfile))
+ local depfiles = table.wrap(target:sourcefiles())
+ table.insert(depfiles, csprojfile)
+ batchcmds:add_depfiles(table.unique(depfiles))
+ batchcmds:set_depmtime(_get_output_mtime(target, targetfile, targetdirabs))
batchcmds:set_depcache(target:dependfile(targetfile))
end
end
diff --git a/xmake/rules/csharp/clean.lua b/xmake/rules/csharp/clean.lua
index 863e97b50..ce683268c 100644
--- a/xmake/rules/csharp/clean.lua
+++ b/xmake/rules/csharp/clean.lua
@@ -19,7 +19,7 @@
--
import("target.action.clean", {alias = "_do_clean_target"})
-import("csharp_common")
+import("modules.csharp_common", {rootdir = os.scriptdir(), alias = "csharp_common"})
function main(target, opt)
_do_clean_target(target)
diff --git a/xmake/rules/csharp/csharp_common.lua b/xmake/rules/csharp/csharp_common.lua
deleted file mode 100644
index 1df548c60..000000000
--- a/xmake/rules/csharp/csharp_common.lua
+++ /dev/null
@@ -1,160 +0,0 @@
---!A cross-platform build utility based on Lua
---
--- Licensed under the Apache License, Version 2.0 (the "License");
--- you may not use this file except in compliance with the License.
--- You may obtain a copy of the License at
---
--- http://www.apache.org/licenses/LICENSE-2.0
---
--- Unless required by applicable law or agreed to in writing, software
--- distributed under the License is distributed on an "AS IS" BASIS,
--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--- See the License for the specific language governing permissions and
--- limitations under the License.
---
--- Copyright (C) 2015-present, Xmake Open Source Community.
---
--- @author JassJam
--- @file csharp_common.lua
---
-
-import("core.base.option")
-
-function _map_rid_arch(arch)
- arch = (arch or ""):lower()
- if arch == "x64" or arch == "x86_64" or arch == "amd64" then
- return "x64"
- elseif arch == "x86" or arch == "i386" then
- return "x86"
- elseif arch == "arm64" then
- return "arm64"
- elseif arch == "arm" or arch == "armv7" then
- return "arm"
- elseif arch == "riscv64" then
- return "riscv64"
- end
- return nil
-end
-
-function find_csproj(target)
- local csproj = target:data("csharp.csproj")
- if csproj then
- return csproj
- end
- for _, sourcefile in ipairs(target:sourcefiles()) do
- if path.extension(sourcefile):lower() == ".csproj" then
- local csprojabs = path.is_absolute(sourcefile) and sourcefile or path.absolute(sourcefile, os.projectdir())
- if os.isfile(csprojabs) then
- return csprojabs
- end
- end
- end
- return nil
-end
-
-function build_mode_to_configuration()
- local mode
- if type(get_config) == "function" then
- mode = get_config("mode")
- end
- if not mode and type(is_mode) == "function" then
- if is_mode("debug") then
- mode = "debug"
- elseif is_mode("release") then
- mode = "release"
- end
- end
- mode = mode or "release"
- local mode_lower = mode:lower()
- if mode_lower == "debug" then
- return "Debug"
- elseif mode_lower == "release" then
- return "Release"
- end
- return mode:sub(1, 1):upper() .. mode:sub(2)
-end
-
-function get_runtime_identifier(target)
- local rid = target:values("csharp.runtime_identifier")
- if type(rid) == "table" then
- rid = rid[1]
- end
- if rid and #rid > 0 then
- return rid
- end
- local arch = _map_rid_arch(target:arch())
- if not arch then
- return nil
- end
- local plat = target:plat()
- if plat == "windows" or plat == "mingw" or plat == "msys" or plat == "cygwin" then
- return "win-" .. arch
- elseif plat == "linux" then
- return "linux-" .. arch
- elseif plat == "macosx" then
- return "osx-" .. arch
- end
- return nil
-end
-
-function append_target_flags(target, argv)
- local flags = {}
- table.join2(flags, table.wrap(target:get("csflags")))
- table.join2(flags, table.wrap(target:get("ldflags")))
- table.join2(flags, table.wrap(target:get("arflags")))
- table.join2(flags, table.wrap(target:get("shflags")))
- for _, flag in ipairs(flags) do
- if flag and #flag > 0 then
- table.insert(argv, flag)
- end
- end
-end
-
-function get_dotnet_runopt(csprojfile)
- return {
- curdir = path.directory(csprojfile),
- envs = {
- DOTNET_NOLOGO = "1",
- DOTNET_CLI_TELEMETRY_OPTOUT = "1",
- DOTNET_SKIP_FIRST_TIME_EXPERIENCE = "1",
- DOTNET_GENERATE_ASPNET_CERTIFICATE = "0",
- DOTNET_ADD_GLOBAL_TOOLS_TO_PATH = "0"
- }
- }
-end
-
-function get_dotnet_verbosity()
- if option.get("diagnosis") then
- return "diagnostic"
- end
- return "quiet"
-end
-
-function get_dotnet_program(target)
- local function _get_configured_program(toolkind)
- if target:get("toolset." .. toolkind) then
- local program = target:tool(toolkind)
- if program and #program > 0 then
- return program
- end
- end
- end
-
- local program = nil
- if target:is_binary() then
- program = _get_configured_program("ld")
- elseif target:is_shared() then
- program = _get_configured_program("sh")
- else
- program = _get_configured_program("cs")
- end
- if program then
- return program
- end
-
- program = target:tool("cs")
- if program and #program > 0 then
- return program
- end
- return "dotnet"
-end
diff --git a/xmake/rules/csharp/install.lua b/xmake/rules/csharp/install.lua
index 34109a054..32cc9e8e0 100644
--- a/xmake/rules/csharp/install.lua
+++ b/xmake/rules/csharp/install.lua
@@ -18,7 +18,7 @@
-- @file install.lua
--
-import("csharp_common")
+import("modules.csharp_common", {rootdir = os.scriptdir(), alias = "csharp_common"})
function main(target, opt)
local function _q(arg)
@@ -29,7 +29,7 @@ function main(target, opt)
return arg
end
- local csprojfile = assert(csharp_common.find_csproj(target), "target(%s): missing csharp .csproj file!", target:name())
+ local csprojfile = assert(csharp_common.find_or_generate_csproj(target), "target(%s): missing csharp .csproj file!", target:name())
local dotnet = csharp_common.get_dotnet_program(target)
local configuration = csharp_common.build_mode_to_configuration()
local verbosity = csharp_common.get_dotnet_verbosity()
diff --git a/xmake/rules/csharp/installcmd.lua b/xmake/rules/csharp/installcmd.lua
index 76c051a59..7e1e385b4 100644
--- a/xmake/rules/csharp/installcmd.lua
+++ b/xmake/rules/csharp/installcmd.lua
@@ -18,10 +18,10 @@
-- @file installcmd.lua
--
-import("csharp_common")
+import("modules.csharp_common", {rootdir = os.scriptdir(), alias = "csharp_common"})
function main(target, batchcmds, opt)
- local csprojfile = assert(csharp_common.find_csproj(target), "target(%s): missing csharp .csproj file!", target:name())
+ local csprojfile = assert(csharp_common.find_or_generate_csproj(target), "target(%s): missing csharp .csproj file!", target:name())
local dotnet = csharp_common.get_dotnet_program(target)
local configuration = csharp_common.build_mode_to_configuration()
local verbosity = csharp_common.get_dotnet_verbosity()
diff --git a/xmake/rules/csharp/load.lua b/xmake/rules/csharp/load.lua
index 0811cf8d0..14b53a1fa 100644
--- a/xmake/rules/csharp/load.lua
+++ b/xmake/rules/csharp/load.lua
@@ -18,6 +18,8 @@
-- @file load.lua
--
+import("modules.csharp_common", {rootdir = os.scriptdir(), alias = "csharp_common"})
+
function main(target)
local targetdir = target:targetdir()
if targetdir and path.filename(targetdir) ~= target:name() then
@@ -40,12 +42,21 @@ function main(target)
table.insert(csprojfiles, sourcefile)
end
end
- assert(#csprojfiles > 0, "target(%s): csharp requires one .csproj in add_files()!", target:name())
- assert(#csprojfiles == 1, "target(%s): csharp only supports one .csproj file!", target:name())
+ assert(#csprojfiles <= 1, "target(%s): csharp only supports one .csproj file!", target:name())
- local csprojfile = csprojfiles[1]
- local csprojabs = path.is_absolute(csprojfile) and csprojfile or path.absolute(csprojfile, os.projectdir())
- assert(os.isfile(csprojabs), "target(%s): csharp .csproj not found: %s", target:name(), csprojfile)
+ local csprojabs = nil
+ if #csprojfiles == 1 then
+ local csprojfile = csprojfiles[1]
+ local csprojpath = path.is_absolute(csprojfile) and csprojfile or path.absolute(csprojfile, os.projectdir())
+ if os.isfile(csprojpath) then
+ csprojabs = csprojpath
+ end
+ end
+
+ if not csprojabs then
+ csprojabs = csharp_common.find_or_generate_csproj(target, {skip_deps = true})
+ end
+ assert(csprojabs, "target(%s): csharp failed to resolve or generate .csproj file!", target:name())
if not target:get("filename") and not target:get("basename") then
target:set("basename", path.basename(csprojabs))
diff --git a/xmake/rules/csharp/modules/csharp_common.lua b/xmake/rules/csharp/modules/csharp_common.lua
new file mode 100644
index 000000000..dce35021a
--- /dev/null
+++ b/xmake/rules/csharp/modules/csharp_common.lua
@@ -0,0 +1,232 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author JassJam
+-- @file csharp_common.lua
+--
+
+import("core.base.option")
+import("core.project.config")
+import("csproj_generator", {rootdir = os.scriptdir(), alias = "generate_csproj"})
+
+function _is_csharp_target(target)
+ if target:rule("csharp") then
+ return true
+ end
+ for _, sourcefile in ipairs(target:sourcefiles()) do
+ local ext = path.extension(sourcefile):lower()
+ if ext == ".cs" or ext == ".csproj" then
+ return true
+ end
+ end
+ return false
+end
+
+function _generated_csproj_path(target)
+ local targetkey = target:fullname():replace("::", path.sep())
+ local csprojdir = path.join(config.directory(), "rules", "csharp", targetkey, target:plat(), target:arch())
+ local csprojname = target:name() .. ".csproj"
+ return path.join(csprojdir, csprojname)
+end
+
+function _map_rid_arch(arch)
+ arch = (arch or ""):lower()
+ if arch == "x64" or arch == "x86_64" or arch == "amd64" then
+ return "x64"
+ elseif arch == "x86" or arch == "i386" then
+ return "x86"
+ elseif arch == "arm64" then
+ return "arm64"
+ elseif arch == "arm" or arch == "armv7" then
+ return "arm"
+ elseif arch == "riscv64" then
+ return "riscv64"
+ end
+ return nil
+end
+
+function find_csproj(target)
+ local csproj = target:data("csharp.csproj")
+ if csproj then
+ return csproj
+ end
+ for _, sourcefile in ipairs(target:sourcefiles()) do
+ if path.extension(sourcefile):lower() == ".csproj" then
+ local csprojabs = path.is_absolute(sourcefile) and sourcefile or path.absolute(sourcefile, os.projectdir())
+ if os.isfile(csprojabs) then
+ return csprojabs
+ end
+ end
+ end
+ return nil
+end
+
+function find_or_generate_csproj(target, opt)
+ opt = opt or {}
+ local csproj = find_csproj(target)
+ local generated = target:data("csharp.csproj.generated")
+ local generated_with_deps = target:data("csharp.csproj.generated.with_deps")
+
+ -- prefer existing source .csproj directly
+ if csproj and not generated then
+ return csproj
+ end
+
+ -- generated .csproj in memory cache
+ if csproj and generated then
+ if opt.skip_deps or generated_with_deps then
+ return csproj
+ end
+ end
+
+ if not _is_csharp_target(target) then
+ return nil
+ end
+
+ local csprojfile = csproj or _generated_csproj_path(target)
+ local generated_now = false
+
+ -- in load phase (skip_deps), reuse existing generated file to avoid
+ -- touching mtime and causing unnecessary rebuilds.
+ if not (opt.skip_deps and os.isfile(csprojfile)) then
+ generate_csproj(target, csprojfile, table.join(opt, {
+ is_csharp_target = _is_csharp_target,
+ find_or_generate_csproj = find_or_generate_csproj
+ }))
+ generated_now = true
+ end
+
+ target:data_set("csharp.csproj", csprojfile)
+ target:data_set("csharp.csproj.generated", true)
+ if opt.skip_deps then
+ -- keep this conservative in load phase: build/install phase will
+ -- generate a deps-enabled project if needed, and content checks avoid
+ -- unnecessary rewrites.
+ if generated_now or generated_with_deps == nil then
+ target:data_set("csharp.csproj.generated.with_deps", false)
+ end
+ else
+ target:data_set("csharp.csproj.generated.with_deps", true)
+ end
+ return csprojfile
+end
+
+function build_mode_to_configuration()
+ local mode
+ if type(get_config) == "function" then
+ mode = get_config("mode")
+ end
+ if not mode and type(is_mode) == "function" then
+ if is_mode("debug") then
+ mode = "debug"
+ elseif is_mode("release") then
+ mode = "release"
+ end
+ end
+ mode = mode or "release"
+ local mode_lower = mode:lower()
+ if mode_lower == "debug" then
+ return "Debug"
+ elseif mode_lower == "release" then
+ return "Release"
+ end
+ return mode:sub(1, 1):upper() .. mode:sub(2)
+end
+
+function get_runtime_identifier(target)
+ local rid = target:values("csharp.runtime_identifier")
+ if type(rid) == "table" then
+ rid = rid[1]
+ end
+ if rid and #rid > 0 then
+ return rid
+ end
+ local arch = _map_rid_arch(target:arch())
+ if not arch then
+ return nil
+ end
+ local plat = target:plat()
+ if plat == "windows" or plat == "mingw" or plat == "msys" or plat == "cygwin" then
+ return "win-" .. arch
+ elseif plat == "linux" then
+ return "linux-" .. arch
+ elseif plat == "macosx" then
+ return "osx-" .. arch
+ end
+ return nil
+end
+
+function append_target_flags(target, argv)
+ local flags = {}
+ table.join2(flags, table.wrap(target:get("csflags")))
+ table.join2(flags, table.wrap(target:get("ldflags")))
+ table.join2(flags, table.wrap(target:get("arflags")))
+ table.join2(flags, table.wrap(target:get("shflags")))
+ for _, flag in ipairs(flags) do
+ if flag and #flag > 0 then
+ table.insert(argv, flag)
+ end
+ end
+end
+
+function get_dotnet_runopt(csprojfile)
+ return {
+ curdir = path.directory(csprojfile),
+ envs = {
+ DOTNET_NOLOGO = "1",
+ DOTNET_CLI_TELEMETRY_OPTOUT = "1",
+ DOTNET_SKIP_FIRST_TIME_EXPERIENCE = "1",
+ DOTNET_GENERATE_ASPNET_CERTIFICATE = "0",
+ DOTNET_ADD_GLOBAL_TOOLS_TO_PATH = "0"
+ }
+ }
+end
+
+function get_dotnet_verbosity()
+ if option.get("diagnosis") then
+ return "diagnostic"
+ end
+ return "quiet"
+end
+
+function get_dotnet_program(target)
+ local function _get_configured_program(toolkind)
+ if target:get("toolset." .. toolkind) then
+ local program = target:tool(toolkind)
+ if program and #program > 0 then
+ return program
+ end
+ end
+ end
+
+ local program = nil
+ if target:is_binary() then
+ program = _get_configured_program("ld")
+ elseif target:is_shared() then
+ program = _get_configured_program("sh")
+ else
+ program = _get_configured_program("cs")
+ end
+ if program then
+ return program
+ end
+
+ program = target:tool("cs")
+ if program and #program > 0 then
+ return program
+ end
+ return "dotnet"
+end
diff --git a/xmake/rules/csharp/modules/csproj_generator.lua b/xmake/rules/csharp/modules/csproj_generator.lua
new file mode 100644
index 000000000..0f94ed806
--- /dev/null
+++ b/xmake/rules/csharp/modules/csproj_generator.lua
@@ -0,0 +1,301 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author JassJam
+-- @file csproj_generator.lua
+--
+
+import("properties", {rootdir = os.scriptdir(), alias = "csharp_properties"})
+import("itemgroups", {rootdir = os.scriptdir(), alias = "csharp_itemgroups"})
+
+function _xml_escape(value)
+ value = tostring(value or "")
+ value = value:gsub("&", "&")
+ value = value:gsub("<", "<")
+ value = value:gsub(">", ">")
+ value = value:gsub("\"", """)
+ value = value:gsub("'", "'")
+ return value
+end
+
+function _format_attributes(attrs)
+ if type(attrs) ~= "table" then
+ return ""
+ end
+ local keys = {}
+ for key, value in pairs(attrs) do
+ if value ~= nil and value ~= "" then
+ table.insert(keys, key)
+ end
+ end
+ table.sort(keys)
+ if #keys == 0 then
+ return ""
+ end
+ local chunks = {}
+ for _, key in ipairs(keys) do
+ table.insert(chunks, string.format(" %s=\"%s\"", key, _xml_escape(attrs[key])))
+ end
+ return table.concat(chunks)
+end
+
+function _get_csharp_value(target, name, defaultval)
+ local val = target:values(name)
+ if type(val) == "table" then
+ val = val[1]
+ end
+ if val == nil or val == "" then
+ return defaultval
+ end
+ return val
+end
+
+function _resolve_registry_value(entry, target, context)
+ if entry.resolve then
+ return entry.resolve(context)
+ end
+ if entry.value_type == "list" then
+ local values = table.wrap(target:values(entry.lua_key))
+ if #values == 0 and entry.default ~= nil then
+ values = table.wrap(entry.default)
+ end
+ if #values > 0 then
+ local items = {}
+ for _, value in ipairs(values) do
+ if value ~= nil and value ~= "" then
+ table.insert(items, tostring(value))
+ end
+ end
+ if #items > 0 then
+ return table.concat(items, entry.sep or ";")
+ end
+ end
+ return nil
+ end
+ return _get_csharp_value(target, entry.lua_key, entry.default)
+end
+
+function _collect_project_attributes(target, context, registry_entries)
+ local attrs = {}
+ for _, entry in ipairs(registry_entries) do
+ if entry.kind == "project_attribute" then
+ if not entry.when or entry.when(context) then
+ local value = _resolve_registry_value(entry, target, context)
+ if value ~= nil and value ~= "" then
+ attrs[entry.attr] = value
+ end
+ end
+ end
+ end
+ return attrs
+end
+
+function _collect_property_entries(target, context, registry_entries)
+ local entries = {}
+ for _, entry in ipairs(registry_entries) do
+ if entry.kind == "property" then
+ if not entry.when or entry.when(context) then
+ local value = _resolve_registry_value(entry, target, context)
+ if value ~= nil and value ~= "" then
+ table.insert(entries, {xml = entry.xml, value = value})
+ end
+ end
+ end
+ end
+ return entries
+end
+
+function _normalize_item_entry(item, default_xml)
+ if type(item) == "string" then
+ return {xml = default_xml, attrs = {Include = item}}
+ elseif type(item) ~= "table" then
+ return nil
+ end
+ local xml = item.xml or default_xml
+ if not xml or #tostring(xml) == 0 then
+ return nil
+ end
+ local attrs = item.attrs
+ if type(attrs) ~= "table" then
+ attrs = {}
+ for key, value in pairs(item) do
+ if type(key) == "string" and key ~= "xml" and key ~= "value" and key ~= "attrs" then
+ attrs[key] = value
+ end
+ end
+ end
+ return {xml = xml, attrs = attrs, value = item.value}
+end
+
+function _collect_item_groups(context, registry_entries)
+ local groups = {}
+ local groupmap = {}
+ function _group(name)
+ local g = groupmap[name]
+ if not g then
+ g = {name = name, items = {}}
+ groupmap[name] = g
+ table.insert(groups, g)
+ end
+ return g
+ end
+ for _, entry in ipairs(registry_entries) do
+ if entry.kind == "item" then
+ if not entry.when or entry.when(context) then
+ for _, item in ipairs(table.wrap(entry.resolve_items and entry.resolve_items(context) or {})) do
+ local normalized = _normalize_item_entry(item, entry.xml)
+ if normalized then
+ table.insert(_group(entry.group or entry.xml).items, normalized)
+ end
+ end
+ end
+ end
+ end
+ return groups
+end
+
+function _is_valid_property_name(name)
+ return type(name) == "string" and name:match("^[A-Za-z_][A-Za-z0-9_.-]*$") ~= nil
+end
+
+function _add_custom_property(entries, name, value)
+ if not _is_valid_property_name(name) then
+ return
+ end
+ if value == nil then
+ return
+ end
+ if type(value) == "table" then
+ local values = {}
+ for _, v in ipairs(value) do
+ if v ~= nil and v ~= "" then
+ table.insert(values, tostring(v))
+ end
+ end
+ if #values == 0 then
+ return
+ end
+ value = table.concat(values, ";")
+ else
+ value = tostring(value)
+ end
+ if #value == 0 then
+ return
+ end
+ table.insert(entries, {xml = name, value = value})
+end
+
+function _add_custom_properties_from_item(entries, item)
+ if type(item) == "string" then
+ local name, value = item:match("^%s*([^=]+)%s*=(.*)$")
+ if name then
+ _add_custom_property(entries, name:trim(), value)
+ end
+ return
+ end
+ if type(item) ~= "table" then
+ return
+ end
+ if item.name then
+ _add_custom_property(entries, tostring(item.name), item.value)
+ return
+ end
+ local keys = {}
+ for k in pairs(item) do
+ if type(k) == "string" then
+ table.insert(keys, k)
+ end
+ end
+ table.sort(keys)
+ for _, key in ipairs(keys) do
+ _add_custom_property(entries, key, item[key])
+ end
+end
+
+function _collect_custom_property_entries(target)
+ local entries = {}
+ for _, item in ipairs(table.wrap(target:values("csharp.properties"))) do
+ _add_custom_properties_from_item(entries, item)
+ end
+ return entries
+end
+
+function _render_property_group(lines, entries)
+ if #entries == 0 then
+ return
+ end
+ table.insert(lines, " ")
+ for _, entry in ipairs(entries) do
+ table.insert(lines, string.format(" <%s>%s%s>", entry.xml, _xml_escape(entry.value), entry.xml))
+ end
+ table.insert(lines, " ")
+end
+
+function _render_item_groups(lines, item_groups)
+ for _, group in ipairs(item_groups) do
+ if #group.items > 0 then
+ table.insert(lines, " ")
+ for _, item in ipairs(group.items) do
+ local attrs = _format_attributes(item.attrs)
+ if item.value ~= nil and item.value ~= "" then
+ table.insert(lines, string.format(" <%s%s>%s%s>", item.xml, attrs, _xml_escape(item.value), item.xml))
+ else
+ table.insert(lines, string.format(" <%s%s />", item.xml, attrs))
+ end
+ end
+ table.insert(lines, " ")
+ end
+ end
+end
+
+function main(target, csprojfile, opt)
+ opt = opt or {}
+
+ local csprojdir = path.directory(csprojfile)
+ local context = {
+ target = target,
+ csprojfile = csprojfile,
+ csprojdir = csprojdir,
+ opt = opt
+ }
+
+ local property_registry_entries = csharp_properties()
+ local item_registry_entries = csharp_itemgroups()
+
+ local project_attributes = _collect_project_attributes(target, context, property_registry_entries)
+ local property_entries = _collect_property_entries(target, context, property_registry_entries)
+ local custom_property_entries = _collect_custom_property_entries(target)
+ table.join2(property_entries, custom_property_entries)
+
+ local item_groups = _collect_item_groups(context, item_registry_entries)
+ local lines = {}
+
+ table.insert(lines, string.format("", _format_attributes(project_attributes)))
+ _render_property_group(lines, property_entries)
+ _render_item_groups(lines, item_groups)
+ table.insert(lines, "")
+
+ local content = table.concat(lines, "\n") .. "\n"
+
+ os.mkdir(csprojdir)
+ local oldcontent = nil
+ if os.isfile(csprojfile) then
+ oldcontent = io.readfile(csprojfile)
+ end
+ if oldcontent ~= content then
+ io.writefile(csprojfile, content)
+ end
+end
diff --git a/xmake/rules/csharp/modules/itemgroups.lua b/xmake/rules/csharp/modules/itemgroups.lua
new file mode 100644
index 000000000..6d105fd45
--- /dev/null
+++ b/xmake/rules/csharp/modules/itemgroups.lua
@@ -0,0 +1,166 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author JassJam
+-- @file itemsgroups.lua
+--
+
+local function _normalize_relative(fromdir, targetpath)
+ local relpath = path.relative(targetpath, fromdir) or targetpath
+ if os.host() == "windows" then
+ relpath = relpath:gsub("\\", "/")
+ end
+ return relpath
+end
+
+local function _collect_cs_sourcefiles(context)
+ local csfiles = {}
+ for _, sourcefile in ipairs(context.target:sourcefiles()) do
+ if path.extension(sourcefile):lower() == ".cs" then
+ local sourceabs = path.is_absolute(sourcefile) and sourcefile or path.absolute(sourcefile, os.projectdir())
+ table.insert(csfiles, _normalize_relative(context.csprojdir, sourceabs))
+ end
+ end
+ table.sort(csfiles)
+ return table.unique(csfiles)
+end
+
+local function _collect_project_references(context)
+ if context.opt.skip_deps then
+ return {}
+ end
+ local references = {}
+ for _, depname in ipairs(table.wrap(context.target:get("deps"))) do
+ local dep = context.target:dep(depname)
+ if dep and context.opt.is_csharp_target and context.opt.is_csharp_target(dep) then
+ local depcsproj = context.opt.find_or_generate_csproj and context.opt.find_or_generate_csproj(dep)
+ if depcsproj then
+ table.insert(references, _normalize_relative(context.csprojdir, depcsproj))
+ end
+ end
+ end
+ table.sort(references)
+ return table.unique(references)
+end
+
+local function _get_nuget_info(pkg)
+ local requirestr = pkg:requirestr() or ""
+ local splitinfo = requirestr:trim():split("%s+")
+ if #splitinfo == 0 then
+ return nil
+ end
+
+ local pkgname = splitinfo[1]
+ if pkgname:find("::", 1, true) then
+ pkgname = pkgname:split("::", {plain = true})
+ pkgname = pkgname[#pkgname]
+ end
+ local pkgname_raw = pkgname:match("(.-)%[.*%]$")
+ if pkgname_raw and #pkgname_raw > 0 then
+ pkgname = pkgname_raw
+ end
+ if not pkgname or #pkgname == 0 then
+ return nil
+ end
+
+ local version
+ local versionobj = pkg:version()
+ if versionobj then
+ version = tostring(versionobj)
+ end
+ if not version and #splitinfo > 1 then
+ local require_version = table.concat(table.slice(splitinfo, 2), " ")
+ if require_version ~= "latest" then
+ version = require_version
+ end
+ end
+ return pkgname, version
+end
+
+local function _collect_nuget_references(context)
+ local versions = {}
+ for _, pkg in ipairs(context.target:orderpkgs()) do
+ local namespace = pkg:namespace()
+ local requirestr = pkg:requirestr() or ""
+ if namespace == "nuget" or requirestr:startswith("nuget::") then
+ local pkgname, version = _get_nuget_info(pkg)
+ if pkgname then
+ if version or versions[pkgname] == nil then
+ versions[pkgname] = version or false
+ end
+ end
+ end
+ end
+
+ local references = {}
+ for pkgname, version in pairs(versions) do
+ table.insert(references, {name = pkgname, version = version or nil})
+ end
+ table.sort(references, function (a, b) return a.name < b.name end)
+ return references
+end
+
+function main()
+ local entries = {}
+ local function register(entry)
+ table.insert(entries, entry)
+ end
+
+ register({
+ kind = "item",
+ group = "compile",
+ xml = "Compile",
+ resolve_items = function (context)
+ local items = {}
+ for _, sourcefile in ipairs(_collect_cs_sourcefiles(context)) do
+ table.insert(items, {attrs = {Include = sourcefile}})
+ end
+ return items
+ end
+ })
+
+ register({
+ kind = "item",
+ group = "project_reference",
+ xml = "ProjectReference",
+ resolve_items = function (context)
+ local items = {}
+ for _, reffile in ipairs(_collect_project_references(context)) do
+ table.insert(items, {attrs = {Include = reffile}})
+ end
+ return items
+ end
+ })
+
+ register({
+ kind = "item",
+ group = "package_reference",
+ xml = "PackageReference",
+ resolve_items = function (context)
+ local items = {}
+ for _, pkginfo in ipairs(_collect_nuget_references(context)) do
+ local attrs = {Include = pkginfo.name}
+ if pkginfo.version then
+ attrs.Version = pkginfo.version
+ end
+ table.insert(items, {attrs = attrs})
+ end
+ return items
+ end
+ })
+
+ return entries
+end
diff --git a/xmake/rules/csharp/modules/properties.lua b/xmake/rules/csharp/modules/properties.lua
new file mode 100644
index 000000000..b8d30f1e9
--- /dev/null
+++ b/xmake/rules/csharp/modules/properties.lua
@@ -0,0 +1,307 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author JassJam
+-- @file properties.lua
+--
+
+function _has_target_frameworks(context)
+ return #table.wrap(context.target:values("csharp.target_frameworks")) > 0
+end
+
+local _default_target_framework_cached = {}
+
+function _first(value)
+ if type(value) == "table" then
+ return value[1]
+ end
+ return value
+end
+
+function _get_target_value(target, name)
+ return _first(target:values(name))
+end
+
+function _collect_strings(result, value)
+ if value == nil then
+ return
+ end
+ if type(value) == "table" then
+ for _, item in ipairs(value) do
+ _collect_strings(result, item)
+ end
+ else
+ local sval = tostring(value)
+ if #sval > 0 then
+ table.insert(result, sval)
+ end
+ end
+end
+
+function _extract_define_symbol(define)
+ define = tostring(define):trim()
+ if #define == 0 then
+ return nil
+ end
+ local symbol = define:split("=", {plain = true})[1]
+ if not symbol then
+ return nil
+ end
+ symbol = symbol:trim()
+ if symbol:match("^[A-Za-z_][A-Za-z0-9_]*$") then
+ return symbol
+ end
+ return nil
+end
+
+function _resolve_define_constants(context)
+ local target = context.target
+ local constants = {}
+
+ _collect_strings(constants, target:get("defines"))
+ local opt_defines = target:get_from("defines", "option::*")
+ if opt_defines then
+ _collect_strings(constants, opt_defines)
+ end
+
+ local symbols = {}
+ for _, define in ipairs(constants) do
+ local symbol = _extract_define_symbol(define)
+ if symbol then
+ table.insert(symbols, symbol)
+ end
+ end
+ symbols = table.unique(symbols)
+ if #symbols > 0 then
+ return table.concat(symbols, ";")
+ end
+end
+
+function _get_default_target_framework(context)
+ local dotnet = _first(context.target:get("toolset.cs")) or "dotnet"
+ dotnet = tostring(dotnet)
+ local cached = _default_target_framework_cached[dotnet]
+ if cached ~= nil then
+ return cached
+ end
+
+ local major = nil
+ local sdks = try { function ()
+ return os.iorunv(dotnet, {"--list-sdks"})
+ end }
+ if sdks then
+ for line in sdks:gmatch("[^\r\n]+") do
+ local line_major = tonumber(line:match("^%s*(%d+)%.%d+%.%d+"))
+ if line_major and (not major or line_major > major) then
+ major = line_major
+ end
+ end
+ end
+ if not major then
+ local version = try { function ()
+ return os.iorunv(dotnet, {"--version"})
+ end }
+ if version then
+ major = tonumber(version:match("^%s*(%d+)"))
+ end
+ end
+
+ local target_framework = major and ("net" .. tostring(major) .. ".0") or "net8.0"
+ _default_target_framework_cached[dotnet] = target_framework
+ return target_framework
+end
+
+function _resolve_target_framework(context)
+ local target_framework = _get_target_value(context.target, "csharp.target_framework")
+ if target_framework ~= nil and #tostring(target_framework) > 0 then
+ return target_framework
+ end
+ return _get_default_target_framework(context)
+end
+
+function _resolve_assembly_name(context)
+ local basename = context.target:basename()
+ if basename ~= nil and #tostring(basename) > 0 then
+ return basename
+ end
+end
+
+function _resolve_optimize(context)
+ local optimize = _first(context.target:get("optimize"))
+ if optimize ~= nil then
+ return optimize == "none" and "false" or "true"
+ end
+end
+
+function _resolve_debug_symbols(context)
+ local symbols = _first(context.target:get("symbols"))
+ if symbols ~= nil then
+ return symbols == "none" and "false" or "true"
+ end
+end
+
+function _resolve_debug_type(context)
+ local symbols = _first(context.target:get("symbols"))
+ if symbols and symbols ~= "none" then
+ return "portable"
+ end
+end
+
+function _resolve_platform_target(context)
+ local arch = (context.target:arch() or ""):lower()
+ local mapping = {
+ x86_64 = "x64",
+ amd64 = "x64",
+ x64 = "x64",
+ i386 = "x86",
+ x86 = "x86",
+ arm64 = "arm64",
+ arm = "arm",
+ armv7 = "arm"
+ }
+ return mapping[arch]
+end
+
+function _resolve_warning_level(context)
+ local warnings = _first(context.target:get("warnings"))
+ local mapping = {
+ none = "0",
+ less = "2",
+ more = "3",
+ all = "4",
+ allextra = "4",
+ everything = "4",
+ error = "4"
+ }
+ return warnings and mapping[warnings]
+end
+
+function _resolve_treat_warnings_as_errors(context)
+ local warnings = _first(context.target:get("warnings"))
+ if warnings == "error" then
+ return "true"
+ end
+end
+
+function _register_property(register, suffix, xml, default, extra)
+ local entry = table.join({
+ kind = "property",
+ xml = xml,
+ lua_key = "csharp." .. suffix,
+ default = default
+ }, extra or {})
+ register(entry)
+end
+
+function _register_list_property(register, suffix, xml, extra)
+ local entry = table.join({
+ kind = "property",
+ xml = xml,
+ lua_key = "csharp." .. suffix,
+ value_type = "list",
+ sep = ";"
+ }, extra or {})
+ register(entry)
+end
+
+function main()
+ local entries = {}
+ function register(entry)
+ table.insert(entries, entry)
+ end
+
+ register({kind = "project_attribute", attr = "Sdk", lua_key = "csharp.sdk", default = "Microsoft.NET.Sdk"})
+ register({kind = "property", xml = "OutputType", resolve = function (context)
+ return context.target:is_binary() and "Exe" or "Library"
+ end})
+ _register_list_property(register, "target_frameworks", "TargetFrameworks", {when = _has_target_frameworks})
+ register({kind = "property", xml = "TargetFramework", resolve = _resolve_target_framework, when = function (context)
+ return not _has_target_frameworks(context)
+ end})
+
+ _register_property(register, "implicit_usings", "ImplicitUsings", "enable")
+ _register_property(register, "nullable", "Nullable", "enable")
+ _register_property(register, "lang_version", "LangVersion")
+ _register_property(register, "enable_default_compile_items", "EnableDefaultCompileItems", "false")
+ _register_property(register, "enable_default_embedded_resource_items", "EnableDefaultEmbeddedResourceItems")
+ _register_property(register, "enable_default_none_items", "EnableDefaultNoneItems")
+ _register_property(register, "root_namespace", "RootNamespace")
+ register({kind = "property", xml = "AssemblyName", resolve = _resolve_assembly_name})
+ _register_property(register, "generate_assembly_info", "GenerateAssemblyInfo")
+ _register_property(register, "deterministic", "Deterministic")
+ register({kind = "property", xml = "Optimize", resolve = _resolve_optimize})
+ register({kind = "property", xml = "PlatformTarget", resolve = _resolve_platform_target})
+ _register_property(register, "prefer_32bit", "Prefer32Bit")
+ _register_property(register, "allow_unsafe_blocks", "AllowUnsafeBlocks")
+ _register_property(register, "check_for_overflow_underflow", "CheckForOverflowUnderflow")
+ register({kind = "property", xml = "WarningLevel", resolve = _resolve_warning_level})
+ _register_property(register, "analysis_level", "AnalysisLevel")
+ _register_property(register, "enable_net_analyzers", "EnableNETAnalyzers")
+ _register_property(register, "enforce_code_style_in_build", "EnforceCodeStyleInBuild")
+ register({kind = "property", xml = "TreatWarningsAsErrors", resolve = _resolve_treat_warnings_as_errors})
+ _register_list_property(register, "warnings_as_errors", "WarningsAsErrors")
+ _register_list_property(register, "warnings_not_as_errors", "WarningsNotAsErrors")
+ register({kind = "property", xml = "DefineConstants", resolve = _resolve_define_constants})
+ _register_property(register, "error_log", "ErrorLog")
+ register({kind = "property", xml = "DebugType", resolve = _resolve_debug_type})
+ register({kind = "property", xml = "DebugSymbols", resolve = _resolve_debug_symbols})
+ _register_property(register, "generate_documentation_file", "GenerateDocumentationFile")
+ _register_property(register, "documentation_file", "DocumentationFile")
+
+ _register_property(register, "runtime_identifier", "RuntimeIdentifier")
+ _register_list_property(register, "runtime_identifiers", "RuntimeIdentifiers")
+ _register_property(register, "self_contained", "SelfContained")
+ _register_property(register, "use_app_host", "UseAppHost")
+ _register_property(register, "roll_forward", "RollForward")
+ _register_property(register, "publish_single_file", "PublishSingleFile")
+ _register_property(register, "publish_trimmed", "PublishTrimmed")
+ _register_property(register, "trim_mode", "TrimMode")
+ _register_property(register, "publish_ready_to_run", "PublishReadyToRun")
+ _register_property(register, "invariant_globalization", "InvariantGlobalization")
+ _register_property(register, "include_native_libraries_for_self_extract", "IncludeNativeLibrariesForSelfExtract")
+ _register_property(register, "enable_compression_in_single_file", "EnableCompressionInSingleFile")
+ _register_property(register, "publish_aot", "PublishAot")
+ _register_property(register, "strip_symbols", "StripSymbols")
+ _register_property(register, "enable_trim_analyzer", "EnableTrimAnalyzer")
+ _register_property(register, "json_serializer_is_reflection_enabled_by_default", "JsonSerializerIsReflectionEnabledByDefault")
+ _register_list_property(register, "satellite_resource_languages", "SatelliteResourceLanguages")
+
+ _register_property(register, "version", "Version")
+ _register_property(register, "assembly_version", "AssemblyVersion")
+ _register_property(register, "file_version", "FileVersion")
+ _register_property(register, "informational_version", "InformationalVersion")
+ _register_property(register, "package_id", "PackageId")
+ _register_property(register, "authors", "Authors")
+ _register_property(register, "company", "Company")
+ _register_property(register, "product", "Product")
+ _register_property(register, "description", "Description")
+ _register_property(register, "copyright", "Copyright")
+ _register_property(register, "repository_url", "RepositoryUrl")
+ _register_property(register, "repository_type", "RepositoryType")
+ _register_property(register, "package_license_expression", "PackageLicenseExpression")
+ _register_property(register, "package_project_url", "PackageProjectUrl")
+ _register_property(register, "neutral_language", "NeutralLanguage")
+ _register_property(register, "enable_preview_features", "EnablePreviewFeatures")
+
+ _register_property(register, "generate_runtime_configuration_files", "GenerateRuntimeConfigurationFiles")
+ _register_property(register, "copy_local_lock_file_assemblies", "CopyLocalLockFileAssemblies")
+ _register_property(register, "append_target_framework_to_output_path", "AppendTargetFrameworkToOutputPath", "false")
+ _register_property(register, "append_runtime_identifier_to_output_path", "AppendRuntimeIdentifierToOutputPath", "false")
+ _register_property(register, "produce_reference_assembly", "ProduceReferenceAssembly")
+ _register_property(register, "disable_implicit_framework_references", "DisableImplicitFrameworkReferences")
+ _register_property(register, "generate_target_framework_attribute", "GenerateTargetFrameworkAttribute")
+ return entries
+end
diff --git a/xmake/rules/csharp/xmake.lua b/xmake/rules/csharp/xmake.lua
index 5d358e099..b5e76dfa9 100644
--- a/xmake/rules/csharp/xmake.lua
+++ b/xmake/rules/csharp/xmake.lua
@@ -24,10 +24,87 @@
-- target("app")
-- set_kind("binary") -- or "shared"
-- add_rules("csharp")
--- add_files("src/*.cs", "src/app.csproj")
+-- add_files("src/*.cs")
--
--- -- optional: override runtime identifier (arch-specific) for self-contained publish (e.g. "win-x64", "linux-x64", "osx-x64", "osx-arm64")
--- -- add_values("csharp.runtime_identifier", "win-x64")
+-- -- optional: if a .csproj is provided, xmake uses it directly.
+-- -- otherwise xmake auto-generates one in .xmake/... for dotnet build/publish.
+-- -- add_files("src/app.csproj")
+--
+-- -- add_values("csharp.", ) -- use one the properties below
+-- -- add_values("csharp.properties", "AnotherProperty=Value") -- declare a custom property
+--
+-- -- mapped csharp properties (lua key => generated xml):
+-- -- https://learn.microsoft.com/en-us/nuget/reference/msbuild-targets#pack-target
+-- -- csharp.sdk => (project attribute) (defaults to "Microsoft.NET.Sdk")
+-- -- csharp.target_frameworks => (list, ';' separated)
+-- -- csharp.target_framework => (defaults to latest installed dotnet sdk major, e.g. net8.0)
+-- -- csharp.implicit_usings =>
+-- -- csharp.nullable =>
+-- -- csharp.lang_version =>
+-- -- csharp.enable_default_compile_items =>
+-- -- csharp.enable_default_embedded_resource_items =>
+-- -- csharp.enable_default_none_items =>
+-- -- csharp.root_namespace =>
+-- -- default/derived: set_basename(...) =>
+-- -- csharp.generate_assembly_info =>
+-- -- csharp.deterministic =>
+-- -- default/derived: set_optimize(...) =>
+-- -- default/derived: get_plat()/get_arch() =>
+-- -- csharp.prefer_32bit =>
+-- -- csharp.allow_unsafe_blocks =>
+-- -- csharp.check_for_overflow_underflow =>
+-- -- default/derived: set_warnings(...) =>
+-- -- csharp.analysis_level =>
+-- -- csharp.enable_net_analyzers =>
+-- -- csharp.enforce_code_style_in_build =>
+-- -- default/derived: set_warnings("error") => true
+-- -- csharp.warnings_as_errors => (list, ';' separated)
+-- -- csharp.warnings_not_as_errors => (list, ';' separated)
+-- -- default/derived: add_defines(...) =>
+-- -- csharp.error_log =>
+-- -- default/derived: set_symbols(...) => /
+-- -- csharp.generate_documentation_file =>
+-- -- csharp.documentation_file =>
+-- -- csharp.runtime_identifier =>
+-- -- csharp.runtime_identifiers => (list, ';' separated)
+-- -- csharp.self_contained =>
+-- -- csharp.use_app_host =>
+-- -- csharp.roll_forward =>
+-- -- csharp.publish_single_file =>
+-- -- csharp.publish_trimmed =>
+-- -- csharp.trim_mode =>
+-- -- csharp.publish_ready_to_run =>
+-- -- csharp.invariant_globalization =>
+-- -- csharp.include_native_libraries_for_self_extract =>
+-- -- csharp.enable_compression_in_single_file =>
+-- -- csharp.publish_aot =>
+-- -- csharp.strip_symbols =>
+-- -- csharp.enable_trim_analyzer =>
+-- -- csharp.json_serializer_is_reflection_enabled_by_default =>
+-- -- csharp.satellite_resource_languages => (list, ';' separated)
+-- -- csharp.version =>
+-- -- csharp.assembly_version =>
+-- -- csharp.file_version =>
+-- -- csharp.informational_version =>
+-- -- csharp.package_id =>
+-- -- csharp.authors =>
+-- -- csharp.company =>
+-- -- csharp.product =>
+-- -- csharp.description =>
+-- -- csharp.copyright =>
+-- -- csharp.repository_url =>
+-- -- csharp.repository_type =>
+-- -- csharp.package_license_expression =>
+-- -- csharp.package_project_url =>
+-- -- csharp.neutral_language =>
+-- -- csharp.enable_preview_features =>
+-- -- csharp.generate_runtime_configuration_files =>
+-- -- csharp.copy_local_lock_file_assemblies =>
+-- -- csharp.append_target_framework_to_output_path => (default: false)
+-- -- csharp.append_runtime_identifier_to_output_path => (default: false)
+-- -- csharp.produce_reference_assembly =>
+-- -- csharp.disable_implicit_framework_references =>
+-- -- csharp.generate_target_framework_attribute =>
-- @endcode
--
rule("csharp")
--
cgit v1.3.1
From 78d6ffb3674247d917f22779cf2e684c73470bfd Mon Sep 17 00:00:00 2001
From: JassJam <0139ne@gmail.com>
Date: Sat, 28 Feb 2026 16:40:15 +0000
Subject: feat: update nuget's find_package to resolve packages properly with
fallback
---
.../modules/package/manager/nuget/find_package.lua | 184 ++++++++++++++++-----
1 file changed, 144 insertions(+), 40 deletions(-)
diff --git a/xmake/modules/package/manager/nuget/find_package.lua b/xmake/modules/package/manager/nuget/find_package.lua
index a206eb301..4a27eed19 100644
--- a/xmake/modules/package/manager/nuget/find_package.lua
+++ b/xmake/modules/package/manager/nuget/find_package.lua
@@ -28,6 +28,82 @@ import("core.project.config")
import("core.project.target")
import("private.utils.toolchain", {alias = "toolchain_utils"})
+function _find_target_root(targets, name)
+ local namelower = name:lower()
+ for targetname in pairs(targets or {}) do
+ local targetpkg = targetname:match("^([^/]+)") or targetname
+ local targetpkglower = targetpkg:lower()
+ local targetnamelower = targetname:lower()
+ local normalized_targetpkg = targetpkglower:gsub("[._%-]", "")
+ local normalized_name = namelower:gsub("[._%-]", "")
+ if targetnamelower == namelower
+ or targetnamelower:startswith(namelower .. "/")
+ or targetpkglower == namelower
+ or normalized_targetpkg == normalized_name then
+ return targetname
+ end
+ end
+end
+
+function _find_library_root(libraries, name)
+ local namelower = name:lower()
+ for libraryname in pairs(libraries or {}) do
+ local librarypkg = libraryname:match("^([^/]+)") or libraryname
+ local librarypkglower = librarypkg:lower()
+ local librarynamelower = libraryname:lower()
+ local normalized_librarypkg = librarypkglower:gsub("[._%-]", "")
+ local normalized_name = namelower:gsub("[._%-]", "")
+ if librarynamelower == namelower
+ or librarynamelower:startswith(namelower .. "/")
+ or librarypkglower == namelower
+ or normalized_librarypkg == normalized_name then
+ return libraryname
+ end
+ end
+end
+
+function _find_version_in_project_dependencies(manifest, name)
+ local namelower = name:lower()
+ local normalized_name = namelower:gsub("[._%-]", "")
+ if manifest.project and manifest.project.frameworks then
+ for _, framework in pairs(manifest.project.frameworks) do
+ local dependencies = framework.dependencies or {}
+ for depname, depver in pairs(dependencies) do
+ local deplower = depname:lower()
+ local normalized_depname = deplower:gsub("[._%-]", "")
+ if deplower == namelower or normalized_depname == normalized_name then
+ return tostring(depver)
+ end
+ end
+ end
+ end
+end
+
+function _get_packagesdir_from_manifest(manifest)
+ if manifest.project and manifest.project.restore and manifest.project.restore.packagesPath then
+ return manifest.project.restore.packagesPath
+ end
+ if manifest.packageFolders then
+ for folder in pairs(manifest.packageFolders) do
+ return folder
+ end
+ end
+ local packagesdir = os.getenv("NUGET_PACKAGES")
+ if packagesdir and #packagesdir > 0 then
+ return packagesdir
+ end
+ local homedir = os.homedir and os.homedir()
+ if homedir and #homedir > 0 then
+ return path.join(homedir, ".nuget", "packages")
+ end
+end
+
+local function _extract_version_from_root(rootname)
+ if rootname then
+ return rootname:match("/(.+)$")
+ end
+end
+
-- check if pattern matches as a complete path component
-- e.g. "x86" should match "/x86/" but not "/x86_64/"
function _match_path_component(file_lower, pattern)
@@ -86,7 +162,7 @@ function _match_libfile(file, libarch, toolset, libmode, runtime)
if file_lower:find(libmode:lower(), 1, true) then
score = score + 2
end
- if file_lower:find(runtime:lower(), 1, true) then
+ if runtime and file_lower:find(runtime:lower(), 1, true) then
score = score + 1
end
return score
@@ -105,9 +181,10 @@ function _find_package(name, result, opt)
MTd = "MultiThreadedDebug",
MD = "MultiThreadedDLL",
MDd = "MultiThreadedDebugDLL"}
+ local runtime = runtimes[configs.runtimes]
local installdir = path.join(opt.packagesdir, name)
- local runtime = assert(runtimes[configs.runtimes], "unknown runtimes %s", configs.runtimes)
- local toolset = toolchain_utils.get_vs_toolset_ver(toolchain.load("msvc", {plat = plat, arch = arch}):config("vs_toolset") or config.get("vs_toolset"))
+ local msvc = toolchain.load("msvc", {plat = plat, arch = arch})
+ local toolset = msvc and toolchain_utils.get_vs_toolset_ver(msvc:config("vs_toolset") or config.get("vs_toolset")) or nil
local libarch = libarchs[arch] or "x64"
local libmode = configs.debug and "Debug" or "Release"
for _, file in ipairs(libinfo.files) do
@@ -169,6 +246,22 @@ function _find_package(name, result, opt)
end
end
+function _cleanup_result(result, version)
+ result._libscores = nil
+ if version and not result.version then
+ result.version = version
+ end
+ if result.links or result.includedirs then
+ if result.includedirs then
+ result.includedirs = table.unique(result.includedirs)
+ end
+ if result.linkdirs then
+ result.linkdirs = table.unique(result.linkdirs)
+ end
+ end
+ return result
+end
+
-- find package from the nuget package manager
--
-- @param name the package name, e.g. zlib, pcre
@@ -185,46 +278,57 @@ function main(name, opt)
return
end
local manifest = json.loadfile(manifestfile)
- local targets
- for k, v in pairs(manifest.targets) do
- targets = v
- break
- end
- local target_root
- if targets then
- for k, v in pairs(targets) do
- if k:startswith(name) then
- target_root = k
- break
- end
- end
+ local packagesdir = _get_packagesdir_from_manifest(manifest)
+ if not manifest.libraries then
+ return
end
- local metainfo = {}
- metainfo.plat = opt.plat
- metainfo.arch = opt.arch
- metainfo.mode = opt.mode
- metainfo.configs = opt.configs
- metainfo.targets = targets
- metainfo.libraries = manifest.libraries
- if manifest.project and manifest.project.restore then
- metainfo.packagesdir = manifest.project.restore.packagesPath
- end
- if target_root and metainfo.targets and metainfo.libraries and metainfo.packagesdir then
- local result = {}
- _find_package(target_root, result, metainfo)
- -- clean up internal scoring data
- result._libscores = nil
- if result.links or result.includedirs then
- -- deduplicate paths
- if result.includedirs then
- result.includedirs = table.unique(result.includedirs)
- end
- if result.linkdirs then
- result.linkdirs = table.unique(result.linkdirs)
+
+ if manifest.targets and packagesdir then
+ for _, targets in pairs(manifest.targets) do
+ local target_root = _find_target_root(targets, name)
+ if target_root then
+ local metainfo = {}
+ metainfo.plat = opt.plat
+ metainfo.arch = opt.arch
+ metainfo.mode = opt.mode
+ metainfo.configs = opt.configs
+ metainfo.targets = targets
+ metainfo.libraries = manifest.libraries
+ metainfo.packagesdir = packagesdir
+ local result = {}
+ _find_package(target_root, result, metainfo)
+ return _cleanup_result(result, _extract_version_from_root(target_root))
end
- return result
end
end
-end
+ local library_root = _find_library_root(manifest.libraries, name)
+ if library_root then
+ if packagesdir then
+ local metainfo = {}
+ metainfo.plat = opt.plat
+ metainfo.arch = opt.arch
+ metainfo.mode = opt.mode
+ metainfo.configs = opt.configs
+ metainfo.targets = {}
+ metainfo.libraries = manifest.libraries
+ metainfo.packagesdir = packagesdir
+ local result = {}
+ _find_package(library_root, result, metainfo)
+ return _cleanup_result(result, _extract_version_from_root(library_root))
+ end
+ -- package exists in manifest but package root cannot be determined,
+ -- treat managed-only package as found.
+ return {version = _extract_version_from_root(library_root)}
+ end
+ -- fallback for managed package references if target/library keys are not aligned
+ -- with the requested package name format.
+ local depver = _find_version_in_project_dependencies(manifest, name)
+ if depver then
+ return {version = depver}
+ end
+ if opt.require_version then
+ return {version = tostring(opt.require_version)}
+ end
+end
--
cgit v1.3.1
From 4b56123d797578f1abec0c3d507282d9e5bc8f3b Mon Sep 17 00:00:00 2001
From: JassJam <0139ne@gmail.com>
Date: Sat, 28 Feb 2026 16:50:26 +0000
Subject: refactor: dont declare local function for private function
---
xmake/rules/csharp/buildcmd.lua | 2 +-
xmake/rules/csharp/modules/itemgroups.lua | 10 +++++-----
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/xmake/rules/csharp/buildcmd.lua b/xmake/rules/csharp/buildcmd.lua
index b8f85360a..614cc5a25 100644
--- a/xmake/rules/csharp/buildcmd.lua
+++ b/xmake/rules/csharp/buildcmd.lua
@@ -20,7 +20,7 @@
import("modules.csharp_common", {rootdir = os.scriptdir(), alias = "csharp_common"})
-local function _get_output_mtime(target, targetfile, targetdirabs)
+function _get_output_mtime(target, targetfile, targetdirabs)
local mtime = targetfile and os.mtime(targetfile) or nil
if mtime then
return mtime
diff --git a/xmake/rules/csharp/modules/itemgroups.lua b/xmake/rules/csharp/modules/itemgroups.lua
index 6d105fd45..6a7506bba 100644
--- a/xmake/rules/csharp/modules/itemgroups.lua
+++ b/xmake/rules/csharp/modules/itemgroups.lua
@@ -18,7 +18,7 @@
-- @file itemsgroups.lua
--
-local function _normalize_relative(fromdir, targetpath)
+function _normalize_relative(fromdir, targetpath)
local relpath = path.relative(targetpath, fromdir) or targetpath
if os.host() == "windows" then
relpath = relpath:gsub("\\", "/")
@@ -26,7 +26,7 @@ local function _normalize_relative(fromdir, targetpath)
return relpath
end
-local function _collect_cs_sourcefiles(context)
+function _collect_cs_sourcefiles(context)
local csfiles = {}
for _, sourcefile in ipairs(context.target:sourcefiles()) do
if path.extension(sourcefile):lower() == ".cs" then
@@ -38,7 +38,7 @@ local function _collect_cs_sourcefiles(context)
return table.unique(csfiles)
end
-local function _collect_project_references(context)
+function _collect_project_references(context)
if context.opt.skip_deps then
return {}
end
@@ -56,7 +56,7 @@ local function _collect_project_references(context)
return table.unique(references)
end
-local function _get_nuget_info(pkg)
+function _get_nuget_info(pkg)
local requirestr = pkg:requirestr() or ""
local splitinfo = requirestr:trim():split("%s+")
if #splitinfo == 0 then
@@ -90,7 +90,7 @@ local function _get_nuget_info(pkg)
return pkgname, version
end
-local function _collect_nuget_references(context)
+function _collect_nuget_references(context)
local versions = {}
for _, pkg in ipairs(context.target:orderpkgs()) do
local namespace = pkg:namespace()
--
cgit v1.3.1
From a86bde866d5f65d5fbd31f1474190dcc40aa2876 Mon Sep 17 00:00:00 2001
From: ruki
Date: Sat, 14 Mar 2026 00:04:01 +0800
Subject: improve csharp support
---
xmake/modules/core/tools/dotnet.lua | 88 +++++++++++++++++
xmake/modules/core/tools/dotnet/has_flags.lua | 50 ++++++++++
xmake/modules/detect/sdks/find_dotnet.lua | 134 +++++++++++++++++---------
xmake/modules/detect/tools/find_dotnet.lua | 53 ++++++++++
xmake/toolchains/dotnet/xmake.lua | 60 ++++++++++++
5 files changed, 342 insertions(+), 43 deletions(-)
create mode 100644 xmake/modules/core/tools/dotnet.lua
create mode 100644 xmake/modules/core/tools/dotnet/has_flags.lua
create mode 100644 xmake/modules/detect/tools/find_dotnet.lua
create mode 100644 xmake/toolchains/dotnet/xmake.lua
diff --git a/xmake/modules/core/tools/dotnet.lua b/xmake/modules/core/tools/dotnet.lua
new file mode 100644
index 000000000..4cc96e317
--- /dev/null
+++ b/xmake/modules/core/tools/dotnet.lua
@@ -0,0 +1,88 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author ruki
+-- @file dotnet.lua
+--
+
+-- imports
+import("core.base.option")
+import("core.project.config")
+import("core.project.project")
+import("core.language.language")
+
+-- init it
+function init(self)
+end
+
+-- make the define flag
+function nf_define(self, macro)
+ return {"-p:DefineConstants=" .. macro}
+end
+
+-- make the optimize flag
+function nf_optimize(self, level)
+ local maps = {
+ none = "-p:Optimize=false"
+ , fast = "-p:Optimize=true"
+ , faster = "-p:Optimize=true"
+ , fastest = "-p:Optimize=true"
+ , smallest = "-p:Optimize=true"
+ , aggressive = "-p:Optimize=true"
+ }
+ return maps[level]
+end
+
+-- make the symbol flag
+function nf_symbol(self, level)
+ local maps = {
+ debug = {"-p:DebugType=full", "-p:DebugSymbols=true"}
+ , hidden = {"-p:DebugType=pdbonly", "-p:DebugSymbols=true"}
+ }
+ return maps[level]
+end
+
+-- make the warning flag
+function nf_warning(self, level)
+ local maps = {
+ none = "-p:WarningLevel=0"
+ , less = "-p:WarningLevel=1"
+ , more = "-p:WarningLevel=3"
+ , all = "-p:WarningLevel=5"
+ , allextra = "-p:WarningLevel=9999"
+ , error = {"-p:WarningLevel=5", "-p:TreatWarningsAsErrors=true"}
+ }
+ return maps[level]
+end
+
+-- make the build arguments list
+function buildargv(self, sourcefiles, targetkind, targetfile, flags)
+ local argv = {"build"}
+ if flags then
+ table.join2(argv, flags)
+ end
+ table.insert(argv, "--output")
+ table.insert(argv, path.directory(targetfile))
+ table.join2(argv, sourcefiles)
+ return self:program(), argv
+end
+
+-- build the target file
+function build(self, sourcefiles, targetkind, targetfile, flags)
+ os.mkdir(path.directory(targetfile))
+ local program, argv = buildargv(self, sourcefiles, targetkind, targetfile, flags)
+ os.runv(program, argv, {envs = self:runenvs()})
+end
diff --git a/xmake/modules/core/tools/dotnet/has_flags.lua b/xmake/modules/core/tools/dotnet/has_flags.lua
new file mode 100644
index 000000000..eaffa7e88
--- /dev/null
+++ b/xmake/modules/core/tools/dotnet/has_flags.lua
@@ -0,0 +1,50 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author ruki
+-- @file has_flags.lua
+--
+
+-- attempt to check it from known flags
+function _check_from_knownargs(flags, opt)
+ local flag = flags[1]
+ -- dotnet MSBuild properties
+ if flag:startswith("-p:") or flag:startswith("/p:") then
+ return true
+ end
+ -- dotnet CLI options
+ if flag:startswith("--") then
+ return true
+ end
+ -- common csflags patterns
+ if flag:startswith("-") or flag:startswith("/") then
+ return true
+ end
+end
+
+-- has_flags(flags)?
+--
+-- @param opt the argument options, e.g. {toolname = "", program = "", programver = "", toolkind = "[cs|csld|cssh]"}
+--
+-- @return true or false
+--
+function main(flags, opt)
+ opt = opt or {}
+ if _check_from_knownargs(flags, opt) then
+ return true
+ end
+ return false
+end
diff --git a/xmake/modules/detect/sdks/find_dotnet.lua b/xmake/modules/detect/sdks/find_dotnet.lua
index 2dfd2a916..3430d2626 100644
--- a/xmake/modules/detect/sdks/find_dotnet.lua
+++ b/xmake/modules/detect/sdks/find_dotnet.lua
@@ -20,16 +20,60 @@
-- imports
import("lib.detect.find_file")
+import("lib.detect.find_tool")
import("core.tool.toolchain")
import("core.base.option")
import("core.base.global")
import("core.project.config")
import("core.cache.detectcache")
--- find dotnet directory
-function _find_sdkdir(sdkdir)
+-- find dotnet sdk info from dotnet cli
+function _find_dotnet_cli()
+ local dotnet = find_tool("dotnet", {version = true})
+ if not dotnet then
+ return nil
+ end
- -- get sdk directory from vcvars
+ local result = {program = dotnet.program, version = dotnet.version}
+
+ -- get sdk list, e.g. "8.0.100 [/usr/share/dotnet/sdk]"
+ local sdklist = try { function () return os.iorunv(dotnet.program, {"--list-sdks"}) end }
+ if sdklist then
+ local sdks = {}
+ for _, line in ipairs(sdklist:split("\n", {plain = true})) do
+ line = line:trim()
+ local ver, dir = line:match("^(%S+)%s+%[(.-)%]")
+ if ver and dir then
+ table.insert(sdks, {version = ver, directory = path.join(dir, ver)})
+ end
+ end
+ if #sdks > 0 then
+ result.sdks = sdks
+ result.sdkdir = sdks[#sdks].directory
+ result.sdkver = sdks[#sdks].version
+ end
+ end
+
+ -- get runtime list, e.g. "Microsoft.NETCore.App 8.0.0 [/usr/share/dotnet/shared/Microsoft.NETCore.App]"
+ local runtimelist = try { function () return os.iorunv(dotnet.program, {"--list-runtimes"}) end }
+ if runtimelist then
+ local runtimes = {}
+ for _, line in ipairs(runtimelist:split("\n", {plain = true})) do
+ line = line:trim()
+ local name, ver = line:match("^(%S+)%s+(%S+)%s+%[")
+ if name and ver then
+ table.insert(runtimes, {name = name, version = ver})
+ end
+ end
+ if #runtimes > 0 then
+ result.runtimes = runtimes
+ end
+ end
+ return result
+end
+
+-- find .NET Framework SDK directory from MSVC (Windows only)
+function _find_netfxsdk(sdkdir)
if not sdkdir then
local msvc = toolchain.load("msvc")
if msvc and msvc:check() then
@@ -42,55 +86,40 @@ function _find_sdkdir(sdkdir)
end
end
end
- return sdkdir
-end
-
--- find dotnet toolchains
-function _find_dotnet(sdkdir, sdkver)
-
- -- find dotnet directory
- sdkdir = _find_sdkdir(sdkdir)
if not sdkdir or not os.isdir(sdkdir) then
return nil
end
-- get sdk version
- if not sdkver then
- local vers = {}
- for _, dir in ipairs(os.dirs(path.join(sdkdir, "*", "Include"))) do
- table.insert(vers, path.filename(path.directory(dir)))
- end
- for _, ver in ipairs(vers) do
- if os.isdir(path.join(sdkdir, ver, "Lib", "um")) and os.isdir(path.join(sdkdir, ver, "Include", "um")) then
- sdkver = ver
- break
- end
+ local sdkver
+ local vers = {}
+ for _, dir in ipairs(os.dirs(path.join(sdkdir, "*", "Include"))) do
+ table.insert(vers, path.filename(path.directory(dir)))
+ end
+ for _, ver in ipairs(vers) do
+ if os.isdir(path.join(sdkdir, ver, "Lib", "um")) and os.isdir(path.join(sdkdir, ver, "Include", "um")) then
+ sdkver = ver
+ break
end
end
if not sdkver then
return nil
end
-
- -- get the lib directory
- local libdir = path.join(sdkdir, sdkver, "Lib")
-
- -- get the include directory
- local includedir = path.join(sdkdir, sdkver, "Include")
-
- -- get toolchains
- return {sdkdir = sdkdir, libdir = libdir, includedir = includedir, sdkver = sdkver}
+ return {sdkdir = sdkdir, sdkver = sdkver,
+ libdir = path.join(sdkdir, sdkver, "Lib"),
+ includedir = path.join(sdkdir, sdkver, "Include")}
end
--- find dotnet toolchains
+-- find dotnet sdk
--
-- @param sdkdir the dotnet directory
--- @param opt the argument options, e.g. {verbose = true, force = false, version = "5.9.1"}
+-- @param opt the argument options, e.g. {verbose = true, force = false}
--
--- @return the dotnet toolchains. e.g. {sdkver = ..., sdkdir = ..., bindir = .., libdir = ..., includedir = ..., .. }
+-- @return the dotnet sdk info, e.g. {program = ..., version = ..., sdkver = ..., sdkdir = ..., sdks = {...}, runtimes = {...}}
--
-- @code
--
--- local toolchains = find_dotnet("~/dotnet")
+-- local dotnet = find_dotnet()
--
-- @endcode
--
@@ -106,24 +135,43 @@ function main(sdkdir, opt)
return cacheinfo.dotnet
end
- -- find dotnet
- local dotnet = _find_dotnet(sdkdir or config.get("dotnet") or global.get("dotnet"), opt.version or config.get("dotnet_sdkver"))
- if dotnet then
+ -- find dotnet cli sdk
+ local dotnet = _find_dotnet_cli()
+
+ -- find .NET Framework SDK on Windows
+ if is_host("windows") then
+ local netfxsdk = _find_netfxsdk(sdkdir or config.get("dotnet") or global.get("dotnet"))
+ if netfxsdk then
+ if dotnet then
+ dotnet.netfxsdk = netfxsdk
+ else
+ dotnet = netfxsdk
+ end
+ end
+ end
+ if dotnet then
-- save to config
- config.set("dotnet", dotnet.sdkdir, {force = true, readonly = true})
- config.set("dotnet_sdkver", dotnet.sdkver, {force = true, readonly = true})
+ if dotnet.sdkdir then
+ config.set("dotnet", dotnet.sdkdir, {force = true, readonly = true})
+ end
+ if dotnet.sdkver then
+ config.set("dotnet_sdkver", dotnet.sdkver, {force = true, readonly = true})
+ end
-- trace
if opt.verbose or option.get("verbose") then
- cprint("checking for .Net SDK directory ... ${color.success}%s", dotnet.sdkdir)
- cprint("checking for .Net SDK version ... ${color.success}%s", dotnet.sdkver)
+ if dotnet.version then
+ cprint("checking for .NET SDK version ... ${color.success}%s", dotnet.version)
+ end
+ if dotnet.sdkdir then
+ cprint("checking for .NET SDK directory ... ${color.success}%s", dotnet.sdkdir)
+ end
end
else
-
-- trace
if opt.verbose or option.get("verbose") then
- cprint("checking for .Net SDK directory ... ${color.nothing}${text.nothing}")
+ cprint("checking for .NET SDK directory ... ${color.nothing}${text.nothing}")
end
end
diff --git a/xmake/modules/detect/tools/find_dotnet.lua b/xmake/modules/detect/tools/find_dotnet.lua
new file mode 100644
index 000000000..ac17979ec
--- /dev/null
+++ b/xmake/modules/detect/tools/find_dotnet.lua
@@ -0,0 +1,53 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author ruki
+-- @file find_dotnet.lua
+--
+
+-- imports
+import("lib.detect.find_program")
+import("lib.detect.find_programver")
+
+-- find dotnet
+--
+-- @param opt the argument options, e.g. {version = true}
+--
+-- @return program, version
+--
+-- @code
+--
+-- local dotnet = find_dotnet()
+--
+-- @endcode
+--
+function main(opt)
+
+ -- init options
+ opt = opt or {}
+ opt.check = opt.check or "--version"
+ opt.command = opt.command or "--version"
+
+ -- find program
+ local program = find_program(opt.program or "dotnet", opt)
+
+ -- find program version
+ local version = nil
+ if program and opt and opt.version then
+ version = find_programver(program, opt)
+ end
+ return program, version
+end
diff --git a/xmake/toolchains/dotnet/xmake.lua b/xmake/toolchains/dotnet/xmake.lua
new file mode 100644
index 000000000..9c1fe40c3
--- /dev/null
+++ b/xmake/toolchains/dotnet/xmake.lua
@@ -0,0 +1,60 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author ruki
+-- @file xmake.lua
+--
+
+toolchain("dotnet")
+ set_kind("standalone")
+ set_homepage("https://dotnet.microsoft.com/")
+ set_description(".NET SDK Toolchain")
+
+ set_toolset("cs", "dotnet")
+ set_toolset("csld", "dotnet")
+ set_toolset("cssh", "dotnet")
+
+ on_check(function (toolchain)
+ import("lib.detect.find_tool")
+ import("detect.sdks.find_dotnet")
+
+ -- find dotnet program
+ local dotnet = find_tool("dotnet", {version = true})
+ if not dotnet then
+ return false
+ end
+ toolchain:config_set("dotnet", dotnet.program)
+ toolchain:config_set("dotnet_version", dotnet.version)
+
+ -- find dotnet sdk info
+ local sdkinfo = find_dotnet()
+ if sdkinfo then
+ toolchain:config_set("sdkinfo", sdkinfo)
+ end
+ return true
+ end)
+
+ on_load(function (toolchain)
+ local dotnet = toolchain:config("dotnet") or "dotnet"
+ toolchain:set("toolset", "cs", dotnet)
+ toolchain:set("toolset", "csld", dotnet)
+ toolchain:set("toolset", "cssh", dotnet)
+
+ -- set default environment variables
+ toolchain:add("runenvs", "DOTNET_NOLOGO", "1")
+ toolchain:add("runenvs", "DOTNET_CLI_TELEMETRY_OPTOUT", "1")
+ toolchain:add("runenvs", "DOTNET_SKIP_FIRST_TIME_EXPERIENCE", "1")
+ end)
--
cgit v1.3.1
From 22976f06349a3af4b520ae5796ab20edc95bfe0c Mon Sep 17 00:00:00 2001
From: ruki
Date: Sat, 14 Mar 2026 23:02:18 +0800
Subject: improve find_dotnet
---
xmake/modules/detect/sdks/find_dotnet.lua | 27 +++++++++++++++++++------
xmake/toolchains/dotnet/xmake.lua | 33 +++++++++++++++++--------------
2 files changed, 39 insertions(+), 21 deletions(-)
diff --git a/xmake/modules/detect/sdks/find_dotnet.lua b/xmake/modules/detect/sdks/find_dotnet.lua
index 3430d2626..f64acacd3 100644
--- a/xmake/modules/detect/sdks/find_dotnet.lua
+++ b/xmake/modules/detect/sdks/find_dotnet.lua
@@ -28,13 +28,21 @@ import("core.project.config")
import("core.cache.detectcache")
-- find dotnet sdk info from dotnet cli
-function _find_dotnet_cli()
- local dotnet = find_tool("dotnet", {version = true})
+function _find_dotnet_cli(sdkdir)
+
+ -- find dotnet program
+ local paths = {}
+ if sdkdir then
+ table.insert(paths, path.join(sdkdir, "bin"))
+ table.insert(paths, sdkdir)
+ end
+ local dotnet = find_tool("dotnet", {version = true, paths = #paths > 0 and paths or nil})
if not dotnet then
return nil
end
- local result = {program = dotnet.program, version = dotnet.version}
+ local bindir = path.directory(dotnet.program)
+ local result = {bindir = bindir, version = dotnet.version}
-- get sdk list, e.g. "8.0.100 [/usr/share/dotnet/sdk]"
local sdklist = try { function () return os.iorunv(dotnet.program, {"--list-sdks"}) end }
@@ -54,6 +62,11 @@ function _find_dotnet_cli()
end
end
+ -- set sdkdir from bindir if not found from sdk list
+ if not result.sdkdir then
+ result.sdkdir = sdkdir or path.directory(bindir)
+ end
+
-- get runtime list, e.g. "Microsoft.NETCore.App 8.0.0 [/usr/share/dotnet/shared/Microsoft.NETCore.App]"
local runtimelist = try { function () return os.iorunv(dotnet.program, {"--list-runtimes"}) end }
if runtimelist then
@@ -129,14 +142,14 @@ function main(sdkdir, opt)
opt = opt or {}
-- attempt to load cache first
- local key = "detect.sdks.find_dotnet." .. (sdkdir or "")
+ local key = "detect.sdks.find_dotnet"
local cacheinfo = detectcache:get(key) or {}
- if not opt.force and cacheinfo.dotnet then
+ if not opt.force and cacheinfo.dotnet and cacheinfo.dotnet.sdkdir and os.isdir(cacheinfo.dotnet.sdkdir) then
return cacheinfo.dotnet
end
-- find dotnet cli sdk
- local dotnet = _find_dotnet_cli()
+ local dotnet = _find_dotnet_cli(sdkdir or config.get("dotnet") or global.get("dotnet"))
-- find .NET Framework SDK on Windows
if is_host("windows") then
@@ -151,6 +164,7 @@ function main(sdkdir, opt)
end
if dotnet then
+
-- save to config
if dotnet.sdkdir then
config.set("dotnet", dotnet.sdkdir, {force = true, readonly = true})
@@ -169,6 +183,7 @@ function main(sdkdir, opt)
end
end
else
+
-- trace
if opt.verbose or option.get("verbose") then
cprint("checking for .NET SDK directory ... ${color.nothing}${text.nothing}")
diff --git a/xmake/toolchains/dotnet/xmake.lua b/xmake/toolchains/dotnet/xmake.lua
index 9c1fe40c3..6a02b0a0a 100644
--- a/xmake/toolchains/dotnet/xmake.lua
+++ b/xmake/toolchains/dotnet/xmake.lua
@@ -28,30 +28,33 @@ toolchain("dotnet")
set_toolset("cssh", "dotnet")
on_check(function (toolchain)
- import("lib.detect.find_tool")
import("detect.sdks.find_dotnet")
- -- find dotnet program
- local dotnet = find_tool("dotnet", {version = true})
- if not dotnet then
- return false
+ -- find dotnet sdk from packages first
+ local sdkinfo
+ for _, package in ipairs(toolchain:packages()) do
+ local installdir = package:installdir()
+ if installdir and os.isdir(installdir) then
+ sdkinfo = find_dotnet(installdir, {force = true})
+ if sdkinfo then
+ break
+ end
+ end
end
- toolchain:config_set("dotnet", dotnet.program)
- toolchain:config_set("dotnet_version", dotnet.version)
- -- find dotnet sdk info
- local sdkinfo = find_dotnet()
- if sdkinfo then
- toolchain:config_set("sdkinfo", sdkinfo)
+ -- find dotnet sdk from system
+ if not sdkinfo then
+ sdkinfo = find_dotnet()
+ end
+ if not sdkinfo or not sdkinfo.bindir then
+ return false
end
+ toolchain:config_set("bindir", sdkinfo.bindir)
+ toolchain:config_set("sdkdir", sdkinfo.sdkdir)
return true
end)
on_load(function (toolchain)
- local dotnet = toolchain:config("dotnet") or "dotnet"
- toolchain:set("toolset", "cs", dotnet)
- toolchain:set("toolset", "csld", dotnet)
- toolchain:set("toolset", "cssh", dotnet)
-- set default environment variables
toolchain:add("runenvs", "DOTNET_NOLOGO", "1")
--
cgit v1.3.1
From 7fa59f32b5c88c655d83a83bb05e3eb9680e5004 Mon Sep 17 00:00:00 2001
From: ruki
Date: Sat, 14 Mar 2026 23:17:32 +0800
Subject: improve csharp rules
---
xmake/modules/core/tools/dotnet.lua | 57 ++++++++++--
xmake/rules/csharp/buildcmd.lua | 88 -------------------
xmake/rules/csharp/install.lua | 81 ++++++-----------
xmake/rules/csharp/installcmd.lua | 59 -------------
xmake/rules/csharp/load.lua | 65 --------------
xmake/rules/csharp/modules/csharp_common.lua | 61 -------------
xmake/rules/csharp/xmake.lua | 126 ++++++---------------------
7 files changed, 106 insertions(+), 431 deletions(-)
delete mode 100644 xmake/rules/csharp/buildcmd.lua
delete mode 100644 xmake/rules/csharp/installcmd.lua
delete mode 100644 xmake/rules/csharp/load.lua
diff --git a/xmake/modules/core/tools/dotnet.lua b/xmake/modules/core/tools/dotnet.lua
index 4cc96e317..2647be1be 100644
--- a/xmake/modules/core/tools/dotnet.lua
+++ b/xmake/modules/core/tools/dotnet.lua
@@ -68,21 +68,66 @@ function nf_warning(self, level)
return maps[level]
end
+-- get the .csproj file from source files
+function _get_csprojfile(sourcefiles, opt)
+ local target = opt and opt.target
+ if target then
+ local csprojfile = target:data("csharp.csproj")
+ if csprojfile then
+ return csprojfile
+ end
+ end
+ -- find .csproj from source files
+ for _, sourcefile in ipairs(sourcefiles) do
+ if sourcefile:endswith(".csproj") then
+ return sourcefile
+ end
+ end
+end
+
+-- get dotnet verbosity level
+function _get_verbosity()
+ if option.get("diagnosis") then
+ return "diagnostic"
+ end
+ return "quiet"
+end
+
+-- get build configuration from mode
+function _get_configuration()
+ local mode = config.mode() or "release"
+ if mode:lower() == "debug" then
+ return "Debug"
+ end
+ return "Release"
+end
+
-- make the build arguments list
-function buildargv(self, sourcefiles, targetkind, targetfile, flags)
+function buildargv(self, sourcefiles, targetkind, targetfile, flags, opt)
local argv = {"build"}
+
+ -- add .csproj file
+ local csprojfile = _get_csprojfile(sourcefiles, opt)
+ if csprojfile then
+ table.insert(argv, csprojfile)
+ end
+
+ -- add common options
+ table.join2(argv, {"--nologo", "--configuration", _get_configuration(), "--verbosity", _get_verbosity()})
+
+ -- add output directory
+ table.join2(argv, {"--output", path.directory(targetfile)})
+
+ -- add flags
if flags then
table.join2(argv, flags)
end
- table.insert(argv, "--output")
- table.insert(argv, path.directory(targetfile))
- table.join2(argv, sourcefiles)
return self:program(), argv
end
-- build the target file
-function build(self, sourcefiles, targetkind, targetfile, flags)
+function build(self, sourcefiles, targetkind, targetfile, flags, opt)
os.mkdir(path.directory(targetfile))
- local program, argv = buildargv(self, sourcefiles, targetkind, targetfile, flags)
+ local program, argv = buildargv(self, sourcefiles, targetkind, targetfile, flags, opt)
os.runv(program, argv, {envs = self:runenvs()})
end
diff --git a/xmake/rules/csharp/buildcmd.lua b/xmake/rules/csharp/buildcmd.lua
deleted file mode 100644
index 614cc5a25..000000000
--- a/xmake/rules/csharp/buildcmd.lua
+++ /dev/null
@@ -1,88 +0,0 @@
---!A cross-platform build utility based on Lua
---
--- Licensed under the Apache License, Version 2.0 (the "License");
--- you may not use this file except in compliance with the License.
--- You may obtain a copy of the License at
---
--- http://www.apache.org/licenses/LICENSE-2.0
---
--- Unless required by applicable law or agreed to in writing, software
--- distributed under the License is distributed on an "AS IS" BASIS,
--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--- See the License for the specific language governing permissions and
--- limitations under the License.
---
--- Copyright (C) 2015-present, Xmake Open Source Community.
---
--- @author JassJam
--- @file buildcmd.lua
---
-
-import("modules.csharp_common", {rootdir = os.scriptdir(), alias = "csharp_common"})
-
-function _get_output_mtime(target, targetfile, targetdirabs)
- local mtime = targetfile and os.mtime(targetfile) or nil
- if mtime then
- return mtime
- end
-
- if targetdirabs and os.isdir(targetdirabs) then
- -- dotnet can place outputs under tfm/rid subdirectories depending on
- -- project properties, so scan recursively for a matching assembly name.
- local basename = target:basename() or target:name()
- for _, ext in ipairs({".exe", ".dll", ".so", ".dylib"}) do
- for _, file in ipairs(os.files(path.join(targetdirabs, "**", basename .. ext))) do
- local filemtime = os.mtime(file)
- if filemtime and (not mtime or filemtime > mtime) then
- mtime = filemtime
- end
- end
- end
- if not mtime then
- mtime = os.mtime(targetdirabs)
- end
- end
- return mtime
-end
-
-function main(target, batchcmds, opt)
- local csprojfile = assert(csharp_common.find_or_generate_csproj(target), "target(%s): missing csharp .csproj file!", target:name())
- local dotnet = csharp_common.get_dotnet_program(target)
- local configuration = csharp_common.build_mode_to_configuration()
- local verbosity = csharp_common.get_dotnet_verbosity()
- local command = target:is_binary() and "publish" or "build"
- local argv = {
- command, csprojfile,
- "--nologo",
- "--configuration", configuration,
- "--verbosity", verbosity
- }
-
- local targetdir = target:targetdir()
- local targetdirabs
- if targetdir then
- targetdirabs = path.is_absolute(targetdir) and targetdir or path.absolute(targetdir, os.projectdir())
- table.join2(argv, {"--output", targetdirabs})
- end
-
- local rid = csharp_common.get_runtime_identifier(target)
- if rid and target:is_binary() then
- table.join2(argv, {"--runtime", rid})
- end
- csharp_common.append_target_flags(target, argv)
-
- batchcmds:show_progress(opt.progress, "${color.build.target}building.csharp.$(mode) %s", target:name())
- if targetdirabs then
- batchcmds:mkdir(targetdirabs)
- end
- batchcmds:vrunv(dotnet, argv, csharp_common.get_dotnet_runopt(csprojfile))
-
- local targetfile = target:targetfile()
- if targetfile then
- local depfiles = table.wrap(target:sourcefiles())
- table.insert(depfiles, csprojfile)
- batchcmds:add_depfiles(table.unique(depfiles))
- batchcmds:set_depmtime(_get_output_mtime(target, targetfile, targetdirabs))
- batchcmds:set_depcache(target:dependfile(targetfile))
- end
-end
diff --git a/xmake/rules/csharp/install.lua b/xmake/rules/csharp/install.lua
index 32cc9e8e0..4d871e291 100644
--- a/xmake/rules/csharp/install.lua
+++ b/xmake/rules/csharp/install.lua
@@ -14,38 +14,28 @@
--
-- Copyright (C) 2015-present, Xmake Open Source Community.
--
--- @author JassJam
+-- @author ruki
-- @file install.lua
--
+import("core.base.option")
+import("core.project.config")
+import("core.tool.compiler")
import("modules.csharp_common", {rootdir = os.scriptdir(), alias = "csharp_common"})
function main(target, opt)
- local function _q(arg)
- arg = tostring(arg)
- if arg:find("[%s\"]") then
- arg = "\"" .. arg:gsub("\"", "\\\"") .. "\""
- end
- return arg
- end
-
local csprojfile = assert(csharp_common.find_or_generate_csproj(target), "target(%s): missing csharp .csproj file!", target:name())
- local dotnet = csharp_common.get_dotnet_program(target)
- local configuration = csharp_common.build_mode_to_configuration()
- local verbosity = csharp_common.get_dotnet_verbosity()
- local install_path = target:installdir()
- if target:is_binary() then
- install_path = target:installdir("bin")
- elseif target:is_static() or target:is_shared() then
- install_path = target:installdir("lib")
- end
- if not install_path or #install_path == 0 then
- return
- end
+ -- get dotnet program from compiler toolchain
+ local compinst = compiler.load("cs", {target = target})
+ local dotnet = compinst:program()
- local install_abs = path.is_absolute(install_path) and install_path or path.absolute(install_path, os.projectdir())
- os.mkdir(install_abs)
+ local configuration = csharp_common.build_mode_to_configuration()
+ local verbosity = option.get("diagnosis") and "diagnostic" or "quiet"
+
+ -- publish to a directory under target autogendir
+ local tmpdir = path.join(target:autogendir(), "publish")
+ os.mkdir(tmpdir)
local rid = csharp_common.get_runtime_identifier(target)
local argv = {
@@ -53,43 +43,24 @@ function main(target, opt)
"--nologo",
"--configuration", configuration,
"--verbosity", verbosity,
- "--output", install_abs
+ "--output", tmpdir
}
if rid and target:is_binary() then
table.join2(argv, {"--runtime", rid})
end
- csharp_common.append_target_flags(target, argv)
-
- local runopt = csharp_common.get_dotnet_runopt(csprojfile)
- if os.vrunv then
- os.vrunv(dotnet, argv, runopt)
- elseif os.runv then
- os.runv(dotnet, argv, runopt)
- elseif os.execv then
- os.execv(dotnet, argv, runopt)
- elseif os.vrun then
- local cmd = _q(dotnet)
- for _, arg in ipairs(argv) do
- cmd = cmd .. " " .. _q(arg)
- end
- os.vrun(cmd, runopt)
- elseif os.run then
- local cmd = _q(dotnet)
- for _, arg in ipairs(argv) do
- cmd = cmd .. " " .. _q(arg)
- end
- os.run(cmd, runopt)
- else
- local targetdir = target:targetdir()
- if targetdir and os.isdir(targetdir) then
- os.cp(path.join(targetdir, "**"), install_abs, {rootdir = targetdir})
- end
- end
+ os.vrunv(dotnet, argv, {curdir = path.directory(csprojfile), envs = compinst:runenvs()})
+ -- copy published files to installdir
+ local installdir = target:installdir()
if target:is_binary() then
- local targetdir = target:targetdir()
- if targetdir and os.isdir(targetdir) then
- os.cp(path.join(targetdir, "**"), install_abs, {rootdir = targetdir})
- end
+ installdir = target:installdir("bin")
+ elseif target:is_static() or target:is_shared() then
+ installdir = target:installdir("lib")
+ end
+ if installdir and #installdir > 0 then
+ local install_abs = path.is_absolute(installdir) and installdir or path.absolute(installdir, os.projectdir())
+ os.mkdir(install_abs)
+ os.cp(path.join(tmpdir, "**"), install_abs, {rootdir = tmpdir})
end
+
end
diff --git a/xmake/rules/csharp/installcmd.lua b/xmake/rules/csharp/installcmd.lua
deleted file mode 100644
index 7e1e385b4..000000000
--- a/xmake/rules/csharp/installcmd.lua
+++ /dev/null
@@ -1,59 +0,0 @@
---!A cross-platform build utility based on Lua
---
--- Licensed under the Apache License, Version 2.0 (the "License");
--- you may not use this file except in compliance with the License.
--- You may obtain a copy of the License at
---
--- http://www.apache.org/licenses/LICENSE-2.0
---
--- Unless required by applicable law or agreed to in writing, software
--- distributed under the License is distributed on an "AS IS" BASIS,
--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--- See the License for the specific language governing permissions and
--- limitations under the License.
---
--- Copyright (C) 2015-present, Xmake Open Source Community.
---
--- @author JassJam
--- @file installcmd.lua
---
-
-import("modules.csharp_common", {rootdir = os.scriptdir(), alias = "csharp_common"})
-
-function main(target, batchcmds, opt)
- local csprojfile = assert(csharp_common.find_or_generate_csproj(target), "target(%s): missing csharp .csproj file!", target:name())
- local dotnet = csharp_common.get_dotnet_program(target)
- local configuration = csharp_common.build_mode_to_configuration()
- local verbosity = csharp_common.get_dotnet_verbosity()
-
- local install_path = target:installdir()
- if target:is_binary() then
- install_path = target:installdir("bin")
- elseif target:is_static() or target:is_shared() then
- install_path = target:installdir("lib")
- end
-
- local install_abs
- local argv = {
- "publish", csprojfile,
- "--nologo",
- "--configuration", configuration,
- "--verbosity", verbosity
- }
- if install_path and #install_path > 0 then
- install_abs = path.is_absolute(install_path) and install_path or path.absolute(install_path, os.projectdir())
- table.join2(argv, {"--output", install_abs})
- end
-
- local rid = csharp_common.get_runtime_identifier(target)
- if rid and target:is_binary() then
- table.join2(argv, {"--runtime", rid})
- end
- csharp_common.append_target_flags(target, argv)
-
- batchcmds:show_progress(opt.progress, "${color.build.target}publishing.csharp.$(mode) %s", target:name())
- if install_abs then
- batchcmds:mkdir(install_abs)
- end
- batchcmds:vrunv(dotnet, argv, csharp_common.get_dotnet_runopt(csprojfile))
-end
diff --git a/xmake/rules/csharp/load.lua b/xmake/rules/csharp/load.lua
deleted file mode 100644
index 14b53a1fa..000000000
--- a/xmake/rules/csharp/load.lua
+++ /dev/null
@@ -1,65 +0,0 @@
---!A cross-platform build utility based on Lua
---
--- Licensed under the Apache License, Version 2.0 (the "License");
--- you may not use this file except in compliance with the License.
--- You may obtain a copy of the License at
---
--- http://www.apache.org/licenses/LICENSE-2.0
---
--- Unless required by applicable law or agreed to in writing, software
--- distributed under the License is distributed on an "AS IS" BASIS,
--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--- See the License for the specific language governing permissions and
--- limitations under the License.
---
--- Copyright (C) 2015-present, Xmake Open Source Community.
---
--- @author JassJam
--- @file load.lua
---
-
-import("modules.csharp_common", {rootdir = os.scriptdir(), alias = "csharp_common"})
-
-function main(target)
- local targetdir = target:targetdir()
- if targetdir and path.filename(targetdir) ~= target:name() then
- target:set("targetdir", path.join(targetdir, target:name()))
- end
-
- if target:is_static() or target:is_shared() then
- if not target:get("extension") then
- target:set("extension", ".dll")
- end
- if target:get("prefixname") == nil then
- target:set("prefixname", "")
- end
- end
-
- -- find and cache the .csproj file for later use in build/install
- local csprojfiles = {}
- for _, sourcefile in ipairs(target:sourcefiles()) do
- if path.extension(sourcefile):lower() == ".csproj" then
- table.insert(csprojfiles, sourcefile)
- end
- end
- assert(#csprojfiles <= 1, "target(%s): csharp only supports one .csproj file!", target:name())
-
- local csprojabs = nil
- if #csprojfiles == 1 then
- local csprojfile = csprojfiles[1]
- local csprojpath = path.is_absolute(csprojfile) and csprojfile or path.absolute(csprojfile, os.projectdir())
- if os.isfile(csprojpath) then
- csprojabs = csprojpath
- end
- end
-
- if not csprojabs then
- csprojabs = csharp_common.find_or_generate_csproj(target, {skip_deps = true})
- end
- assert(csprojabs, "target(%s): csharp failed to resolve or generate .csproj file!", target:name())
-
- if not target:get("filename") and not target:get("basename") then
- target:set("basename", path.basename(csprojabs))
- end
- target:data_set("csharp.csproj", csprojabs)
-end
diff --git a/xmake/rules/csharp/modules/csharp_common.lua b/xmake/rules/csharp/modules/csharp_common.lua
index dce35021a..516297a45 100644
--- a/xmake/rules/csharp/modules/csharp_common.lua
+++ b/xmake/rules/csharp/modules/csharp_common.lua
@@ -169,64 +169,3 @@ function get_runtime_identifier(target)
return nil
end
-function append_target_flags(target, argv)
- local flags = {}
- table.join2(flags, table.wrap(target:get("csflags")))
- table.join2(flags, table.wrap(target:get("ldflags")))
- table.join2(flags, table.wrap(target:get("arflags")))
- table.join2(flags, table.wrap(target:get("shflags")))
- for _, flag in ipairs(flags) do
- if flag and #flag > 0 then
- table.insert(argv, flag)
- end
- end
-end
-
-function get_dotnet_runopt(csprojfile)
- return {
- curdir = path.directory(csprojfile),
- envs = {
- DOTNET_NOLOGO = "1",
- DOTNET_CLI_TELEMETRY_OPTOUT = "1",
- DOTNET_SKIP_FIRST_TIME_EXPERIENCE = "1",
- DOTNET_GENERATE_ASPNET_CERTIFICATE = "0",
- DOTNET_ADD_GLOBAL_TOOLS_TO_PATH = "0"
- }
- }
-end
-
-function get_dotnet_verbosity()
- if option.get("diagnosis") then
- return "diagnostic"
- end
- return "quiet"
-end
-
-function get_dotnet_program(target)
- local function _get_configured_program(toolkind)
- if target:get("toolset." .. toolkind) then
- local program = target:tool(toolkind)
- if program and #program > 0 then
- return program
- end
- end
- end
-
- local program = nil
- if target:is_binary() then
- program = _get_configured_program("ld")
- elseif target:is_shared() then
- program = _get_configured_program("sh")
- else
- program = _get_configured_program("cs")
- end
- if program then
- return program
- end
-
- program = target:tool("cs")
- if program and #program > 0 then
- return program
- end
- return "dotnet"
-end
diff --git a/xmake/rules/csharp/xmake.lua b/xmake/rules/csharp/xmake.lua
index b5e76dfa9..9ad5f7974 100644
--- a/xmake/rules/csharp/xmake.lua
+++ b/xmake/rules/csharp/xmake.lua
@@ -14,104 +14,36 @@
--
-- Copyright (C) 2015-present, Xmake Open Source Community.
--
--- @author JassJam
+-- @author ruki
-- @file xmake.lua
--
--- define rule: build csharp target with dotnet
---
--- @code
--- target("app")
--- set_kind("binary") -- or "shared"
--- add_rules("csharp")
--- add_files("src/*.cs")
---
--- -- optional: if a .csproj is provided, xmake uses it directly.
--- -- otherwise xmake auto-generates one in .xmake/... for dotnet build/publish.
--- -- add_files("src/app.csproj")
---
--- -- add_values("csharp.", ) -- use one the properties below
--- -- add_values("csharp.properties", "AnotherProperty=Value") -- declare a custom property
---
--- -- mapped csharp properties (lua key => generated xml):
--- -- https://learn.microsoft.com/en-us/nuget/reference/msbuild-targets#pack-target
--- -- csharp.sdk => (project attribute) (defaults to "Microsoft.NET.Sdk")
--- -- csharp.target_frameworks => (list, ';' separated)
--- -- csharp.target_framework => (defaults to latest installed dotnet sdk major, e.g. net8.0)
--- -- csharp.implicit_usings =>
--- -- csharp.nullable =>
--- -- csharp.lang_version =>
--- -- csharp.enable_default_compile_items =>
--- -- csharp.enable_default_embedded_resource_items =>
--- -- csharp.enable_default_none_items =>
--- -- csharp.root_namespace =>
--- -- default/derived: set_basename(...) =>
--- -- csharp.generate_assembly_info =>
--- -- csharp.deterministic =>
--- -- default/derived: set_optimize(...) =>
--- -- default/derived: get_plat()/get_arch() =>
--- -- csharp.prefer_32bit =>
--- -- csharp.allow_unsafe_blocks =>
--- -- csharp.check_for_overflow_underflow =>
--- -- default/derived: set_warnings(...) =>
--- -- csharp.analysis_level =>
--- -- csharp.enable_net_analyzers =>
--- -- csharp.enforce_code_style_in_build =>
--- -- default/derived: set_warnings("error") => true
--- -- csharp.warnings_as_errors => (list, ';' separated)
--- -- csharp.warnings_not_as_errors => (list, ';' separated)
--- -- default/derived: add_defines(...) =>
--- -- csharp.error_log =>
--- -- default/derived: set_symbols(...) => /
--- -- csharp.generate_documentation_file =>
--- -- csharp.documentation_file =>
--- -- csharp.runtime_identifier =>
--- -- csharp.runtime_identifiers => (list, ';' separated)
--- -- csharp.self_contained =>
--- -- csharp.use_app_host =>
--- -- csharp.roll_forward =>
--- -- csharp.publish_single_file =>
--- -- csharp.publish_trimmed =>
--- -- csharp.trim_mode =>
--- -- csharp.publish_ready_to_run =>
--- -- csharp.invariant_globalization =>
--- -- csharp.include_native_libraries_for_self_extract =>
--- -- csharp.enable_compression_in_single_file =>
--- -- csharp.publish_aot =>
--- -- csharp.strip_symbols =>
--- -- csharp.enable_trim_analyzer =>
--- -- csharp.json_serializer_is_reflection_enabled_by_default =>
--- -- csharp.satellite_resource_languages => (list, ';' separated)
--- -- csharp.version =>
--- -- csharp.assembly_version =>
--- -- csharp.file_version =>
--- -- csharp.informational_version =>
--- -- csharp.package_id =>
--- -- csharp.authors =>
--- -- csharp.company =>
--- -- csharp.product =>
--- -- csharp.description =>
--- -- csharp.copyright =>
--- -- csharp.repository_url =>
--- -- csharp.repository_type =>
--- -- csharp.package_license_expression =>
--- -- csharp.package_project_url =>
--- -- csharp.neutral_language =>
--- -- csharp.enable_preview_features =>
--- -- csharp.generate_runtime_configuration_files =>
--- -- csharp.copy_local_lock_file_assemblies =>
--- -- csharp.append_target_framework_to_output_path => (default: false)
--- -- csharp.append_runtime_identifier_to_output_path => (default: false)
--- -- csharp.produce_reference_assembly =>
--- -- csharp.disable_implicit_framework_references =>
--- -- csharp.generate_target_framework_attribute =>
--- @endcode
---
+rule("csharp.build")
+ set_sourcekinds("cs")
+ on_load(function (target)
+ import("modules.csharp_common", {rootdir = os.scriptdir(), alias = "csharp_common"})
+
+ -- set target extension and prefix
+ if target:is_shared() then
+ if not target:get("extension") then
+ target:set("extension", ".dll")
+ end
+ if target:get("prefixname") == nil then
+ target:set("prefixname", "")
+ end
+ end
+
+ -- find or generate .csproj file
+ local csprojfile = csharp_common.find_or_generate_csproj(target, {skip_deps = true})
+ if csprojfile then
+ target:data_set("csharp.csproj", csprojfile)
+ if not target:get("filename") and not target:get("basename") then
+ target:set("basename", path.basename(csprojfile))
+ end
+ end
+ end)
+ on_build("build.target")
+
rule("csharp")
- set_extensions(".cs", ".csproj")
- set_sourcekinds("cs", "csproj", {objectfiles = false})
- on_load("load")
- on_buildcmd("buildcmd")
- on_clean("clean")
- on_install("install")
- on_installcmd("installcmd")
+ add_deps("csharp.build")
+ add_deps("utils.inherit.links")
--
cgit v1.3.1
From a838614942e840868655e96a865c4f854f6cedc2 Mon Sep 17 00:00:00 2001
From: ruki
Date: Sat, 14 Mar 2026 23:26:32 +0800
Subject: add missing files
---
xmake/rules/csharp/build/target.lua | 85 +++++++++++++++++++++++++++++++++++++
1 file changed, 85 insertions(+)
create mode 100644 xmake/rules/csharp/build/target.lua
diff --git a/xmake/rules/csharp/build/target.lua b/xmake/rules/csharp/build/target.lua
new file mode 100644
index 000000000..d97757dee
--- /dev/null
+++ b/xmake/rules/csharp/build/target.lua
@@ -0,0 +1,85 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author ruki
+-- @file target.lua
+--
+
+-- imports
+import("core.base.option")
+import("core.tool.compiler")
+import("core.project.depend")
+import("utils.progress")
+
+-- build the source files
+function build_sourcefiles(target, sourcebatch, opt)
+
+ -- get the target file
+ local targetfile = target:targetfile()
+
+ -- get source files and kind
+ local sourcefiles = sourcebatch.sourcefiles
+ local sourcekind = sourcebatch.sourcekind
+
+ -- get depend file
+ local dependfile = target:dependfile(targetfile)
+
+ -- load compiler
+ local compinst = compiler.load(sourcekind, {target = target})
+
+ -- get compile flags
+ local compflags = compinst:compflags({target = target})
+
+ -- load dependent info
+ local dependinfo = option.get("rebuild") and {} or (depend.load(dependfile) or {})
+
+ -- need build this target?
+ local depvalues = {compinst:program(), compflags}
+ if not depend.is_changed(dependinfo, {lastmtime = os.mtime(targetfile), values = depvalues}) then
+ return
+ end
+
+ -- trace progress info
+ progress.show(opt.progress, "${color.build.target}linking.$(mode) %s", path.filename(targetfile))
+
+ -- trace verbose info
+ vprint(compinst:buildcmd(sourcefiles, targetfile, {target = target, compflags = compflags}))
+
+ -- flush io buffer to update progress info
+ io.flush()
+
+ -- build it
+ dependinfo.files = {}
+ assert(compinst:build(sourcefiles, targetfile, {target = target, dependinfo = dependinfo, compflags = compflags}))
+
+ -- update files and values to the dependent file
+ dependinfo.values = depvalues
+ table.join2(dependinfo.files, sourcefiles)
+ depend.save(dependinfo, dependfile)
+end
+
+-- build target
+function main(target, opt)
+
+ -- @note only support one source kind!
+ local sourcebatches = target:sourcebatches()
+ if sourcebatches then
+ local sourcebatch = sourcebatches["csharp.build"]
+ if sourcebatch then
+ build_sourcefiles(target, sourcebatch, opt)
+ end
+ end
+end
--
cgit v1.3.1
From 20277e59408e4d93f4bb6f4ac518ed746f4eefba Mon Sep 17 00:00:00 2001
From: ruki
Date: Sat, 14 Mar 2026 23:30:52 +0800
Subject: add dotnet to windows platform
---
xmake/platforms/windows/xmake.lua | 2 +-
xmake/toolchains/dotnet/xmake.lua | 1 -
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/xmake/platforms/windows/xmake.lua b/xmake/platforms/windows/xmake.lua
index 4d8335171..4bb307c7e 100644
--- a/xmake/platforms/windows/xmake.lua
+++ b/xmake/platforms/windows/xmake.lua
@@ -29,7 +29,7 @@ platform("windows")
set_formats("binary", "$(name).exe")
set_formats("symbol", "$(name).pdb")
- set_toolchains("msvc", "clang", "yasm", "nasm", "cuda", "rust", "swift", "go", "gfortran", "zig", "fpc", "nim")
+ set_toolchains("msvc", "clang", "yasm", "nasm", "cuda", "rust", "swift", "go", "gfortran", "zig", "fpc", "nim", "dotnet")
set_menu {
config =
diff --git a/xmake/toolchains/dotnet/xmake.lua b/xmake/toolchains/dotnet/xmake.lua
index 6a02b0a0a..64fad0ec0 100644
--- a/xmake/toolchains/dotnet/xmake.lua
+++ b/xmake/toolchains/dotnet/xmake.lua
@@ -19,7 +19,6 @@
--
toolchain("dotnet")
- set_kind("standalone")
set_homepage("https://dotnet.microsoft.com/")
set_description(".NET SDK Toolchain")
--
cgit v1.3.1
From 8228de2b4957e8da78a158b4d80c1eb8c2a100b6 Mon Sep 17 00:00:00 2001
From: ruki
Date: Sat, 14 Mar 2026 23:36:19 +0800
Subject: fix flags
---
xmake/languages/csharp/xmake.lua | 8 ++------
xmake/modules/core/tools/dotnet.lua | 1 -
xmake/rules/csharp/build/target.lua | 8 +++++++-
3 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/xmake/languages/csharp/xmake.lua b/xmake/languages/csharp/xmake.lua
index c4bc5aa30..e47e65585 100644
--- a/xmake/languages/csharp/xmake.lua
+++ b/xmake/languages/csharp/xmake.lua
@@ -36,9 +36,7 @@ language("csharp")
, "target.optimize:check"
}
, binary = {
- "target.strip"
- , "target.symbols"
- , "config.linkdirs"
+ "config.linkdirs"
, "target.linkdirs"
, "config.links"
, "target.links"
@@ -46,9 +44,7 @@ language("csharp")
, "target.syslinks"
}
, shared = {
- "target.strip"
- , "target.symbols"
- , "config.linkdirs"
+ "config.linkdirs"
, "target.linkdirs"
, "config.links"
, "target.links"
diff --git a/xmake/modules/core/tools/dotnet.lua b/xmake/modules/core/tools/dotnet.lua
index 2647be1be..867112dc7 100644
--- a/xmake/modules/core/tools/dotnet.lua
+++ b/xmake/modules/core/tools/dotnet.lua
@@ -50,7 +50,6 @@ end
function nf_symbol(self, level)
local maps = {
debug = {"-p:DebugType=full", "-p:DebugSymbols=true"}
- , hidden = {"-p:DebugType=pdbonly", "-p:DebugSymbols=true"}
}
return maps[level]
end
diff --git a/xmake/rules/csharp/build/target.lua b/xmake/rules/csharp/build/target.lua
index d97757dee..60b26602f 100644
--- a/xmake/rules/csharp/build/target.lua
+++ b/xmake/rules/csharp/build/target.lua
@@ -31,9 +31,15 @@ function build_sourcefiles(target, sourcebatch, opt)
local targetfile = target:targetfile()
-- get source files and kind
- local sourcefiles = sourcebatch.sourcefiles
+ local sourcefiles = table.copy(sourcebatch.sourcefiles)
local sourcekind = sourcebatch.sourcekind
+ -- prepend .csproj file so dotnet tool can find it
+ local csprojfile = target:data("csharp.csproj")
+ if csprojfile then
+ table.insert(sourcefiles, 1, csprojfile)
+ end
+
-- get depend file
local dependfile = target:dependfile(targetfile)
--
cgit v1.3.1
From 1e792c4af7c0328b6d693c34b788ad0df09cbf66 Mon Sep 17 00:00:00 2001
From: ruki
Date: Sat, 14 Mar 2026 23:39:12 +0800
Subject: add .csproj support
---
xmake/languages/csharp/xmake.lua | 2 +-
xmake/rules/csharp/xmake.lua | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/xmake/languages/csharp/xmake.lua b/xmake/languages/csharp/xmake.lua
index e47e65585..9c2c0ce22 100644
--- a/xmake/languages/csharp/xmake.lua
+++ b/xmake/languages/csharp/xmake.lua
@@ -20,7 +20,7 @@
language("csharp")
add_rules("csharp")
- set_sourcekinds {cs = ".cs", csproj = ".csproj"}
+ set_sourcekinds {cs = ".cs"}
set_sourceflags {cs = "csflags"}
set_targetkinds {binary = "cs", shared = "cs"}
set_targetflags {binary = "ldflags", shared = "shflags"}
diff --git a/xmake/rules/csharp/xmake.lua b/xmake/rules/csharp/xmake.lua
index 9ad5f7974..46f46bb75 100644
--- a/xmake/rules/csharp/xmake.lua
+++ b/xmake/rules/csharp/xmake.lua
@@ -20,6 +20,7 @@
rule("csharp.build")
set_sourcekinds("cs")
+ set_extensions(".csproj")
on_load(function (target)
import("modules.csharp_common", {rootdir = os.scriptdir(), alias = "csharp_common"})
--
cgit v1.3.1
From 894833ab4ed9cc930c60ab7b9dec889ab1a67277 Mon Sep 17 00:00:00 2001
From: ruki
Date: Sat, 14 Mar 2026 23:50:35 +0800
Subject: improve csproj rules
---
xmake/rules/csharp/xmake.lua | 29 +++++++++++++++++------------
1 file changed, 17 insertions(+), 12 deletions(-)
diff --git a/xmake/rules/csharp/xmake.lua b/xmake/rules/csharp/xmake.lua
index 46f46bb75..56772a0e8 100644
--- a/xmake/rules/csharp/xmake.lua
+++ b/xmake/rules/csharp/xmake.lua
@@ -18,12 +18,26 @@
-- @file xmake.lua
--
-rule("csharp.build")
- set_sourcekinds("cs")
+-- handle .csproj project file
+rule("csharp.csproj")
set_extensions(".csproj")
- on_load(function (target)
+ on_config(function (target)
import("modules.csharp_common", {rootdir = os.scriptdir(), alias = "csharp_common"})
+ -- find .csproj from source files or generate one
+ local csprojfile = csharp_common.find_or_generate_csproj(target, {skip_deps = true})
+ if csprojfile then
+ target:data_set("csharp.csproj", csprojfile)
+ if not target:get("filename") and not target:get("basename") then
+ target:set("basename", path.basename(csprojfile))
+ end
+ end
+ end)
+
+rule("csharp.build")
+ set_sourcekinds("cs")
+ add_deps("csharp.csproj")
+ on_load(function (target)
-- set target extension and prefix
if target:is_shared() then
if not target:get("extension") then
@@ -33,15 +47,6 @@ rule("csharp.build")
target:set("prefixname", "")
end
end
-
- -- find or generate .csproj file
- local csprojfile = csharp_common.find_or_generate_csproj(target, {skip_deps = true})
- if csprojfile then
- target:data_set("csharp.csproj", csprojfile)
- if not target:get("filename") and not target:get("basename") then
- target:set("basename", path.basename(csprojfile))
- end
- end
end)
on_build("build.target")
--
cgit v1.3.1
From ca72dbcfd8c45f194b9770f63c91ebd00412fa39 Mon Sep 17 00:00:00 2001
From: ruki
Date: Sat, 14 Mar 2026 23:59:57 +0800
Subject: improve csproj rules
---
tests/projects/csharp/existing_csproj/.gitignore | 6 ------
tests/projects/csharp/existing_csproj/Program.cs | 2 --
.../csharp/existing_csproj/existing_csproj.csproj | 10 ----------
tests/projects/csharp/existing_csproj/test.lua | 10 ----------
tests/projects/csharp/existing_csproj/xmake.lua | 6 ------
xmake/rules/csharp/build/target.lua | 16 +++++++++++++++-
xmake/rules/csharp/xmake.lua | 17 -----------------
7 files changed, 15 insertions(+), 52 deletions(-)
delete mode 100644 tests/projects/csharp/existing_csproj/.gitignore
delete mode 100644 tests/projects/csharp/existing_csproj/Program.cs
delete mode 100644 tests/projects/csharp/existing_csproj/existing_csproj.csproj
delete mode 100644 tests/projects/csharp/existing_csproj/test.lua
delete mode 100644 tests/projects/csharp/existing_csproj/xmake.lua
diff --git a/tests/projects/csharp/existing_csproj/.gitignore b/tests/projects/csharp/existing_csproj/.gitignore
deleted file mode 100644
index 6b8d3af38..000000000
--- a/tests/projects/csharp/existing_csproj/.gitignore
+++ /dev/null
@@ -1,6 +0,0 @@
-.xmake/
-build/
-bin/
-obj/
-vsxmake*/
-.vs/
diff --git a/tests/projects/csharp/existing_csproj/Program.cs b/tests/projects/csharp/existing_csproj/Program.cs
deleted file mode 100644
index 3751555cb..000000000
--- a/tests/projects/csharp/existing_csproj/Program.cs
+++ /dev/null
@@ -1,2 +0,0 @@
-// See https://aka.ms/new-console-template for more information
-Console.WriteLine("Hello, World!");
diff --git a/tests/projects/csharp/existing_csproj/existing_csproj.csproj b/tests/projects/csharp/existing_csproj/existing_csproj.csproj
deleted file mode 100644
index ed9781c22..000000000
--- a/tests/projects/csharp/existing_csproj/existing_csproj.csproj
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
- Exe
- net10.0
- enable
- enable
-
-
-
diff --git a/tests/projects/csharp/existing_csproj/test.lua b/tests/projects/csharp/existing_csproj/test.lua
deleted file mode 100644
index 5ab8adc69..000000000
--- a/tests/projects/csharp/existing_csproj/test.lua
+++ /dev/null
@@ -1,10 +0,0 @@
-import("lib.detect.find_tool")
-
-function test_build(t)
- local dotnet = find_tool("dotnet")
- if dotnet then
- t:build()
- else
- return t:skip("dotnet not found")
- end
-end
diff --git a/tests/projects/csharp/existing_csproj/xmake.lua b/tests/projects/csharp/existing_csproj/xmake.lua
deleted file mode 100644
index 95e5e45d9..000000000
--- a/tests/projects/csharp/existing_csproj/xmake.lua
+++ /dev/null
@@ -1,6 +0,0 @@
-add_rules("mode.debug", "mode.release")
-
-target("test")
- set_kind("binary")
- add_rules("csharp")
- add_files("existing_csproj.csproj")
diff --git a/xmake/rules/csharp/build/target.lua b/xmake/rules/csharp/build/target.lua
index 60b26602f..a193b2f8b 100644
--- a/xmake/rules/csharp/build/target.lua
+++ b/xmake/rules/csharp/build/target.lua
@@ -23,6 +23,20 @@ import("core.base.option")
import("core.tool.compiler")
import("core.project.depend")
import("utils.progress")
+import("modules.csharp_common", {rootdir = path.directory(os.scriptdir()), alias = "csharp_common"})
+
+-- find or generate .csproj file
+function _get_csprojfile(target)
+ local csprojfile = target:data("csharp.csproj")
+ if csprojfile then
+ return csprojfile
+ end
+ csprojfile = csharp_common.find_or_generate_csproj(target)
+ if csprojfile then
+ target:data_set("csharp.csproj", csprojfile)
+ end
+ return csprojfile
+end
-- build the source files
function build_sourcefiles(target, sourcebatch, opt)
@@ -35,7 +49,7 @@ function build_sourcefiles(target, sourcebatch, opt)
local sourcekind = sourcebatch.sourcekind
-- prepend .csproj file so dotnet tool can find it
- local csprojfile = target:data("csharp.csproj")
+ local csprojfile = _get_csprojfile(target)
if csprojfile then
table.insert(sourcefiles, 1, csprojfile)
end
diff --git a/xmake/rules/csharp/xmake.lua b/xmake/rules/csharp/xmake.lua
index 56772a0e8..3b6e3bf94 100644
--- a/xmake/rules/csharp/xmake.lua
+++ b/xmake/rules/csharp/xmake.lua
@@ -18,25 +18,8 @@
-- @file xmake.lua
--
--- handle .csproj project file
-rule("csharp.csproj")
- set_extensions(".csproj")
- on_config(function (target)
- import("modules.csharp_common", {rootdir = os.scriptdir(), alias = "csharp_common"})
-
- -- find .csproj from source files or generate one
- local csprojfile = csharp_common.find_or_generate_csproj(target, {skip_deps = true})
- if csprojfile then
- target:data_set("csharp.csproj", csprojfile)
- if not target:get("filename") and not target:get("basename") then
- target:set("basename", path.basename(csprojfile))
- end
- end
- end)
-
rule("csharp.build")
set_sourcekinds("cs")
- add_deps("csharp.csproj")
on_load(function (target)
-- set target extension and prefix
if target:is_shared() then
--
cgit v1.3.1
From 443918d7e489ce7023e95184cc1e7ef537fa08f6 Mon Sep 17 00:00:00 2001
From: ruki
Date: Mon, 16 Mar 2026 23:41:13 +0800
Subject: remove unused codes
---
xmake/rules/csharp/modules/properties.lua | 117 ------------------------------
1 file changed, 117 deletions(-)
diff --git a/xmake/rules/csharp/modules/properties.lua b/xmake/rules/csharp/modules/properties.lua
index b8d30f1e9..10606b357 100644
--- a/xmake/rules/csharp/modules/properties.lua
+++ b/xmake/rules/csharp/modules/properties.lua
@@ -35,60 +35,6 @@ function _get_target_value(target, name)
return _first(target:values(name))
end
-function _collect_strings(result, value)
- if value == nil then
- return
- end
- if type(value) == "table" then
- for _, item in ipairs(value) do
- _collect_strings(result, item)
- end
- else
- local sval = tostring(value)
- if #sval > 0 then
- table.insert(result, sval)
- end
- end
-end
-
-function _extract_define_symbol(define)
- define = tostring(define):trim()
- if #define == 0 then
- return nil
- end
- local symbol = define:split("=", {plain = true})[1]
- if not symbol then
- return nil
- end
- symbol = symbol:trim()
- if symbol:match("^[A-Za-z_][A-Za-z0-9_]*$") then
- return symbol
- end
- return nil
-end
-
-function _resolve_define_constants(context)
- local target = context.target
- local constants = {}
-
- _collect_strings(constants, target:get("defines"))
- local opt_defines = target:get_from("defines", "option::*")
- if opt_defines then
- _collect_strings(constants, opt_defines)
- end
-
- local symbols = {}
- for _, define in ipairs(constants) do
- local symbol = _extract_define_symbol(define)
- if symbol then
- table.insert(symbols, symbol)
- end
- end
- symbols = table.unique(symbols)
- if #symbols > 0 then
- return table.concat(symbols, ";")
- end
-end
function _get_default_target_framework(context)
local dotnet = _first(context.target:get("toolset.cs")) or "dotnet"
@@ -139,62 +85,6 @@ function _resolve_assembly_name(context)
end
end
-function _resolve_optimize(context)
- local optimize = _first(context.target:get("optimize"))
- if optimize ~= nil then
- return optimize == "none" and "false" or "true"
- end
-end
-
-function _resolve_debug_symbols(context)
- local symbols = _first(context.target:get("symbols"))
- if symbols ~= nil then
- return symbols == "none" and "false" or "true"
- end
-end
-
-function _resolve_debug_type(context)
- local symbols = _first(context.target:get("symbols"))
- if symbols and symbols ~= "none" then
- return "portable"
- end
-end
-
-function _resolve_platform_target(context)
- local arch = (context.target:arch() or ""):lower()
- local mapping = {
- x86_64 = "x64",
- amd64 = "x64",
- x64 = "x64",
- i386 = "x86",
- x86 = "x86",
- arm64 = "arm64",
- arm = "arm",
- armv7 = "arm"
- }
- return mapping[arch]
-end
-
-function _resolve_warning_level(context)
- local warnings = _first(context.target:get("warnings"))
- local mapping = {
- none = "0",
- less = "2",
- more = "3",
- all = "4",
- allextra = "4",
- everything = "4",
- error = "4"
- }
- return warnings and mapping[warnings]
-end
-
-function _resolve_treat_warnings_as_errors(context)
- local warnings = _first(context.target:get("warnings"))
- if warnings == "error" then
- return "true"
- end
-end
function _register_property(register, suffix, xml, default, extra)
local entry = table.join({
@@ -242,22 +132,15 @@ function main()
register({kind = "property", xml = "AssemblyName", resolve = _resolve_assembly_name})
_register_property(register, "generate_assembly_info", "GenerateAssemblyInfo")
_register_property(register, "deterministic", "Deterministic")
- register({kind = "property", xml = "Optimize", resolve = _resolve_optimize})
- register({kind = "property", xml = "PlatformTarget", resolve = _resolve_platform_target})
_register_property(register, "prefer_32bit", "Prefer32Bit")
_register_property(register, "allow_unsafe_blocks", "AllowUnsafeBlocks")
_register_property(register, "check_for_overflow_underflow", "CheckForOverflowUnderflow")
- register({kind = "property", xml = "WarningLevel", resolve = _resolve_warning_level})
_register_property(register, "analysis_level", "AnalysisLevel")
_register_property(register, "enable_net_analyzers", "EnableNETAnalyzers")
_register_property(register, "enforce_code_style_in_build", "EnforceCodeStyleInBuild")
- register({kind = "property", xml = "TreatWarningsAsErrors", resolve = _resolve_treat_warnings_as_errors})
_register_list_property(register, "warnings_as_errors", "WarningsAsErrors")
_register_list_property(register, "warnings_not_as_errors", "WarningsNotAsErrors")
- register({kind = "property", xml = "DefineConstants", resolve = _resolve_define_constants})
_register_property(register, "error_log", "ErrorLog")
- register({kind = "property", xml = "DebugType", resolve = _resolve_debug_type})
- register({kind = "property", xml = "DebugSymbols", resolve = _resolve_debug_symbols})
_register_property(register, "generate_documentation_file", "GenerateDocumentationFile")
_register_property(register, "documentation_file", "DocumentationFile")
--
cgit v1.3.1
From 738d63ec3a02a86e93011f2b3b41267be30e7484 Mon Sep 17 00:00:00 2001
From: ruki
Date: Mon, 16 Mar 2026 23:42:00 +0800
Subject: format codes
---
xmake/rules/csharp/build/target.lua | 2 +-
xmake/rules/csharp/clean.lua | 3 ++-
xmake/rules/csharp/install.lua | 4 ++--
xmake/rules/csharp/modules/csharp_common.lua | 3 ++-
xmake/rules/csharp/modules/csproj_generator.lua | 5 +++--
5 files changed, 10 insertions(+), 7 deletions(-)
diff --git a/xmake/rules/csharp/build/target.lua b/xmake/rules/csharp/build/target.lua
index a193b2f8b..259ccf5d4 100644
--- a/xmake/rules/csharp/build/target.lua
+++ b/xmake/rules/csharp/build/target.lua
@@ -23,7 +23,7 @@ import("core.base.option")
import("core.tool.compiler")
import("core.project.depend")
import("utils.progress")
-import("modules.csharp_common", {rootdir = path.directory(os.scriptdir()), alias = "csharp_common"})
+import("rules.modules.csharp_common", {rootdir = os.scriptdir(), alias = "csharp_common"})
-- find or generate .csproj file
function _get_csprojfile(target)
diff --git a/xmake/rules/csharp/clean.lua b/xmake/rules/csharp/clean.lua
index ce683268c..78631738a 100644
--- a/xmake/rules/csharp/clean.lua
+++ b/xmake/rules/csharp/clean.lua
@@ -18,8 +18,9 @@
-- @file clean.lua
--
+-- imports
import("target.action.clean", {alias = "_do_clean_target"})
-import("modules.csharp_common", {rootdir = os.scriptdir(), alias = "csharp_common"})
+import("rules.modules.csharp_common", {rootdir = os.scriptdir(), alias = "csharp_common"})
function main(target, opt)
_do_clean_target(target)
diff --git a/xmake/rules/csharp/install.lua b/xmake/rules/csharp/install.lua
index 4d871e291..a43006dd6 100644
--- a/xmake/rules/csharp/install.lua
+++ b/xmake/rules/csharp/install.lua
@@ -18,10 +18,11 @@
-- @file install.lua
--
+-- imports
import("core.base.option")
import("core.project.config")
import("core.tool.compiler")
-import("modules.csharp_common", {rootdir = os.scriptdir(), alias = "csharp_common"})
+import("rules.modules.csharp_common", {rootdir = os.scriptdir(), alias = "csharp_common"})
function main(target, opt)
local csprojfile = assert(csharp_common.find_or_generate_csproj(target), "target(%s): missing csharp .csproj file!", target:name())
@@ -62,5 +63,4 @@ function main(target, opt)
os.mkdir(install_abs)
os.cp(path.join(tmpdir, "**"), install_abs, {rootdir = tmpdir})
end
-
end
diff --git a/xmake/rules/csharp/modules/csharp_common.lua b/xmake/rules/csharp/modules/csharp_common.lua
index 516297a45..c223bdce8 100644
--- a/xmake/rules/csharp/modules/csharp_common.lua
+++ b/xmake/rules/csharp/modules/csharp_common.lua
@@ -18,9 +18,10 @@
-- @file csharp_common.lua
--
+-- imports
import("core.base.option")
import("core.project.config")
-import("csproj_generator", {rootdir = os.scriptdir(), alias = "generate_csproj"})
+import("csproj_generator")
function _is_csharp_target(target)
if target:rule("csharp") then
diff --git a/xmake/rules/csharp/modules/csproj_generator.lua b/xmake/rules/csharp/modules/csproj_generator.lua
index 0f94ed806..8972b5550 100644
--- a/xmake/rules/csharp/modules/csproj_generator.lua
+++ b/xmake/rules/csharp/modules/csproj_generator.lua
@@ -18,8 +18,9 @@
-- @file csproj_generator.lua
--
-import("properties", {rootdir = os.scriptdir(), alias = "csharp_properties"})
-import("itemgroups", {rootdir = os.scriptdir(), alias = "csharp_itemgroups"})
+-- imports
+import("properties", {alias = "csharp_properties"})
+import("itemgroups", {alias = "csharp_itemgroups"})
function _xml_escape(value)
value = tostring(value or "")
--
cgit v1.3.1
From 9bb7b558f2e1c4cb0408f09e85a52c9a1e0db360 Mon Sep 17 00:00:00 2001
From: ruki
Date: Mon, 16 Mar 2026 23:42:27 +0800
Subject: remove some codes
---
xmake/rules/csharp/clean.lua | 41 --------------------------
xmake/rules/csharp/install.lua | 66 ------------------------------------------
2 files changed, 107 deletions(-)
delete mode 100644 xmake/rules/csharp/clean.lua
delete mode 100644 xmake/rules/csharp/install.lua
diff --git a/xmake/rules/csharp/clean.lua b/xmake/rules/csharp/clean.lua
deleted file mode 100644
index 78631738a..000000000
--- a/xmake/rules/csharp/clean.lua
+++ /dev/null
@@ -1,41 +0,0 @@
---!A cross-platform build utility based on Lua
---
--- Licensed under the Apache License, Version 2.0 (the "License");
--- you may not use this file except in compliance with the License.
--- You may obtain a copy of the License at
---
--- http://www.apache.org/licenses/LICENSE-2.0
---
--- Unless required by applicable law or agreed to in writing, software
--- distributed under the License is distributed on an "AS IS" BASIS,
--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--- See the License for the specific language governing permissions and
--- limitations under the License.
---
--- Copyright (C) 2015-present, Xmake Open Source Community.
---
--- @author JassJam
--- @file clean.lua
---
-
--- imports
-import("target.action.clean", {alias = "_do_clean_target"})
-import("rules.modules.csharp_common", {rootdir = os.scriptdir(), alias = "csharp_common"})
-
-function main(target, opt)
- _do_clean_target(target)
-
- local targetfile = target:targetfile()
- if targetfile then
- os.tryrm(target:dependfile(targetfile))
- end
- os.tryrm(target:targetdir())
-
- -- also remove the bin/obj folders next to the .csproj file
- local csprojfile = csharp_common.find_csproj(target)
- if csprojfile then
- local csprojdir = path.directory(csprojfile)
- os.tryrm(path.join(csprojdir, "bin"))
- os.tryrm(path.join(csprojdir, "obj"))
- end
-end
diff --git a/xmake/rules/csharp/install.lua b/xmake/rules/csharp/install.lua
deleted file mode 100644
index a43006dd6..000000000
--- a/xmake/rules/csharp/install.lua
+++ /dev/null
@@ -1,66 +0,0 @@
---!A cross-platform build utility based on Lua
---
--- Licensed under the Apache License, Version 2.0 (the "License");
--- you may not use this file except in compliance with the License.
--- You may obtain a copy of the License at
---
--- http://www.apache.org/licenses/LICENSE-2.0
---
--- Unless required by applicable law or agreed to in writing, software
--- distributed under the License is distributed on an "AS IS" BASIS,
--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--- See the License for the specific language governing permissions and
--- limitations under the License.
---
--- Copyright (C) 2015-present, Xmake Open Source Community.
---
--- @author ruki
--- @file install.lua
---
-
--- imports
-import("core.base.option")
-import("core.project.config")
-import("core.tool.compiler")
-import("rules.modules.csharp_common", {rootdir = os.scriptdir(), alias = "csharp_common"})
-
-function main(target, opt)
- local csprojfile = assert(csharp_common.find_or_generate_csproj(target), "target(%s): missing csharp .csproj file!", target:name())
-
- -- get dotnet program from compiler toolchain
- local compinst = compiler.load("cs", {target = target})
- local dotnet = compinst:program()
-
- local configuration = csharp_common.build_mode_to_configuration()
- local verbosity = option.get("diagnosis") and "diagnostic" or "quiet"
-
- -- publish to a directory under target autogendir
- local tmpdir = path.join(target:autogendir(), "publish")
- os.mkdir(tmpdir)
-
- local rid = csharp_common.get_runtime_identifier(target)
- local argv = {
- "publish", csprojfile,
- "--nologo",
- "--configuration", configuration,
- "--verbosity", verbosity,
- "--output", tmpdir
- }
- if rid and target:is_binary() then
- table.join2(argv, {"--runtime", rid})
- end
- os.vrunv(dotnet, argv, {curdir = path.directory(csprojfile), envs = compinst:runenvs()})
-
- -- copy published files to installdir
- local installdir = target:installdir()
- if target:is_binary() then
- installdir = target:installdir("bin")
- elseif target:is_static() or target:is_shared() then
- installdir = target:installdir("lib")
- end
- if installdir and #installdir > 0 then
- local install_abs = path.is_absolute(installdir) and installdir or path.absolute(installdir, os.projectdir())
- os.mkdir(install_abs)
- os.cp(path.join(tmpdir, "**"), install_abs, {rootdir = tmpdir})
- end
-end
--
cgit v1.3.1
From 2504b748c0c716c7ab0db044cf7867b4fd6a62d3 Mon Sep 17 00:00:00 2001
From: ruki
Date: Mon, 16 Mar 2026 23:43:14 +0800
Subject: rename csharp common to utils
---
xmake/rules/csharp/build/target.lua | 4 +-
xmake/rules/csharp/modules/csharp_common.lua | 172 ---------------------------
xmake/rules/csharp/modules/csharp_utils.lua | 172 +++++++++++++++++++++++++++
3 files changed, 174 insertions(+), 174 deletions(-)
delete mode 100644 xmake/rules/csharp/modules/csharp_common.lua
create mode 100644 xmake/rules/csharp/modules/csharp_utils.lua
diff --git a/xmake/rules/csharp/build/target.lua b/xmake/rules/csharp/build/target.lua
index 259ccf5d4..43ee036b1 100644
--- a/xmake/rules/csharp/build/target.lua
+++ b/xmake/rules/csharp/build/target.lua
@@ -23,7 +23,7 @@ import("core.base.option")
import("core.tool.compiler")
import("core.project.depend")
import("utils.progress")
-import("rules.modules.csharp_common", {rootdir = os.scriptdir(), alias = "csharp_common"})
+import("rules.csharp.modules.csharp_utils", {rootdir = os.programdir(), alias = "csharp_utils"})
-- find or generate .csproj file
function _get_csprojfile(target)
@@ -31,7 +31,7 @@ function _get_csprojfile(target)
if csprojfile then
return csprojfile
end
- csprojfile = csharp_common.find_or_generate_csproj(target)
+ csprojfile = csharp_utils.find_or_generate_csproj(target)
if csprojfile then
target:data_set("csharp.csproj", csprojfile)
end
diff --git a/xmake/rules/csharp/modules/csharp_common.lua b/xmake/rules/csharp/modules/csharp_common.lua
deleted file mode 100644
index c223bdce8..000000000
--- a/xmake/rules/csharp/modules/csharp_common.lua
+++ /dev/null
@@ -1,172 +0,0 @@
---!A cross-platform build utility based on Lua
---
--- Licensed under the Apache License, Version 2.0 (the "License");
--- you may not use this file except in compliance with the License.
--- You may obtain a copy of the License at
---
--- http://www.apache.org/licenses/LICENSE-2.0
---
--- Unless required by applicable law or agreed to in writing, software
--- distributed under the License is distributed on an "AS IS" BASIS,
--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--- See the License for the specific language governing permissions and
--- limitations under the License.
---
--- Copyright (C) 2015-present, Xmake Open Source Community.
---
--- @author JassJam
--- @file csharp_common.lua
---
-
--- imports
-import("core.base.option")
-import("core.project.config")
-import("csproj_generator")
-
-function _is_csharp_target(target)
- if target:rule("csharp") then
- return true
- end
- for _, sourcefile in ipairs(target:sourcefiles()) do
- local ext = path.extension(sourcefile):lower()
- if ext == ".cs" or ext == ".csproj" then
- return true
- end
- end
- return false
-end
-
-function _generated_csproj_path(target)
- local targetkey = target:fullname():replace("::", path.sep())
- local csprojdir = path.join(config.directory(), "rules", "csharp", targetkey, target:plat(), target:arch())
- local csprojname = target:name() .. ".csproj"
- return path.join(csprojdir, csprojname)
-end
-
-function _map_rid_arch(arch)
- arch = (arch or ""):lower()
- if arch == "x64" or arch == "x86_64" or arch == "amd64" then
- return "x64"
- elseif arch == "x86" or arch == "i386" then
- return "x86"
- elseif arch == "arm64" then
- return "arm64"
- elseif arch == "arm" or arch == "armv7" then
- return "arm"
- elseif arch == "riscv64" then
- return "riscv64"
- end
- return nil
-end
-
-function find_csproj(target)
- local csproj = target:data("csharp.csproj")
- if csproj then
- return csproj
- end
- for _, sourcefile in ipairs(target:sourcefiles()) do
- if path.extension(sourcefile):lower() == ".csproj" then
- local csprojabs = path.is_absolute(sourcefile) and sourcefile or path.absolute(sourcefile, os.projectdir())
- if os.isfile(csprojabs) then
- return csprojabs
- end
- end
- end
- return nil
-end
-
-function find_or_generate_csproj(target, opt)
- opt = opt or {}
- local csproj = find_csproj(target)
- local generated = target:data("csharp.csproj.generated")
- local generated_with_deps = target:data("csharp.csproj.generated.with_deps")
-
- -- prefer existing source .csproj directly
- if csproj and not generated then
- return csproj
- end
-
- -- generated .csproj in memory cache
- if csproj and generated then
- if opt.skip_deps or generated_with_deps then
- return csproj
- end
- end
-
- if not _is_csharp_target(target) then
- return nil
- end
-
- local csprojfile = csproj or _generated_csproj_path(target)
- local generated_now = false
-
- -- in load phase (skip_deps), reuse existing generated file to avoid
- -- touching mtime and causing unnecessary rebuilds.
- if not (opt.skip_deps and os.isfile(csprojfile)) then
- generate_csproj(target, csprojfile, table.join(opt, {
- is_csharp_target = _is_csharp_target,
- find_or_generate_csproj = find_or_generate_csproj
- }))
- generated_now = true
- end
-
- target:data_set("csharp.csproj", csprojfile)
- target:data_set("csharp.csproj.generated", true)
- if opt.skip_deps then
- -- keep this conservative in load phase: build/install phase will
- -- generate a deps-enabled project if needed, and content checks avoid
- -- unnecessary rewrites.
- if generated_now or generated_with_deps == nil then
- target:data_set("csharp.csproj.generated.with_deps", false)
- end
- else
- target:data_set("csharp.csproj.generated.with_deps", true)
- end
- return csprojfile
-end
-
-function build_mode_to_configuration()
- local mode
- if type(get_config) == "function" then
- mode = get_config("mode")
- end
- if not mode and type(is_mode) == "function" then
- if is_mode("debug") then
- mode = "debug"
- elseif is_mode("release") then
- mode = "release"
- end
- end
- mode = mode or "release"
- local mode_lower = mode:lower()
- if mode_lower == "debug" then
- return "Debug"
- elseif mode_lower == "release" then
- return "Release"
- end
- return mode:sub(1, 1):upper() .. mode:sub(2)
-end
-
-function get_runtime_identifier(target)
- local rid = target:values("csharp.runtime_identifier")
- if type(rid) == "table" then
- rid = rid[1]
- end
- if rid and #rid > 0 then
- return rid
- end
- local arch = _map_rid_arch(target:arch())
- if not arch then
- return nil
- end
- local plat = target:plat()
- if plat == "windows" or plat == "mingw" or plat == "msys" or plat == "cygwin" then
- return "win-" .. arch
- elseif plat == "linux" then
- return "linux-" .. arch
- elseif plat == "macosx" then
- return "osx-" .. arch
- end
- return nil
-end
-
diff --git a/xmake/rules/csharp/modules/csharp_utils.lua b/xmake/rules/csharp/modules/csharp_utils.lua
new file mode 100644
index 000000000..58895ff36
--- /dev/null
+++ b/xmake/rules/csharp/modules/csharp_utils.lua
@@ -0,0 +1,172 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author JassJam
+-- @file csharp_utils.lua
+--
+
+-- imports
+import("core.base.option")
+import("core.project.config")
+import("csproj_generator")
+
+function _is_csharp_target(target)
+ if target:rule("csharp") then
+ return true
+ end
+ for _, sourcefile in ipairs(target:sourcefiles()) do
+ local ext = path.extension(sourcefile):lower()
+ if ext == ".cs" or ext == ".csproj" then
+ return true
+ end
+ end
+ return false
+end
+
+function _generated_csproj_path(target)
+ local targetkey = target:fullname():replace("::", path.sep())
+ local csprojdir = path.join(config.directory(), "rules", "csharp", targetkey, target:plat(), target:arch())
+ local csprojname = target:name() .. ".csproj"
+ return path.join(csprojdir, csprojname)
+end
+
+function _map_rid_arch(arch)
+ arch = (arch or ""):lower()
+ if arch == "x64" or arch == "x86_64" or arch == "amd64" then
+ return "x64"
+ elseif arch == "x86" or arch == "i386" then
+ return "x86"
+ elseif arch == "arm64" then
+ return "arm64"
+ elseif arch == "arm" or arch == "armv7" then
+ return "arm"
+ elseif arch == "riscv64" then
+ return "riscv64"
+ end
+ return nil
+end
+
+function find_csproj(target)
+ local csproj = target:data("csharp.csproj")
+ if csproj then
+ return csproj
+ end
+ for _, sourcefile in ipairs(target:sourcefiles()) do
+ if path.extension(sourcefile):lower() == ".csproj" then
+ local csprojabs = path.is_absolute(sourcefile) and sourcefile or path.absolute(sourcefile, os.projectdir())
+ if os.isfile(csprojabs) then
+ return csprojabs
+ end
+ end
+ end
+ return nil
+end
+
+function find_or_generate_csproj(target, opt)
+ opt = opt or {}
+ local csproj = find_csproj(target)
+ local generated = target:data("csharp.csproj.generated")
+ local generated_with_deps = target:data("csharp.csproj.generated.with_deps")
+
+ -- prefer existing source .csproj directly
+ if csproj and not generated then
+ return csproj
+ end
+
+ -- generated .csproj in memory cache
+ if csproj and generated then
+ if opt.skip_deps or generated_with_deps then
+ return csproj
+ end
+ end
+
+ if not _is_csharp_target(target) then
+ return nil
+ end
+
+ local csprojfile = csproj or _generated_csproj_path(target)
+ local generated_now = false
+
+ -- in load phase (skip_deps), reuse existing generated file to avoid
+ -- touching mtime and causing unnecessary rebuilds.
+ if not (opt.skip_deps and os.isfile(csprojfile)) then
+ generate_csproj(target, csprojfile, table.join(opt, {
+ is_csharp_target = _is_csharp_target,
+ find_or_generate_csproj = find_or_generate_csproj
+ }))
+ generated_now = true
+ end
+
+ target:data_set("csharp.csproj", csprojfile)
+ target:data_set("csharp.csproj.generated", true)
+ if opt.skip_deps then
+ -- keep this conservative in load phase: build/install phase will
+ -- generate a deps-enabled project if needed, and content checks avoid
+ -- unnecessary rewrites.
+ if generated_now or generated_with_deps == nil then
+ target:data_set("csharp.csproj.generated.with_deps", false)
+ end
+ else
+ target:data_set("csharp.csproj.generated.with_deps", true)
+ end
+ return csprojfile
+end
+
+function build_mode_to_configuration()
+ local mode
+ if type(get_config) == "function" then
+ mode = get_config("mode")
+ end
+ if not mode and type(is_mode) == "function" then
+ if is_mode("debug") then
+ mode = "debug"
+ elseif is_mode("release") then
+ mode = "release"
+ end
+ end
+ mode = mode or "release"
+ local mode_lower = mode:lower()
+ if mode_lower == "debug" then
+ return "Debug"
+ elseif mode_lower == "release" then
+ return "Release"
+ end
+ return mode:sub(1, 1):upper() .. mode:sub(2)
+end
+
+function get_runtime_identifier(target)
+ local rid = target:values("csharp.runtime_identifier")
+ if type(rid) == "table" then
+ rid = rid[1]
+ end
+ if rid and #rid > 0 then
+ return rid
+ end
+ local arch = _map_rid_arch(target:arch())
+ if not arch then
+ return nil
+ end
+ local plat = target:plat()
+ if plat == "windows" or plat == "mingw" or plat == "msys" or plat == "cygwin" then
+ return "win-" .. arch
+ elseif plat == "linux" then
+ return "linux-" .. arch
+ elseif plat == "macosx" then
+ return "osx-" .. arch
+ end
+ return nil
+end
+
--
cgit v1.3.1
From bf997637e98558b7289e8f9b3d05b08d102bb8af Mon Sep 17 00:00:00 2001
From: ruki
Date: Mon, 16 Mar 2026 23:43:37 +0800
Subject: fix generate csproj
---
xmake/rules/csharp/modules/csharp_utils.lua | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/xmake/rules/csharp/modules/csharp_utils.lua b/xmake/rules/csharp/modules/csharp_utils.lua
index 58895ff36..4861ff0b8 100644
--- a/xmake/rules/csharp/modules/csharp_utils.lua
+++ b/xmake/rules/csharp/modules/csharp_utils.lua
@@ -21,7 +21,7 @@
-- imports
import("core.base.option")
import("core.project.config")
-import("csproj_generator")
+import("csproj_generator", {alias = "generate_csproj"})
function _is_csharp_target(target)
if target:rule("csharp") then
--
cgit v1.3.1
From de6664486ef54cf7d3b50b45074539fb413e583c Mon Sep 17 00:00:00 2001
From: ruki
Date: Mon, 16 Mar 2026 23:45:18 +0800
Subject: format csharp codes
---
tests/projects/csharp/console_with_runtime_json/src/Program.cs | 4 +---
tests/projects/csharp/exe_with_library/src/lib/Greeter.cs | 7 ++-----
tests/projects/csharp/multiple_library/src/libalpha/Alpha.cs | 7 ++-----
tests/projects/csharp/multiple_library/src/libbeta/Beta.cs | 7 ++-----
xmake/repository/templates/csharp/console/src/Program.cs | 9 +++++++++
xmake/repository/templates/csharp/console/xmake.lua | 7 +++++++
xmake/repository/templates/csharp/shared/src/foo.cs | 7 +++++++
xmake/repository/templates/csharp/shared/src/main.cs | 7 +++++++
xmake/repository/templates/csharp/shared/xmake.lua | 10 ++++++++++
xmake/repository/templates/csharp/static/src/foo.cs | 7 +++++++
xmake/repository/templates/csharp/static/src/main.cs | 7 +++++++
xmake/repository/templates/csharp/static/xmake.lua | 10 ++++++++++
12 files changed, 71 insertions(+), 18 deletions(-)
create mode 100644 xmake/repository/templates/csharp/console/src/Program.cs
create mode 100644 xmake/repository/templates/csharp/console/xmake.lua
create mode 100644 xmake/repository/templates/csharp/shared/src/foo.cs
create mode 100644 xmake/repository/templates/csharp/shared/src/main.cs
create mode 100644 xmake/repository/templates/csharp/shared/xmake.lua
create mode 100644 xmake/repository/templates/csharp/static/src/foo.cs
create mode 100644 xmake/repository/templates/csharp/static/src/main.cs
create mode 100644 xmake/repository/templates/csharp/static/xmake.lua
diff --git a/tests/projects/csharp/console_with_runtime_json/src/Program.cs b/tests/projects/csharp/console_with_runtime_json/src/Program.cs
index 2c9b0783b..690faf873 100644
--- a/tests/projects/csharp/console_with_runtime_json/src/Program.cs
+++ b/tests/projects/csharp/console_with_runtime_json/src/Program.cs
@@ -1,8 +1,7 @@
using System.Text.Json;
var runtimeFile = Path.Combine(AppContext.BaseDirectory, "runtime.json");
-if (!File.Exists(runtimeFile))
-{
+if (!File.Exists(runtimeFile)) {
Console.WriteLine("runtime.json missing");
return;
}
@@ -14,4 +13,3 @@ var runtime = doc.RootElement.TryGetProperty("runtime", out var value)
: "unknown";
Console.WriteLine($"runtime={runtime}");
-
diff --git a/tests/projects/csharp/exe_with_library/src/lib/Greeter.cs b/tests/projects/csharp/exe_with_library/src/lib/Greeter.cs
index 546dd4b6f..cf895138a 100644
--- a/tests/projects/csharp/exe_with_library/src/lib/Greeter.cs
+++ b/tests/projects/csharp/exe_with_library/src/lib/Greeter.cs
@@ -1,10 +1,7 @@
namespace MyLib;
-public static class Greeter
-{
- public static string Greet(string name)
- {
+public static class Greeter {
+ public static string Greet(string name) {
return $"hello {name}!";
}
}
-
diff --git a/tests/projects/csharp/multiple_library/src/libalpha/Alpha.cs b/tests/projects/csharp/multiple_library/src/libalpha/Alpha.cs
index f203d9c9e..a10ed6d73 100644
--- a/tests/projects/csharp/multiple_library/src/libalpha/Alpha.cs
+++ b/tests/projects/csharp/multiple_library/src/libalpha/Alpha.cs
@@ -1,10 +1,7 @@
namespace LibAlpha;
-public static class Alpha
-{
- public static string Message()
- {
+public static class Alpha {
+ public static string Message() {
return "alpha";
}
}
-
diff --git a/tests/projects/csharp/multiple_library/src/libbeta/Beta.cs b/tests/projects/csharp/multiple_library/src/libbeta/Beta.cs
index 5e431039f..c96944dc5 100644
--- a/tests/projects/csharp/multiple_library/src/libbeta/Beta.cs
+++ b/tests/projects/csharp/multiple_library/src/libbeta/Beta.cs
@@ -2,11 +2,8 @@ using LibAlpha;
namespace LibBeta;
-public static class Beta
-{
- public static string Message()
- {
+public static class Beta {
+ public static string Message() {
return Alpha.Message() + "+beta";
}
}
-
diff --git a/xmake/repository/templates/csharp/console/src/Program.cs b/xmake/repository/templates/csharp/console/src/Program.cs
new file mode 100644
index 000000000..5f75209cb
--- /dev/null
+++ b/xmake/repository/templates/csharp/console/src/Program.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace ${TARGET_NAME};
+
+class Program {
+ static void Main(string[] args) {
+ Console.WriteLine("Hello, xmake!");
+ }
+}
diff --git a/xmake/repository/templates/csharp/console/xmake.lua b/xmake/repository/templates/csharp/console/xmake.lua
new file mode 100644
index 000000000..d19ce0fcd
--- /dev/null
+++ b/xmake/repository/templates/csharp/console/xmake.lua
@@ -0,0 +1,7 @@
+add_rules("mode.debug", "mode.release")
+
+target("${TARGET_NAME}")
+ set_kind("binary")
+ add_files("src/*.cs")
+
+${FAQ}
diff --git a/xmake/repository/templates/csharp/shared/src/foo.cs b/xmake/repository/templates/csharp/shared/src/foo.cs
new file mode 100644
index 000000000..bba30d999
--- /dev/null
+++ b/xmake/repository/templates/csharp/shared/src/foo.cs
@@ -0,0 +1,7 @@
+namespace Foo;
+
+public class Foo {
+ public static int Add(int a, int b) {
+ return a + b;
+ }
+}
diff --git a/xmake/repository/templates/csharp/shared/src/main.cs b/xmake/repository/templates/csharp/shared/src/main.cs
new file mode 100644
index 000000000..c2e4eb2fd
--- /dev/null
+++ b/xmake/repository/templates/csharp/shared/src/main.cs
@@ -0,0 +1,7 @@
+using System;
+
+class Program {
+ static void Main(string[] args) {
+ Console.WriteLine("add(1, 2) = {0}", Foo.Foo.Add(1, 2));
+ }
+}
diff --git a/xmake/repository/templates/csharp/shared/xmake.lua b/xmake/repository/templates/csharp/shared/xmake.lua
new file mode 100644
index 000000000..256ed0f65
--- /dev/null
+++ b/xmake/repository/templates/csharp/shared/xmake.lua
@@ -0,0 +1,10 @@
+target("foo")
+ set_kind("shared")
+ add_files("src/foo.cs")
+
+target("${TARGET_NAME}_demo")
+ set_kind("binary")
+ add_deps("foo")
+ add_files("src/main.cs")
+
+${FAQ}
diff --git a/xmake/repository/templates/csharp/static/src/foo.cs b/xmake/repository/templates/csharp/static/src/foo.cs
new file mode 100644
index 000000000..bba30d999
--- /dev/null
+++ b/xmake/repository/templates/csharp/static/src/foo.cs
@@ -0,0 +1,7 @@
+namespace Foo;
+
+public class Foo {
+ public static int Add(int a, int b) {
+ return a + b;
+ }
+}
diff --git a/xmake/repository/templates/csharp/static/src/main.cs b/xmake/repository/templates/csharp/static/src/main.cs
new file mode 100644
index 000000000..c2e4eb2fd
--- /dev/null
+++ b/xmake/repository/templates/csharp/static/src/main.cs
@@ -0,0 +1,7 @@
+using System;
+
+class Program {
+ static void Main(string[] args) {
+ Console.WriteLine("add(1, 2) = {0}", Foo.Foo.Add(1, 2));
+ }
+}
diff --git a/xmake/repository/templates/csharp/static/xmake.lua b/xmake/repository/templates/csharp/static/xmake.lua
new file mode 100644
index 000000000..51bce92a0
--- /dev/null
+++ b/xmake/repository/templates/csharp/static/xmake.lua
@@ -0,0 +1,10 @@
+target("foo")
+ set_kind("static")
+ add_files("src/foo.cs")
+
+target("${TARGET_NAME}_demo")
+ set_kind("binary")
+ add_deps("foo")
+ add_files("src/main.cs")
+
+${FAQ}
--
cgit v1.3.1
From 24652077a08c7f5d08e091b4e3907d86109d4028 Mon Sep 17 00:00:00 2001
From: ruki
Date: Mon, 16 Mar 2026 23:45:42 +0800
Subject: remove csharp extension
---
xmake/rules/csharp/xmake.lua | 11 -----------
1 file changed, 11 deletions(-)
diff --git a/xmake/rules/csharp/xmake.lua b/xmake/rules/csharp/xmake.lua
index 3b6e3bf94..30611d68a 100644
--- a/xmake/rules/csharp/xmake.lua
+++ b/xmake/rules/csharp/xmake.lua
@@ -20,17 +20,6 @@
rule("csharp.build")
set_sourcekinds("cs")
- on_load(function (target)
- -- set target extension and prefix
- if target:is_shared() then
- if not target:get("extension") then
- target:set("extension", ".dll")
- end
- if target:get("prefixname") == nil then
- target:set("prefixname", "")
- end
- end
- end)
on_build("build.target")
rule("csharp")
--
cgit v1.3.1
From 9beeacf1045a8026486595b46822baced58ae00b Mon Sep 17 00:00:00 2001
From: ruki
Date: Mon, 16 Mar 2026 23:46:36 +0800
Subject: improve to import csharp utils
---
xmake/rules/csharp/build/target.lua | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/xmake/rules/csharp/build/target.lua b/xmake/rules/csharp/build/target.lua
index 43ee036b1..c1111fed1 100644
--- a/xmake/rules/csharp/build/target.lua
+++ b/xmake/rules/csharp/build/target.lua
@@ -23,7 +23,7 @@ import("core.base.option")
import("core.tool.compiler")
import("core.project.depend")
import("utils.progress")
-import("rules.csharp.modules.csharp_utils", {rootdir = os.programdir(), alias = "csharp_utils"})
+import(".modules.csharp_utils")
-- find or generate .csproj file
function _get_csprojfile(target)
--
cgit v1.3.1
From da763bea747c32be00b1385f830ad1f741e3f490 Mon Sep 17 00:00:00 2001
From: ruki
Date: Mon, 16 Mar 2026 23:46:42 +0800
Subject: improve get csproj file
---
xmake/rules/csharp/build/target.lua | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/xmake/rules/csharp/build/target.lua b/xmake/rules/csharp/build/target.lua
index c1111fed1..522ef05f0 100644
--- a/xmake/rules/csharp/build/target.lua
+++ b/xmake/rules/csharp/build/target.lua
@@ -28,11 +28,8 @@ import(".modules.csharp_utils")
-- find or generate .csproj file
function _get_csprojfile(target)
local csprojfile = target:data("csharp.csproj")
- if csprojfile then
- return csprojfile
- end
- csprojfile = csharp_utils.find_or_generate_csproj(target)
- if csprojfile then
+ if not csprojfile then
+ csprojfile = csharp_utils.find_or_generate_csproj(target)
target:data_set("csharp.csproj", csprojfile)
end
return csprojfile
--
cgit v1.3.1
From 9ef3fac69885dccdb5feb27fed261b05984b914b Mon Sep 17 00:00:00 2001
From: ruki
Date: Mon, 16 Mar 2026 23:47:19 +0800
Subject: remove unused codes
---
xmake/modules/core/tools/dotnet.lua | 6 ------
1 file changed, 6 deletions(-)
diff --git a/xmake/modules/core/tools/dotnet.lua b/xmake/modules/core/tools/dotnet.lua
index 867112dc7..62d9a63d2 100644
--- a/xmake/modules/core/tools/dotnet.lua
+++ b/xmake/modules/core/tools/dotnet.lua
@@ -76,12 +76,6 @@ function _get_csprojfile(sourcefiles, opt)
return csprojfile
end
end
- -- find .csproj from source files
- for _, sourcefile in ipairs(sourcefiles) do
- if sourcefile:endswith(".csproj") then
- return sourcefile
- end
- end
end
-- get dotnet verbosity level
--
cgit v1.3.1
From 8aa975d2fc26c603146c933684aba299bd3dc60c Mon Sep 17 00:00:00 2001
From: ruki
Date: Mon, 16 Mar 2026 23:48:44 +0800
Subject: improve to get csproj file
---
xmake/modules/core/tools/dotnet.lua | 4 ++--
xmake/rules/csharp/build/target.lua | 11 +++--------
2 files changed, 5 insertions(+), 10 deletions(-)
diff --git a/xmake/modules/core/tools/dotnet.lua b/xmake/modules/core/tools/dotnet.lua
index 62d9a63d2..9a502b616 100644
--- a/xmake/modules/core/tools/dotnet.lua
+++ b/xmake/modules/core/tools/dotnet.lua
@@ -68,7 +68,7 @@ function nf_warning(self, level)
end
-- get the .csproj file from source files
-function _get_csprojfile(sourcefiles, opt)
+function _get_csprojfile(opt)
local target = opt and opt.target
if target then
local csprojfile = target:data("csharp.csproj")
@@ -100,7 +100,7 @@ function buildargv(self, sourcefiles, targetkind, targetfile, flags, opt)
local argv = {"build"}
-- add .csproj file
- local csprojfile = _get_csprojfile(sourcefiles, opt)
+ local csprojfile = _get_csprojfile(opt)
if csprojfile then
table.insert(argv, csprojfile)
end
diff --git a/xmake/rules/csharp/build/target.lua b/xmake/rules/csharp/build/target.lua
index 522ef05f0..689bbe6c5 100644
--- a/xmake/rules/csharp/build/target.lua
+++ b/xmake/rules/csharp/build/target.lua
@@ -42,14 +42,9 @@ function build_sourcefiles(target, sourcebatch, opt)
local targetfile = target:targetfile()
-- get source files and kind
- local sourcefiles = table.copy(sourcebatch.sourcefiles)
+ local sourcefiles = sourcebatch.sourcefiles
local sourcekind = sourcebatch.sourcekind
-
- -- prepend .csproj file so dotnet tool can find it
- local csprojfile = _get_csprojfile(target)
- if csprojfile then
- table.insert(sourcefiles, 1, csprojfile)
- end
+ local csprojfile = _get_csprojfile(target)
-- get depend file
local dependfile = target:dependfile(targetfile)
@@ -84,7 +79,7 @@ function build_sourcefiles(target, sourcebatch, opt)
-- update files and values to the dependent file
dependinfo.values = depvalues
- table.join2(dependinfo.files, sourcefiles)
+ table.join2(dependinfo.files, sourcefiles, csprojfile)
depend.save(dependinfo, dependfile)
end
--
cgit v1.3.1
From 495d8e9f70608edf20f9b719df96312a21ae78c3 Mon Sep 17 00:00:00 2001
From: ruki
Date: Mon, 16 Mar 2026 23:49:33 +0800
Subject: pass opt to buildargv
---
xmake/core/tool/compiler.lua | 83 ++------------------------------------------
1 file changed, 2 insertions(+), 81 deletions(-)
diff --git a/xmake/core/tool/compiler.lua b/xmake/core/tool/compiler.lua
index dce7bf532..1afa7165e 100644
--- a/xmake/core/tool/compiler.lua
+++ b/xmake/core/tool/compiler.lua
@@ -203,73 +203,6 @@ function compiler.load(sourcekind, target)
return instance
end
---[[
--- build the source files (compile and link)
-function compiler:build(sourcefiles, targetfile, opt)
- opt = opt or {}
- local target = opt.target or self:target()
- if not opt.target and target then
- opt = table.copy(opt)
- opt.target = target
- end
-
- -- get compile flags
- local compflags = opt.compflags
- if not compflags then
- -- patch sourcefile to get flags of the given source file
- if type(sourcefiles) == "string" then
- opt.sourcefile = sourcefiles
- end
- compflags = self:compflags(opt)
- end
-
- -- make flags
- local flags = compflags
- if target then
- flags = table.join(flags, target:linkflags())
- end
-
- -- get target kind
- local targetkind = opt.targetkind
- if not targetkind and target and target.targetkind then
- targetkind = target:kind()
- end
- return sandbox.load(self:_tool().build, self:_tool(), sourcefiles, targetkind or "binary", targetfile, flags, opt)
-end
-
--- get the build arguments list (compile and link)
-function compiler:buildargv(sourcefiles, targetfile, opt)
- opt = opt or {}
- local target = opt.target or self:target()
- if not opt.target and target then
- opt = table.copy(opt)
- opt.target = target
- end
-
- -- get compile flags
- local compflags = opt.compflags
- if not compflags then
- -- patch sourcefile to get flags of the given source file
- if type(sourcefiles) == "string" then
- opt.sourcefile = sourcefiles
- end
- compflags = self:compflags(opt)
- end
-
- -- make flags
- local flags = compflags
- if target then
- flags = table.join(flags, target:linkflags())
- end
-
- -- get target kind
- local targetkind = opt.targetkind
- if not targetkind and target and target.targetkind then
- targetkind = target:kind()
- end
- return self:_tool():buildargv(sourcefiles, targetkind or "binary", targetfile, flags, opt)
-end]]
-
-- build the source files (compile and link)
function compiler:build(sourcefiles, targetfile, opt)
opt = opt or {}
@@ -295,7 +228,7 @@ function compiler:build(sourcefiles, targetfile, opt)
if not targetkind and opt.target and opt.target.targetkind then
targetkind = opt.target:kind()
end
- return sandbox.load(self:_tool().build, self:_tool(), sourcefiles, targetkind or "binary", targetfile, flags)
+ return sandbox.load(self:_tool().build, self:_tool(), sourcefiles, targetkind or "binary", targetfile, flags, opt)
end
-- get the build arguments list (compile and link)
@@ -323,7 +256,7 @@ function compiler:buildargv(sourcefiles, targetfile, opt)
if not targetkind and opt.target and opt.target.targetkind then
targetkind = opt.target:kind()
end
- return self:_tool():buildargv(sourcefiles, targetkind or "binary", targetfile, flags)
+ return self:_tool():buildargv(sourcefiles, targetkind or "binary", targetfile, flags, opt)
end
-- get the build command
@@ -334,12 +267,6 @@ end
-- compile the source files
function compiler:compile(sourcefiles, objectfile, opt)
opt = opt or {}
- --[[
- local target = opt.target or self:target()
- if not opt.target and target then
- opt = table.copy(opt)
- opt.target = target
- end]]
-- get compile flags
local compflags = opt.compflags
@@ -363,12 +290,6 @@ end
-- get the compile arguments list
function compiler:compargv(sourcefiles, objectfile, opt)
opt = opt or {}
- --[[
- local target = opt.target or self:target()
- if not opt.target and target then
- opt = table.copy(opt)
- opt.target = target
- end]]
-- get compile flags
local compflags = opt.compflags
--
cgit v1.3.1
From 0c81f2b127c94a75257f05e24508209cc10419b0 Mon Sep 17 00:00:00 2001
From: ruki
Date: Mon, 16 Mar 2026 23:51:45 +0800
Subject: remove find csproj
---
xmake/rules/csharp/build/target.lua | 4 +-
xmake/rules/csharp/modules/csharp_utils.lua | 66 +++--------------------------
xmake/rules/csharp/modules/itemgroups.lua | 2 +-
3 files changed, 9 insertions(+), 63 deletions(-)
diff --git a/xmake/rules/csharp/build/target.lua b/xmake/rules/csharp/build/target.lua
index 689bbe6c5..eb61bdef9 100644
--- a/xmake/rules/csharp/build/target.lua
+++ b/xmake/rules/csharp/build/target.lua
@@ -25,11 +25,11 @@ import("core.project.depend")
import("utils.progress")
import(".modules.csharp_utils")
--- find or generate .csproj file
+-- generate .csproj file
function _get_csprojfile(target)
local csprojfile = target:data("csharp.csproj")
if not csprojfile then
- csprojfile = csharp_utils.find_or_generate_csproj(target)
+ csprojfile = csharp_utils.generate_csproj_file(target)
target:data_set("csharp.csproj", csprojfile)
end
return csprojfile
diff --git a/xmake/rules/csharp/modules/csharp_utils.lua b/xmake/rules/csharp/modules/csharp_utils.lua
index 4861ff0b8..23a9978ef 100644
--- a/xmake/rules/csharp/modules/csharp_utils.lua
+++ b/xmake/rules/csharp/modules/csharp_utils.lua
@@ -59,69 +59,15 @@ function _map_rid_arch(arch)
return nil
end
-function find_csproj(target)
- local csproj = target:data("csharp.csproj")
- if csproj then
- return csproj
- end
- for _, sourcefile in ipairs(target:sourcefiles()) do
- if path.extension(sourcefile):lower() == ".csproj" then
- local csprojabs = path.is_absolute(sourcefile) and sourcefile or path.absolute(sourcefile, os.projectdir())
- if os.isfile(csprojabs) then
- return csprojabs
- end
- end
- end
- return nil
-end
-
-function find_or_generate_csproj(target, opt)
- opt = opt or {}
- local csproj = find_csproj(target)
- local generated = target:data("csharp.csproj.generated")
- local generated_with_deps = target:data("csharp.csproj.generated.with_deps")
-
- -- prefer existing source .csproj directly
- if csproj and not generated then
- return csproj
- end
-
- -- generated .csproj in memory cache
- if csproj and generated then
- if opt.skip_deps or generated_with_deps then
- return csproj
- end
- end
-
+function generate_csproj_file(target, opt)
if not _is_csharp_target(target) then
return nil
end
-
- local csprojfile = csproj or _generated_csproj_path(target)
- local generated_now = false
-
- -- in load phase (skip_deps), reuse existing generated file to avoid
- -- touching mtime and causing unnecessary rebuilds.
- if not (opt.skip_deps and os.isfile(csprojfile)) then
- generate_csproj(target, csprojfile, table.join(opt, {
- is_csharp_target = _is_csharp_target,
- find_or_generate_csproj = find_or_generate_csproj
- }))
- generated_now = true
- end
-
- target:data_set("csharp.csproj", csprojfile)
- target:data_set("csharp.csproj.generated", true)
- if opt.skip_deps then
- -- keep this conservative in load phase: build/install phase will
- -- generate a deps-enabled project if needed, and content checks avoid
- -- unnecessary rewrites.
- if generated_now or generated_with_deps == nil then
- target:data_set("csharp.csproj.generated.with_deps", false)
- end
- else
- target:data_set("csharp.csproj.generated.with_deps", true)
- end
+ local csprojfile = _generated_csproj_path(target)
+ generate_csproj(target, csprojfile, table.join(opt or {}, {
+ is_csharp_target = _is_csharp_target,
+ generate_csproj_file = generate_csproj_file
+ }))
return csprojfile
end
diff --git a/xmake/rules/csharp/modules/itemgroups.lua b/xmake/rules/csharp/modules/itemgroups.lua
index 6a7506bba..93e25ea53 100644
--- a/xmake/rules/csharp/modules/itemgroups.lua
+++ b/xmake/rules/csharp/modules/itemgroups.lua
@@ -46,7 +46,7 @@ function _collect_project_references(context)
for _, depname in ipairs(table.wrap(context.target:get("deps"))) do
local dep = context.target:dep(depname)
if dep and context.opt.is_csharp_target and context.opt.is_csharp_target(dep) then
- local depcsproj = context.opt.find_or_generate_csproj and context.opt.find_or_generate_csproj(dep)
+ local depcsproj = context.opt.generate_csproj_file and context.opt.generate_csproj_file(dep)
if depcsproj then
table.insert(references, _normalize_relative(context.csprojdir, depcsproj))
end
--
cgit v1.3.1
From a058333a5ce80508fd7088a5def1bbbcadd2a0c1 Mon Sep 17 00:00:00 2001
From: ruki
Date: Mon, 16 Mar 2026 23:55:21 +0800
Subject: remove find csproj files
---
xmake/rules/csharp/modules/itemgroups.lua | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/xmake/rules/csharp/modules/itemgroups.lua b/xmake/rules/csharp/modules/itemgroups.lua
index 93e25ea53..b03110ea9 100644
--- a/xmake/rules/csharp/modules/itemgroups.lua
+++ b/xmake/rules/csharp/modules/itemgroups.lua
@@ -39,13 +39,9 @@ function _collect_cs_sourcefiles(context)
end
function _collect_project_references(context)
- if context.opt.skip_deps then
- return {}
- end
local references = {}
- for _, depname in ipairs(table.wrap(context.target:get("deps"))) do
- local dep = context.target:dep(depname)
- if dep and context.opt.is_csharp_target and context.opt.is_csharp_target(dep) then
+ for _, dep in ipairs(context.target:orderdeps()) do
+ if context.opt.is_csharp_target and context.opt.is_csharp_target(dep) then
local depcsproj = context.opt.generate_csproj_file and context.opt.generate_csproj_file(dep)
if depcsproj then
table.insert(references, _normalize_relative(context.csprojdir, depcsproj))
--
cgit v1.3.1
From 0b6dfe22a3a5e2e0d305b3cea78f524812238fe7 Mon Sep 17 00:00:00 2001
From: ruki
Date: Mon, 16 Mar 2026 23:55:52 +0800
Subject: fix some csproj ref issues
---
xmake/rules/csharp/modules/csharp_utils.lua | 1 +
1 file changed, 1 insertion(+)
diff --git a/xmake/rules/csharp/modules/csharp_utils.lua b/xmake/rules/csharp/modules/csharp_utils.lua
index 23a9978ef..6d19b39b7 100644
--- a/xmake/rules/csharp/modules/csharp_utils.lua
+++ b/xmake/rules/csharp/modules/csharp_utils.lua
@@ -68,6 +68,7 @@ function generate_csproj_file(target, opt)
is_csharp_target = _is_csharp_target,
generate_csproj_file = generate_csproj_file
}))
+ target:data_set("csharp.csproj", csprojfile)
return csprojfile
end
--
cgit v1.3.1
From 0f22c796d88325884e568fbe5a559323ba862fe9 Mon Sep 17 00:00:00 2001
From: ruki
Date: Mon, 16 Mar 2026 23:56:29 +0800
Subject: fix some csproj refs again
---
xmake/rules/csharp/modules/csharp_utils.lua | 7 ++-----
xmake/rules/csharp/modules/itemgroups.lua | 8 +++-----
2 files changed, 5 insertions(+), 10 deletions(-)
diff --git a/xmake/rules/csharp/modules/csharp_utils.lua b/xmake/rules/csharp/modules/csharp_utils.lua
index 6d19b39b7..404be965d 100644
--- a/xmake/rules/csharp/modules/csharp_utils.lua
+++ b/xmake/rules/csharp/modules/csharp_utils.lua
@@ -59,15 +59,12 @@ function _map_rid_arch(arch)
return nil
end
-function generate_csproj_file(target, opt)
+function generate_csproj_file(target)
if not _is_csharp_target(target) then
return nil
end
local csprojfile = _generated_csproj_path(target)
- generate_csproj(target, csprojfile, table.join(opt or {}, {
- is_csharp_target = _is_csharp_target,
- generate_csproj_file = generate_csproj_file
- }))
+ generate_csproj(target, csprojfile)
target:data_set("csharp.csproj", csprojfile)
return csprojfile
end
diff --git a/xmake/rules/csharp/modules/itemgroups.lua b/xmake/rules/csharp/modules/itemgroups.lua
index b03110ea9..e9367f96f 100644
--- a/xmake/rules/csharp/modules/itemgroups.lua
+++ b/xmake/rules/csharp/modules/itemgroups.lua
@@ -41,11 +41,9 @@ end
function _collect_project_references(context)
local references = {}
for _, dep in ipairs(context.target:orderdeps()) do
- if context.opt.is_csharp_target and context.opt.is_csharp_target(dep) then
- local depcsproj = context.opt.generate_csproj_file and context.opt.generate_csproj_file(dep)
- if depcsproj then
- table.insert(references, _normalize_relative(context.csprojdir, depcsproj))
- end
+ local depcsproj = dep:data("csharp.csproj")
+ if depcsproj then
+ table.insert(references, _normalize_relative(context.csprojdir, depcsproj))
end
end
table.sort(references)
--
cgit v1.3.1
From b4a1c11be9dac3472299aba9165b4b182dcdc8c1 Mon Sep 17 00:00:00 2001
From: ruki
Date: Tue, 17 Mar 2026 00:02:11 +0800
Subject: remove unused codes
---
xmake/rules/csharp/modules/csharp_utils.lua | 62 -----------------------------
1 file changed, 62 deletions(-)
diff --git a/xmake/rules/csharp/modules/csharp_utils.lua b/xmake/rules/csharp/modules/csharp_utils.lua
index 404be965d..78837cff9 100644
--- a/xmake/rules/csharp/modules/csharp_utils.lua
+++ b/xmake/rules/csharp/modules/csharp_utils.lua
@@ -19,7 +19,6 @@
--
-- imports
-import("core.base.option")
import("core.project.config")
import("csproj_generator", {alias = "generate_csproj"})
@@ -43,22 +42,6 @@ function _generated_csproj_path(target)
return path.join(csprojdir, csprojname)
end
-function _map_rid_arch(arch)
- arch = (arch or ""):lower()
- if arch == "x64" or arch == "x86_64" or arch == "amd64" then
- return "x64"
- elseif arch == "x86" or arch == "i386" then
- return "x86"
- elseif arch == "arm64" then
- return "arm64"
- elseif arch == "arm" or arch == "armv7" then
- return "arm"
- elseif arch == "riscv64" then
- return "riscv64"
- end
- return nil
-end
-
function generate_csproj_file(target)
if not _is_csharp_target(target) then
return nil
@@ -69,48 +52,3 @@ function generate_csproj_file(target)
return csprojfile
end
-function build_mode_to_configuration()
- local mode
- if type(get_config) == "function" then
- mode = get_config("mode")
- end
- if not mode and type(is_mode) == "function" then
- if is_mode("debug") then
- mode = "debug"
- elseif is_mode("release") then
- mode = "release"
- end
- end
- mode = mode or "release"
- local mode_lower = mode:lower()
- if mode_lower == "debug" then
- return "Debug"
- elseif mode_lower == "release" then
- return "Release"
- end
- return mode:sub(1, 1):upper() .. mode:sub(2)
-end
-
-function get_runtime_identifier(target)
- local rid = target:values("csharp.runtime_identifier")
- if type(rid) == "table" then
- rid = rid[1]
- end
- if rid and #rid > 0 then
- return rid
- end
- local arch = _map_rid_arch(target:arch())
- if not arch then
- return nil
- end
- local plat = target:plat()
- if plat == "windows" or plat == "mingw" or plat == "msys" or plat == "cygwin" then
- return "win-" .. arch
- elseif plat == "linux" then
- return "linux-" .. arch
- elseif plat == "macosx" then
- return "osx-" .. arch
- end
- return nil
-end
-
--
cgit v1.3.1
From 9ad1383808d20fbb5ffcfd69dfb507f87c28c3ac Mon Sep 17 00:00:00 2001
From: ruki
Date: Tue, 17 Mar 2026 00:03:55 +0800
Subject: move generate csproj to prepare.lua
---
xmake/rules/csharp/build.lua | 86 +++++++++++++++++++++++++
xmake/rules/csharp/build/target.lua | 97 -----------------------------
xmake/rules/csharp/modules/csharp_utils.lua | 54 ----------------
xmake/rules/csharp/prepare.lua | 50 +++++++++++++++
xmake/rules/csharp/xmake.lua | 3 +-
5 files changed, 138 insertions(+), 152 deletions(-)
create mode 100644 xmake/rules/csharp/build.lua
delete mode 100644 xmake/rules/csharp/build/target.lua
delete mode 100644 xmake/rules/csharp/modules/csharp_utils.lua
create mode 100644 xmake/rules/csharp/prepare.lua
diff --git a/xmake/rules/csharp/build.lua b/xmake/rules/csharp/build.lua
new file mode 100644
index 000000000..9bc6f2e76
--- /dev/null
+++ b/xmake/rules/csharp/build.lua
@@ -0,0 +1,86 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author ruki
+-- @file target.lua
+--
+
+-- imports
+import("core.base.option")
+import("core.tool.compiler")
+import("core.project.depend")
+import("utils.progress")
+
+-- build the source files
+function build_sourcefiles(target, sourcebatch, opt)
+
+ -- get the target file
+ local targetfile = target:targetfile()
+
+ -- get source files and kind
+ local sourcefiles = sourcebatch.sourcefiles
+ local sourcekind = sourcebatch.sourcekind
+ local csprojfile = target:data("csharp.csproj")
+
+ -- get depend file
+ local dependfile = target:dependfile(targetfile)
+
+ -- load compiler
+ local compinst = compiler.load(sourcekind, {target = target})
+
+ -- get compile flags
+ local compflags = compinst:compflags({target = target})
+
+ -- load dependent info
+ local dependinfo = option.get("rebuild") and {} or (depend.load(dependfile) or {})
+
+ -- need build this target?
+ local depvalues = {compinst:program(), compflags}
+ if not depend.is_changed(dependinfo, {lastmtime = os.mtime(targetfile), values = depvalues}) then
+ return
+ end
+
+ -- trace progress info
+ progress.show(opt.progress, "${color.build.target}linking.$(mode) %s", path.filename(targetfile))
+
+ -- trace verbose info
+ vprint(compinst:buildcmd(sourcefiles, targetfile, {target = target, compflags = compflags}))
+
+ -- flush io buffer to update progress info
+ io.flush()
+
+ -- build it
+ dependinfo.files = {}
+ assert(compinst:build(sourcefiles, targetfile, {target = target, dependinfo = dependinfo, compflags = compflags}))
+
+ -- update files and values to the dependent file
+ dependinfo.values = depvalues
+ table.join2(dependinfo.files, sourcefiles, csprojfile)
+ depend.save(dependinfo, dependfile)
+end
+
+-- build target
+function main(target, opt)
+
+ -- @note only support one source kind!
+ local sourcebatches = target:sourcebatches()
+ if sourcebatches then
+ local sourcebatch = sourcebatches["csharp.build"]
+ if sourcebatch then
+ build_sourcefiles(target, sourcebatch, opt)
+ end
+ end
+end
diff --git a/xmake/rules/csharp/build/target.lua b/xmake/rules/csharp/build/target.lua
deleted file mode 100644
index eb61bdef9..000000000
--- a/xmake/rules/csharp/build/target.lua
+++ /dev/null
@@ -1,97 +0,0 @@
---!A cross-platform build utility based on Lua
---
--- Licensed under the Apache License, Version 2.0 (the "License");
--- you may not use this file except in compliance with the License.
--- You may obtain a copy of the License at
---
--- http://www.apache.org/licenses/LICENSE-2.0
---
--- Unless required by applicable law or agreed to in writing, software
--- distributed under the License is distributed on an "AS IS" BASIS,
--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--- See the License for the specific language governing permissions and
--- limitations under the License.
---
--- Copyright (C) 2015-present, Xmake Open Source Community.
---
--- @author ruki
--- @file target.lua
---
-
--- imports
-import("core.base.option")
-import("core.tool.compiler")
-import("core.project.depend")
-import("utils.progress")
-import(".modules.csharp_utils")
-
--- generate .csproj file
-function _get_csprojfile(target)
- local csprojfile = target:data("csharp.csproj")
- if not csprojfile then
- csprojfile = csharp_utils.generate_csproj_file(target)
- target:data_set("csharp.csproj", csprojfile)
- end
- return csprojfile
-end
-
--- build the source files
-function build_sourcefiles(target, sourcebatch, opt)
-
- -- get the target file
- local targetfile = target:targetfile()
-
- -- get source files and kind
- local sourcefiles = sourcebatch.sourcefiles
- local sourcekind = sourcebatch.sourcekind
- local csprojfile = _get_csprojfile(target)
-
- -- get depend file
- local dependfile = target:dependfile(targetfile)
-
- -- load compiler
- local compinst = compiler.load(sourcekind, {target = target})
-
- -- get compile flags
- local compflags = compinst:compflags({target = target})
-
- -- load dependent info
- local dependinfo = option.get("rebuild") and {} or (depend.load(dependfile) or {})
-
- -- need build this target?
- local depvalues = {compinst:program(), compflags}
- if not depend.is_changed(dependinfo, {lastmtime = os.mtime(targetfile), values = depvalues}) then
- return
- end
-
- -- trace progress info
- progress.show(opt.progress, "${color.build.target}linking.$(mode) %s", path.filename(targetfile))
-
- -- trace verbose info
- vprint(compinst:buildcmd(sourcefiles, targetfile, {target = target, compflags = compflags}))
-
- -- flush io buffer to update progress info
- io.flush()
-
- -- build it
- dependinfo.files = {}
- assert(compinst:build(sourcefiles, targetfile, {target = target, dependinfo = dependinfo, compflags = compflags}))
-
- -- update files and values to the dependent file
- dependinfo.values = depvalues
- table.join2(dependinfo.files, sourcefiles, csprojfile)
- depend.save(dependinfo, dependfile)
-end
-
--- build target
-function main(target, opt)
-
- -- @note only support one source kind!
- local sourcebatches = target:sourcebatches()
- if sourcebatches then
- local sourcebatch = sourcebatches["csharp.build"]
- if sourcebatch then
- build_sourcefiles(target, sourcebatch, opt)
- end
- end
-end
diff --git a/xmake/rules/csharp/modules/csharp_utils.lua b/xmake/rules/csharp/modules/csharp_utils.lua
deleted file mode 100644
index 78837cff9..000000000
--- a/xmake/rules/csharp/modules/csharp_utils.lua
+++ /dev/null
@@ -1,54 +0,0 @@
---!A cross-platform build utility based on Lua
---
--- Licensed under the Apache License, Version 2.0 (the "License");
--- you may not use this file except in compliance with the License.
--- You may obtain a copy of the License at
---
--- http://www.apache.org/licenses/LICENSE-2.0
---
--- Unless required by applicable law or agreed to in writing, software
--- distributed under the License is distributed on an "AS IS" BASIS,
--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--- See the License for the specific language governing permissions and
--- limitations under the License.
---
--- Copyright (C) 2015-present, Xmake Open Source Community.
---
--- @author JassJam
--- @file csharp_utils.lua
---
-
--- imports
-import("core.project.config")
-import("csproj_generator", {alias = "generate_csproj"})
-
-function _is_csharp_target(target)
- if target:rule("csharp") then
- return true
- end
- for _, sourcefile in ipairs(target:sourcefiles()) do
- local ext = path.extension(sourcefile):lower()
- if ext == ".cs" or ext == ".csproj" then
- return true
- end
- end
- return false
-end
-
-function _generated_csproj_path(target)
- local targetkey = target:fullname():replace("::", path.sep())
- local csprojdir = path.join(config.directory(), "rules", "csharp", targetkey, target:plat(), target:arch())
- local csprojname = target:name() .. ".csproj"
- return path.join(csprojdir, csprojname)
-end
-
-function generate_csproj_file(target)
- if not _is_csharp_target(target) then
- return nil
- end
- local csprojfile = _generated_csproj_path(target)
- generate_csproj(target, csprojfile)
- target:data_set("csharp.csproj", csprojfile)
- return csprojfile
-end
-
diff --git a/xmake/rules/csharp/prepare.lua b/xmake/rules/csharp/prepare.lua
new file mode 100644
index 000000000..611ad5890
--- /dev/null
+++ b/xmake/rules/csharp/prepare.lua
@@ -0,0 +1,50 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author ruki
+-- @file prepare.lua
+--
+
+-- imports
+import("core.project.config")
+import("core.project.depend")
+import("modules.csproj_generator", {rootdir = os.scriptdir()})
+
+function main(target)
+
+ -- compute csproj path
+ local targetkey = target:fullname():replace("::", path.sep())
+ local csprojdir = path.join(config.directory(), "rules", "csharp", targetkey, target:plat(), target:arch())
+ local csprojfile = path.join(csprojdir, target:name() .. ".csproj")
+ local dependfile = target:dependfile(csprojfile)
+
+ -- collect source files and dep csproj paths as depend values
+ local sourcefiles = target:sourcefiles()
+ local depcsproj = {}
+ for _, dep in ipairs(target:orderdeps()) do
+ local depcsproj_path = dep:data("csharp.csproj")
+ if depcsproj_path then
+ table.insert(depcsproj, depcsproj_path)
+ end
+ end
+
+ -- generate csproj incrementally
+ depend.on_changed(function ()
+ csproj_generator(target, csprojfile)
+ end, {dependfile = dependfile, files = sourcefiles, values = depcsproj})
+
+ target:data_set("csharp.csproj", csprojfile)
+end
diff --git a/xmake/rules/csharp/xmake.lua b/xmake/rules/csharp/xmake.lua
index 30611d68a..1d5818337 100644
--- a/xmake/rules/csharp/xmake.lua
+++ b/xmake/rules/csharp/xmake.lua
@@ -20,7 +20,8 @@
rule("csharp.build")
set_sourcekinds("cs")
- on_build("build.target")
+ on_prepare("prepare")
+ on_build("build")
rule("csharp")
add_deps("csharp.build")
--
cgit v1.3.1
From f8723406844fa8b2f2fe9904e67e69e6cd99a9d2 Mon Sep 17 00:00:00 2001
From: ruki
Date: Tue, 17 Mar 2026 00:04:58 +0800
Subject: move generate csproj to config
---
xmake/rules/csharp/config.lua | 50 ++++++++++++++++++++++++++++++++++++++++++
xmake/rules/csharp/prepare.lua | 50 ------------------------------------------
xmake/rules/csharp/xmake.lua | 2 +-
3 files changed, 51 insertions(+), 51 deletions(-)
create mode 100644 xmake/rules/csharp/config.lua
delete mode 100644 xmake/rules/csharp/prepare.lua
diff --git a/xmake/rules/csharp/config.lua b/xmake/rules/csharp/config.lua
new file mode 100644
index 000000000..97b3a4303
--- /dev/null
+++ b/xmake/rules/csharp/config.lua
@@ -0,0 +1,50 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author ruki
+-- @file config.lua
+--
+
+-- imports
+import("core.project.config")
+import("core.project.depend")
+import("modules.csproj_generator", {rootdir = os.scriptdir()})
+
+function main(target)
+
+ -- compute csproj path
+ local targetkey = target:fullname():replace("::", path.sep())
+ local csprojdir = path.join(config.directory(), "rules", "csharp", targetkey, target:plat(), target:arch())
+ local csprojfile = path.join(csprojdir, target:name() .. ".csproj")
+ local dependfile = target:dependfile(csprojfile)
+
+ -- collect source files and dep csproj paths as depend values
+ local sourcefiles = target:sourcefiles()
+ local depcsproj = {}
+ for _, dep in ipairs(target:orderdeps()) do
+ local depcsproj_path = dep:data("csharp.csproj")
+ if depcsproj_path then
+ table.insert(depcsproj, depcsproj_path)
+ end
+ end
+
+ -- generate csproj incrementally
+ depend.on_changed(function ()
+ csproj_generator(target, csprojfile)
+ end, {dependfile = dependfile, files = sourcefiles, values = depcsproj})
+
+ target:data_set("csharp.csproj", csprojfile)
+end
diff --git a/xmake/rules/csharp/prepare.lua b/xmake/rules/csharp/prepare.lua
deleted file mode 100644
index 611ad5890..000000000
--- a/xmake/rules/csharp/prepare.lua
+++ /dev/null
@@ -1,50 +0,0 @@
---!A cross-platform build utility based on Lua
---
--- Licensed under the Apache License, Version 2.0 (the "License");
--- you may not use this file except in compliance with the License.
--- You may obtain a copy of the License at
---
--- http://www.apache.org/licenses/LICENSE-2.0
---
--- Unless required by applicable law or agreed to in writing, software
--- distributed under the License is distributed on an "AS IS" BASIS,
--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--- See the License for the specific language governing permissions and
--- limitations under the License.
---
--- Copyright (C) 2015-present, Xmake Open Source Community.
---
--- @author ruki
--- @file prepare.lua
---
-
--- imports
-import("core.project.config")
-import("core.project.depend")
-import("modules.csproj_generator", {rootdir = os.scriptdir()})
-
-function main(target)
-
- -- compute csproj path
- local targetkey = target:fullname():replace("::", path.sep())
- local csprojdir = path.join(config.directory(), "rules", "csharp", targetkey, target:plat(), target:arch())
- local csprojfile = path.join(csprojdir, target:name() .. ".csproj")
- local dependfile = target:dependfile(csprojfile)
-
- -- collect source files and dep csproj paths as depend values
- local sourcefiles = target:sourcefiles()
- local depcsproj = {}
- for _, dep in ipairs(target:orderdeps()) do
- local depcsproj_path = dep:data("csharp.csproj")
- if depcsproj_path then
- table.insert(depcsproj, depcsproj_path)
- end
- end
-
- -- generate csproj incrementally
- depend.on_changed(function ()
- csproj_generator(target, csprojfile)
- end, {dependfile = dependfile, files = sourcefiles, values = depcsproj})
-
- target:data_set("csharp.csproj", csprojfile)
-end
diff --git a/xmake/rules/csharp/xmake.lua b/xmake/rules/csharp/xmake.lua
index 1d5818337..f13003520 100644
--- a/xmake/rules/csharp/xmake.lua
+++ b/xmake/rules/csharp/xmake.lua
@@ -20,7 +20,7 @@
rule("csharp.build")
set_sourcekinds("cs")
- on_prepare("prepare")
+ on_config("config")
on_build("build")
rule("csharp")
--
cgit v1.3.1
From 701014b37d30227f2fa38ebe6ce344ab766513a2 Mon Sep 17 00:00:00 2001
From: ruki
Date: Tue, 17 Mar 2026 22:37:53 +0800
Subject: improve csharp generator
---
xmake/rules/csharp/config.lua | 9 ++--
xmake/rules/csharp/modules/csproj_generator.lua | 58 ++++++++++---------------
xmake/rules/csharp/modules/itemgroups.lua | 2 +-
xmake/rules/csharp/modules/properties.lua | 7 ++-
4 files changed, 30 insertions(+), 46 deletions(-)
diff --git a/xmake/rules/csharp/config.lua b/xmake/rules/csharp/config.lua
index 97b3a4303..8b7186888 100644
--- a/xmake/rules/csharp/config.lua
+++ b/xmake/rules/csharp/config.lua
@@ -19,16 +19,13 @@
--
-- imports
-import("core.project.config")
import("core.project.depend")
-import("modules.csproj_generator", {rootdir = os.scriptdir()})
+import("modules.csproj_generator", {rootdir = os.scriptdir(), alias = "generate_csproj"})
function main(target)
-- compute csproj path
- local targetkey = target:fullname():replace("::", path.sep())
- local csprojdir = path.join(config.directory(), "rules", "csharp", targetkey, target:plat(), target:arch())
- local csprojfile = path.join(csprojdir, target:name() .. ".csproj")
+ local csprojfile = path.join(target:autogendir(), "rules", "csharp", target:name() .. ".csproj")
local dependfile = target:dependfile(csprojfile)
-- collect source files and dep csproj paths as depend values
@@ -43,7 +40,7 @@ function main(target)
-- generate csproj incrementally
depend.on_changed(function ()
- csproj_generator(target, csprojfile)
+ generate_csproj(target, csprojfile)
end, {dependfile = dependfile, files = sourcefiles, values = depcsproj})
target:data_set("csharp.csproj", csprojfile)
diff --git a/xmake/rules/csharp/modules/csproj_generator.lua b/xmake/rules/csharp/modules/csproj_generator.lua
index 8972b5550..6ee9cf152 100644
--- a/xmake/rules/csharp/modules/csproj_generator.lua
+++ b/xmake/rules/csharp/modules/csproj_generator.lua
@@ -37,7 +37,7 @@ function _format_attributes(attrs)
return ""
end
local keys = {}
- for key, value in pairs(attrs) do
+ for key, value in table.orderpairs(attrs) do
if value ~= nil and value ~= "" then
table.insert(keys, key)
end
@@ -132,7 +132,7 @@ function _normalize_item_entry(item, default_xml)
local attrs = item.attrs
if type(attrs) ~= "table" then
attrs = {}
- for key, value in pairs(item) do
+ for key, value in table.orderpairs(item) do
if type(key) == "string" and key ~= "xml" and key ~= "value" and key ~= "attrs" then
attrs[key] = value
end
@@ -214,15 +214,8 @@ function _add_custom_properties_from_item(entries, item)
_add_custom_property(entries, tostring(item.name), item.value)
return
end
- local keys = {}
- for k in pairs(item) do
- if type(k) == "string" then
- table.insert(keys, k)
- end
- end
- table.sort(keys)
- for _, key in ipairs(keys) do
- _add_custom_property(entries, key, item[key])
+ for k, v in table.orderpairs(item) do
+ _add_custom_property(entries, k, v)
end
end
@@ -234,37 +227,37 @@ function _collect_custom_property_entries(target)
return entries
end
-function _render_property_group(lines, entries)
+function _render_property_group(file, entries)
if #entries == 0 then
return
end
- table.insert(lines, " ")
+ file:print(" ")
for _, entry in ipairs(entries) do
- table.insert(lines, string.format(" <%s>%s%s>", entry.xml, _xml_escape(entry.value), entry.xml))
+ file:print(" <%s>%s%s>", entry.xml, _xml_escape(entry.value), entry.xml)
end
- table.insert(lines, " ")
+ file:print(" ")
end
-function _render_item_groups(lines, item_groups)
+function _render_item_groups(file, item_groups)
for _, group in ipairs(item_groups) do
if #group.items > 0 then
- table.insert(lines, " ")
+ file:print(" ")
for _, item in ipairs(group.items) do
local attrs = _format_attributes(item.attrs)
if item.value ~= nil and item.value ~= "" then
- table.insert(lines, string.format(" <%s%s>%s%s>", item.xml, attrs, _xml_escape(item.value), item.xml))
+ file:print(" <%s%s>%s%s>", item.xml, attrs, _xml_escape(item.value), item.xml)
else
- table.insert(lines, string.format(" <%s%s />", item.xml, attrs))
+ file:print(" <%s%s />", item.xml, attrs)
end
end
- table.insert(lines, " ")
+ file:print(" ")
end
end
end
function main(target, csprojfile, opt)
opt = opt or {}
-
+
local csprojdir = path.directory(csprojfile)
local context = {
target = target,
@@ -282,21 +275,16 @@ function main(target, csprojfile, opt)
table.join2(property_entries, custom_property_entries)
local item_groups = _collect_item_groups(context, item_registry_entries)
- local lines = {}
-
- table.insert(lines, string.format("", _format_attributes(project_attributes)))
- _render_property_group(lines, property_entries)
- _render_item_groups(lines, item_groups)
- table.insert(lines, "")
- local content = table.concat(lines, "\n") .. "\n"
+ local tmpfile = os.tmpfile() .. ".csproj"
+ local file = io.open(tmpfile, "w")
+ file:print("", _format_attributes(project_attributes))
+ _render_property_group(file, property_entries)
+ _render_item_groups(file, item_groups)
+ file:print("")
+ file:close()
os.mkdir(csprojdir)
- local oldcontent = nil
- if os.isfile(csprojfile) then
- oldcontent = io.readfile(csprojfile)
- end
- if oldcontent ~= content then
- io.writefile(csprojfile, content)
- end
+ os.cp(tmpfile, csprojfile, {copy_if_different = true})
+ os.rm(tmpfile)
end
diff --git a/xmake/rules/csharp/modules/itemgroups.lua b/xmake/rules/csharp/modules/itemgroups.lua
index e9367f96f..785858024 100644
--- a/xmake/rules/csharp/modules/itemgroups.lua
+++ b/xmake/rules/csharp/modules/itemgroups.lua
@@ -100,7 +100,7 @@ function _collect_nuget_references(context)
end
local references = {}
- for pkgname, version in pairs(versions) do
+ for pkgname, version in table.orderpairs(versions) do
table.insert(references, {name = pkgname, version = version or nil})
end
table.sort(references, function (a, b) return a.name < b.name end)
diff --git a/xmake/rules/csharp/modules/properties.lua b/xmake/rules/csharp/modules/properties.lua
index 10606b357..6d392dbd8 100644
--- a/xmake/rules/csharp/modules/properties.lua
+++ b/xmake/rules/csharp/modules/properties.lua
@@ -22,8 +22,6 @@ function _has_target_frameworks(context)
return #table.wrap(context.target:values("csharp.target_frameworks")) > 0
end
-local _default_target_framework_cached = {}
-
function _first(value)
if type(value) == "table" then
return value[1]
@@ -39,7 +37,8 @@ end
function _get_default_target_framework(context)
local dotnet = _first(context.target:get("toolset.cs")) or "dotnet"
dotnet = tostring(dotnet)
- local cached = _default_target_framework_cached[dotnet]
+ _g.default_target_frameworks = _g.default_target_frameworks or {}
+ local cached = _g.default_target_frameworks[dotnet]
if cached ~= nil then
return cached
end
@@ -66,7 +65,7 @@ function _get_default_target_framework(context)
end
local target_framework = major and ("net" .. tostring(major) .. ".0") or "net8.0"
- _default_target_framework_cached[dotnet] = target_framework
+ _g.default_target_frameworks[dotnet] = target_framework
return target_framework
end
--
cgit v1.3.1
From f575c37eefe974965452e29ec19752f62236685a Mon Sep 17 00:00:00 2001
From: ruki
Date: Tue, 17 Mar 2026 22:39:04 +0800
Subject: add csharp rule comments
---
xmake/rules/csharp/modules/csproj_generator.lua | 16 +++++
xmake/rules/csharp/modules/itemgroups.lua | 6 ++
xmake/rules/csharp/modules/properties.lua | 10 ++-
xmake/rules/csharp/xmake.lua | 93 +++++++++++++++++++++++++
4 files changed, 124 insertions(+), 1 deletion(-)
diff --git a/xmake/rules/csharp/modules/csproj_generator.lua b/xmake/rules/csharp/modules/csproj_generator.lua
index 6ee9cf152..8891c6c1e 100644
--- a/xmake/rules/csharp/modules/csproj_generator.lua
+++ b/xmake/rules/csharp/modules/csproj_generator.lua
@@ -22,6 +22,7 @@
import("properties", {alias = "csharp_properties"})
import("itemgroups", {alias = "csharp_itemgroups"})
+-- escape special xml characters
function _xml_escape(value)
value = tostring(value or "")
value = value:gsub("&", "&")
@@ -32,6 +33,7 @@ function _xml_escape(value)
return value
end
+-- format key-value pairs as xml attributes string, e.g. ` Sdk="Microsoft.NET.Sdk"`
function _format_attributes(attrs)
if type(attrs) ~= "table" then
return ""
@@ -53,6 +55,7 @@ function _format_attributes(attrs)
return table.concat(chunks)
end
+-- get single csharp value from target:values(), with default fallback
function _get_csharp_value(target, name, defaultval)
local val = target:values(name)
if type(val) == "table" then
@@ -64,6 +67,7 @@ function _get_csharp_value(target, name, defaultval)
return val
end
+-- resolve a registry entry value, supports custom resolve function, list type and single value
function _resolve_registry_value(entry, target, context)
if entry.resolve then
return entry.resolve(context)
@@ -89,6 +93,7 @@ function _resolve_registry_value(entry, target, context)
return _get_csharp_value(target, entry.lua_key, entry.default)
end
+-- collect element attributes, e.g. Sdk="Microsoft.NET.Sdk"
function _collect_project_attributes(target, context, registry_entries)
local attrs = {}
for _, entry in ipairs(registry_entries) do
@@ -104,6 +109,7 @@ function _collect_project_attributes(target, context, registry_entries)
return attrs
end
+-- collect entries from registered csharp.* properties
function _collect_property_entries(target, context, registry_entries)
local entries = {}
for _, entry in ipairs(registry_entries) do
@@ -119,6 +125,7 @@ function _collect_property_entries(target, context, registry_entries)
return entries
end
+-- normalize item entry to {xml, attrs, value} format
function _normalize_item_entry(item, default_xml)
if type(item) == "string" then
return {xml = default_xml, attrs = {Include = item}}
@@ -141,6 +148,7 @@ function _normalize_item_entry(item, default_xml)
return {xml = xml, attrs = attrs, value = item.value}
end
+-- collect entries (Compile, ProjectReference, PackageReference, ..)
function _collect_item_groups(context, registry_entries)
local groups = {}
local groupmap = {}
@@ -168,10 +176,12 @@ function _collect_item_groups(context, registry_entries)
return groups
end
+-- check if a property name is valid for xml element
function _is_valid_property_name(name)
return type(name) == "string" and name:match("^[A-Za-z_][A-Za-z0-9_.-]*$") ~= nil
end
+-- add a custom property entry, supports string and table(semicolon-joined) values
function _add_custom_property(entries, name, value)
if not _is_valid_property_name(name) then
return
@@ -199,6 +209,8 @@ function _add_custom_property(entries, name, value)
table.insert(entries, {xml = name, value = value})
end
+-- parse custom property from csharp.properties value item
+-- supports string format "Name=Value" and table format {name = .., value = ..}
function _add_custom_properties_from_item(entries, item)
if type(item) == "string" then
local name, value = item:match("^%s*([^=]+)%s*=(.*)$")
@@ -219,6 +231,7 @@ function _add_custom_properties_from_item(entries, item)
end
end
+-- collect custom properties from target:values("csharp.properties")
function _collect_custom_property_entries(target)
local entries = {}
for _, item in ipairs(table.wrap(target:values("csharp.properties"))) do
@@ -227,6 +240,7 @@ function _collect_custom_property_entries(target)
return entries
end
+-- render section to file
function _render_property_group(file, entries)
if #entries == 0 then
return
@@ -238,6 +252,7 @@ function _render_property_group(file, entries)
file:print(" ")
end
+-- render sections to file
function _render_item_groups(file, item_groups)
for _, group in ipairs(item_groups) do
if #group.items > 0 then
@@ -255,6 +270,7 @@ function _render_item_groups(file, item_groups)
end
end
+-- generate .csproj file for the target, write to tmpfile first then copy if different
function main(target, csprojfile, opt)
opt = opt or {}
diff --git a/xmake/rules/csharp/modules/itemgroups.lua b/xmake/rules/csharp/modules/itemgroups.lua
index 785858024..6e939c6fd 100644
--- a/xmake/rules/csharp/modules/itemgroups.lua
+++ b/xmake/rules/csharp/modules/itemgroups.lua
@@ -18,6 +18,7 @@
-- @file itemsgroups.lua
--
+-- normalize path to relative and use forward slashes
function _normalize_relative(fromdir, targetpath)
local relpath = path.relative(targetpath, fromdir) or targetpath
if os.host() == "windows" then
@@ -26,6 +27,7 @@ function _normalize_relative(fromdir, targetpath)
return relpath
end
+-- collect .cs source files as relative paths to csprojdir
function _collect_cs_sourcefiles(context)
local csfiles = {}
for _, sourcefile in ipairs(context.target:sourcefiles()) do
@@ -38,6 +40,7 @@ function _collect_cs_sourcefiles(context)
return table.unique(csfiles)
end
+-- collect ProjectReference paths from dependency targets
function _collect_project_references(context)
local references = {}
for _, dep in ipairs(context.target:orderdeps()) do
@@ -50,6 +53,7 @@ function _collect_project_references(context)
return table.unique(references)
end
+-- extract nuget package name and version from package require string
function _get_nuget_info(pkg)
local requirestr = pkg:requirestr() or ""
local splitinfo = requirestr:trim():split("%s+")
@@ -84,6 +88,7 @@ function _get_nuget_info(pkg)
return pkgname, version
end
+-- collect PackageReference entries from nuget packages
function _collect_nuget_references(context)
local versions = {}
for _, pkg in ipairs(context.target:orderpkgs()) do
@@ -107,6 +112,7 @@ function _collect_nuget_references(context)
return references
end
+-- register all item group entries (Compile, ProjectReference, PackageReference)
function main()
local entries = {}
local function register(entry)
diff --git a/xmake/rules/csharp/modules/properties.lua b/xmake/rules/csharp/modules/properties.lua
index 6d392dbd8..eabebd006 100644
--- a/xmake/rules/csharp/modules/properties.lua
+++ b/xmake/rules/csharp/modules/properties.lua
@@ -18,10 +18,12 @@
-- @file properties.lua
--
+-- check if target has multi-target frameworks set
function _has_target_frameworks(context)
return #table.wrap(context.target:values("csharp.target_frameworks")) > 0
end
+-- get the first element if value is a table
function _first(value)
if type(value) == "table" then
return value[1]
@@ -29,11 +31,12 @@ function _first(value)
return value
end
+-- get single value from target:values()
function _get_target_value(target, name)
return _first(target:values(name))
end
-
+-- detect default target framework from dotnet sdk version, e.g. "net8.0"
function _get_default_target_framework(context)
local dotnet = _first(context.target:get("toolset.cs")) or "dotnet"
dotnet = tostring(dotnet)
@@ -69,6 +72,7 @@ function _get_default_target_framework(context)
return target_framework
end
+-- resolve target framework from user config or auto-detect from dotnet sdk
function _resolve_target_framework(context)
local target_framework = _get_target_value(context.target, "csharp.target_framework")
if target_framework ~= nil and #tostring(target_framework) > 0 then
@@ -77,6 +81,7 @@ function _resolve_target_framework(context)
return _get_default_target_framework(context)
end
+-- resolve assembly name from target basename
function _resolve_assembly_name(context)
local basename = context.target:basename()
if basename ~= nil and #tostring(basename) > 0 then
@@ -85,6 +90,7 @@ function _resolve_assembly_name(context)
end
+-- register a single-value csharp.* property entry
function _register_property(register, suffix, xml, default, extra)
local entry = table.join({
kind = "property",
@@ -95,6 +101,7 @@ function _register_property(register, suffix, xml, default, extra)
register(entry)
end
+-- register a list-value csharp.* property entry (semicolon-joined)
function _register_list_property(register, suffix, xml, extra)
local entry = table.join({
kind = "property",
@@ -106,6 +113,7 @@ function _register_list_property(register, suffix, xml, extra)
register(entry)
end
+-- register all csharp property and project attribute entries for csproj generation
function main()
local entries = {}
function register(entry)
diff --git a/xmake/rules/csharp/xmake.lua b/xmake/rules/csharp/xmake.lua
index f13003520..251a71ecd 100644
--- a/xmake/rules/csharp/xmake.lua
+++ b/xmake/rules/csharp/xmake.lua
@@ -18,6 +18,99 @@
-- @file xmake.lua
--
+-- User Configs:
+--
+-- The following `csharp.*` values can be set via `set_values()` in xmake.lua
+-- to customize the auto-generated .csproj file.
+--
+-- e.g.
+-- target("example")
+-- add_rules("csharp")
+-- add_files("src/*.cs")
+-- set_values("csharp.target_framework", "net8.0")
+-- set_values("csharp.nullable", "disable")
+-- set_values("csharp.allow_unsafe_blocks", "true")
+--
+-- Project:
+-- csharp.sdk - Project Sdk (default: Microsoft.NET.Sdk)
+--
+-- General:
+-- csharp.target_framework - e.g. "net8.0", auto-detected from dotnet sdk if not set
+-- csharp.target_frameworks - multi-target, e.g. {"net8.0", "net9.0"} (list, semicolon-joined)
+-- csharp.implicit_usings - ImplicitUsings (default: "enable")
+-- csharp.nullable - Nullable (default: "enable")
+-- csharp.lang_version - LangVersion, e.g. "12.0", "latest"
+-- csharp.root_namespace - RootNamespace
+-- csharp.enable_default_compile_items - EnableDefaultCompileItems (default: "false")
+-- csharp.enable_default_embedded_resource_items - EnableDefaultEmbeddedResourceItems
+-- csharp.enable_default_none_items - EnableDefaultNoneItems
+--
+-- Build:
+-- csharp.generate_assembly_info - GenerateAssemblyInfo
+-- csharp.deterministic - Deterministic
+-- csharp.prefer_32bit - Prefer32Bit
+-- csharp.allow_unsafe_blocks - AllowUnsafeBlocks
+-- csharp.check_for_overflow_underflow - CheckForOverflowUnderflow
+--
+-- Analysis:
+-- csharp.analysis_level - AnalysisLevel
+-- csharp.enable_net_analyzers - EnableNETAnalyzers
+-- csharp.enforce_code_style_in_build - EnforceCodeStyleInBuild
+-- csharp.warnings_as_errors - WarningsAsErrors (list)
+-- csharp.warnings_not_as_errors - WarningsNotAsErrors (list)
+-- csharp.error_log - ErrorLog
+-- csharp.generate_documentation_file - GenerateDocumentationFile
+-- csharp.documentation_file - DocumentationFile
+--
+-- Publish/Runtime:
+-- csharp.runtime_identifier - RuntimeIdentifier, e.g. "win-x64"
+-- csharp.runtime_identifiers - RuntimeIdentifiers (list)
+-- csharp.self_contained - SelfContained
+-- csharp.use_app_host - UseAppHost
+-- csharp.roll_forward - RollForward
+-- csharp.publish_single_file - PublishSingleFile
+-- csharp.publish_trimmed - PublishTrimmed
+-- csharp.trim_mode - TrimMode
+-- csharp.publish_ready_to_run - PublishReadyToRun
+-- csharp.invariant_globalization - InvariantGlobalization
+-- csharp.include_native_libraries_for_self_extract - IncludeNativeLibrariesForSelfExtract
+-- csharp.enable_compression_in_single_file - EnableCompressionInSingleFile
+-- csharp.publish_aot - PublishAot
+-- csharp.strip_symbols - StripSymbols
+-- csharp.enable_trim_analyzer - EnableTrimAnalyzer
+-- csharp.json_serializer_is_reflection_enabled_by_default - JsonSerializerIsReflectionEnabledByDefault
+-- csharp.satellite_resource_languages - SatelliteResourceLanguages (list)
+--
+-- Package Info:
+-- csharp.version - Version
+-- csharp.assembly_version - AssemblyVersion
+-- csharp.file_version - FileVersion
+-- csharp.informational_version - InformationalVersion
+-- csharp.package_id - PackageId
+-- csharp.authors - Authors
+-- csharp.company - Company
+-- csharp.product - Product
+-- csharp.description - Description
+-- csharp.copyright - Copyright
+-- csharp.repository_url - RepositoryUrl
+-- csharp.repository_type - RepositoryType
+-- csharp.package_license_expression - PackageLicenseExpression
+-- csharp.package_project_url - PackageProjectUrl
+-- csharp.neutral_language - NeutralLanguage
+-- csharp.enable_preview_features - EnablePreviewFeatures
+--
+-- Output:
+-- csharp.generate_runtime_configuration_files - GenerateRuntimeConfigurationFiles
+-- csharp.copy_local_lock_file_assemblies - CopyLocalLockFileAssemblies
+-- csharp.append_target_framework_to_output_path - AppendTargetFrameworkToOutputPath (default: "false")
+-- csharp.append_runtime_identifier_to_output_path - AppendRuntimeIdentifierToOutputPath (default: "false")
+-- csharp.produce_reference_assembly - ProduceReferenceAssembly
+-- csharp.disable_implicit_framework_references - DisableImplicitFrameworkReferences
+-- csharp.generate_target_framework_attribute - GenerateTargetFrameworkAttribute
+--
+-- Custom Properties (for arbitrary csproj properties not listed above):
+-- csharp.properties - e.g. set_values("csharp.properties", "MyProp=value")
+--
rule("csharp.build")
set_sourcekinds("cs")
on_config("config")
--
cgit v1.3.1
From 835c259574e8a071dcc5a290afc89272a55e4240 Mon Sep 17 00:00:00 2001
From: ruki
Date: Tue, 17 Mar 2026 22:40:12 +0800
Subject: simplify csharp configs
---
xmake/rules/csharp/modules/csproj_generator.lua | 63 +++----------------------
xmake/rules/csharp/xmake.lua | 3 +-
2 files changed, 9 insertions(+), 57 deletions(-)
diff --git a/xmake/rules/csharp/modules/csproj_generator.lua b/xmake/rules/csharp/modules/csproj_generator.lua
index 8891c6c1e..ae2f5a777 100644
--- a/xmake/rules/csharp/modules/csproj_generator.lua
+++ b/xmake/rules/csharp/modules/csproj_generator.lua
@@ -176,66 +176,15 @@ function _collect_item_groups(context, registry_entries)
return groups
end
--- check if a property name is valid for xml element
-function _is_valid_property_name(name)
- return type(name) == "string" and name:match("^[A-Za-z_][A-Za-z0-9_.-]*$") ~= nil
-end
-
--- add a custom property entry, supports string and table(semicolon-joined) values
-function _add_custom_property(entries, name, value)
- if not _is_valid_property_name(name) then
- return
- end
- if value == nil then
- return
- end
- if type(value) == "table" then
- local values = {}
- for _, v in ipairs(value) do
- if v ~= nil and v ~= "" then
- table.insert(values, tostring(v))
- end
- end
- if #values == 0 then
- return
- end
- value = table.concat(values, ";")
- else
- value = tostring(value)
- end
- if #value == 0 then
- return
- end
- table.insert(entries, {xml = name, value = value})
-end
-
--- parse custom property from csharp.properties value item
--- supports string format "Name=Value" and table format {name = .., value = ..}
-function _add_custom_properties_from_item(entries, item)
- if type(item) == "string" then
- local name, value = item:match("^%s*([^=]+)%s*=(.*)$")
- if name then
- _add_custom_property(entries, name:trim(), value)
- end
- return
- end
- if type(item) ~= "table" then
- return
- end
- if item.name then
- _add_custom_property(entries, tostring(item.name), item.value)
- return
- end
- for k, v in table.orderpairs(item) do
- _add_custom_property(entries, k, v)
- end
-end
-
-- collect custom properties from target:values("csharp.properties")
+-- value format: "Name=Value", e.g. set_values("csharp.properties", "MyProp=value")
function _collect_custom_property_entries(target)
local entries = {}
for _, item in ipairs(table.wrap(target:values("csharp.properties"))) do
- _add_custom_properties_from_item(entries, item)
+ local name, value = tostring(item):match("^%s*([^=]+)%s*=(.*)$")
+ if name and #value > 0 then
+ table.insert(entries, {xml = name:trim(), value = value})
+ end
end
return entries
end
@@ -282,6 +231,7 @@ function main(target, csprojfile, opt)
opt = opt
}
+ -- collect project properties
local property_registry_entries = csharp_properties()
local item_registry_entries = csharp_itemgroups()
@@ -292,6 +242,7 @@ function main(target, csprojfile, opt)
local item_groups = _collect_item_groups(context, item_registry_entries)
+ -- generate csproj
local tmpfile = os.tmpfile() .. ".csproj"
local file = io.open(tmpfile, "w")
file:print("", _format_attributes(project_attributes))
diff --git a/xmake/rules/csharp/xmake.lua b/xmake/rules/csharp/xmake.lua
index 251a71ecd..ddd686bfc 100644
--- a/xmake/rules/csharp/xmake.lua
+++ b/xmake/rules/csharp/xmake.lua
@@ -109,7 +109,8 @@
-- csharp.generate_target_framework_attribute - GenerateTargetFrameworkAttribute
--
-- Custom Properties (for arbitrary csproj properties not listed above):
--- csharp.properties - e.g. set_values("csharp.properties", "MyProp=value")
+-- csharp.properties - add custom entries, format: "Name=Value"
+-- e.g. set_values("csharp.properties", "MyProp=value", "AnotherProp=value2")
--
rule("csharp.build")
set_sourcekinds("cs")
--
cgit v1.3.1
From ce2f02d4bccd0da6f8e7a12330ac2d56ab9b525f Mon Sep 17 00:00:00 2001
From: ruki
Date: Tue, 17 Mar 2026 22:42:00 +0800
Subject: rename csharp files
---
xmake/rules/csharp/modules/csproj_generator.lua | 8 +-
xmake/rules/csharp/modules/csproj_itemgroups.lua | 163 +++++++++++++++++++
xmake/rules/csharp/modules/csproj_properties.lua | 197 +++++++++++++++++++++++
xmake/rules/csharp/modules/itemgroups.lua | 166 -------------------
xmake/rules/csharp/modules/properties.lua | 197 -----------------------
5 files changed, 364 insertions(+), 367 deletions(-)
create mode 100644 xmake/rules/csharp/modules/csproj_itemgroups.lua
create mode 100644 xmake/rules/csharp/modules/csproj_properties.lua
delete mode 100644 xmake/rules/csharp/modules/itemgroups.lua
delete mode 100644 xmake/rules/csharp/modules/properties.lua
diff --git a/xmake/rules/csharp/modules/csproj_generator.lua b/xmake/rules/csharp/modules/csproj_generator.lua
index ae2f5a777..0755b41a0 100644
--- a/xmake/rules/csharp/modules/csproj_generator.lua
+++ b/xmake/rules/csharp/modules/csproj_generator.lua
@@ -19,8 +19,8 @@
--
-- imports
-import("properties", {alias = "csharp_properties"})
-import("itemgroups", {alias = "csharp_itemgroups"})
+import("csproj_properties")
+import("csproj_itemgroups")
-- escape special xml characters
function _xml_escape(value)
@@ -232,8 +232,8 @@ function main(target, csprojfile, opt)
}
-- collect project properties
- local property_registry_entries = csharp_properties()
- local item_registry_entries = csharp_itemgroups()
+ local property_registry_entries = csproj_properties()
+ local item_registry_entries = csproj_itemgroups()
local project_attributes = _collect_project_attributes(target, context, property_registry_entries)
local property_entries = _collect_property_entries(target, context, property_registry_entries)
diff --git a/xmake/rules/csharp/modules/csproj_itemgroups.lua b/xmake/rules/csharp/modules/csproj_itemgroups.lua
new file mode 100644
index 000000000..79f13b4fb
--- /dev/null
+++ b/xmake/rules/csharp/modules/csproj_itemgroups.lua
@@ -0,0 +1,163 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author JassJam
+-- @file csproj_itemsgroups.lua
+--
+
+-- normalize path to relative and use forward slashes
+function _normalize_relative(fromdir, targetpath)
+ local relpath = path.relative(targetpath, fromdir) or targetpath
+ return path.unix(relpath)
+end
+
+-- collect .cs source files as relative paths to csprojdir
+function _collect_cs_sourcefiles(context)
+ local csfiles = {}
+ for _, sourcefile in ipairs(context.target:sourcefiles()) do
+ if path.extension(sourcefile):lower() == ".cs" then
+ local sourceabs = path.is_absolute(sourcefile) and sourcefile or path.absolute(sourcefile, os.projectdir())
+ table.insert(csfiles, _normalize_relative(context.csprojdir, sourceabs))
+ end
+ end
+ table.sort(csfiles)
+ return table.unique(csfiles)
+end
+
+-- collect ProjectReference paths from dependency targets
+function _collect_project_references(context)
+ local references = {}
+ for _, dep in ipairs(context.target:orderdeps()) do
+ local depcsproj = dep:data("csharp.csproj")
+ if depcsproj then
+ table.insert(references, _normalize_relative(context.csprojdir, depcsproj))
+ end
+ end
+ table.sort(references)
+ return table.unique(references)
+end
+
+-- extract nuget package name and version from package require string
+function _get_nuget_info(pkg)
+ local requirestr = pkg:requirestr() or ""
+ local splitinfo = requirestr:trim():split("%s+")
+ if #splitinfo == 0 then
+ return nil
+ end
+
+ local pkgname = splitinfo[1]
+ if pkgname:find("::", 1, true) then
+ pkgname = pkgname:split("::", {plain = true})
+ pkgname = pkgname[#pkgname]
+ end
+ local pkgname_raw = pkgname:match("(.-)%[.*%]$")
+ if pkgname_raw and #pkgname_raw > 0 then
+ pkgname = pkgname_raw
+ end
+ if not pkgname or #pkgname == 0 then
+ return nil
+ end
+
+ local version
+ local versionobj = pkg:version()
+ if versionobj then
+ version = tostring(versionobj)
+ end
+ if not version and #splitinfo > 1 then
+ local require_version = table.concat(table.slice(splitinfo, 2), " ")
+ if require_version ~= "latest" then
+ version = require_version
+ end
+ end
+ return pkgname, version
+end
+
+-- collect PackageReference entries from nuget packages
+function _collect_nuget_references(context)
+ local versions = {}
+ for _, pkg in ipairs(context.target:orderpkgs()) do
+ local namespace = pkg:namespace()
+ local requirestr = pkg:requirestr() or ""
+ if namespace == "nuget" or requirestr:startswith("nuget::") then
+ local pkgname, version = _get_nuget_info(pkg)
+ if pkgname then
+ if version or versions[pkgname] == nil then
+ versions[pkgname] = version or false
+ end
+ end
+ end
+ end
+
+ local references = {}
+ for pkgname, version in table.orderpairs(versions) do
+ table.insert(references, {name = pkgname, version = version or nil})
+ end
+ table.sort(references, function (a, b) return a.name < b.name end)
+ return references
+end
+
+-- register all item group entries (Compile, ProjectReference, PackageReference)
+function main()
+ local entries = {}
+ local function register(entry)
+ table.insert(entries, entry)
+ end
+
+ register({
+ kind = "item",
+ group = "compile",
+ xml = "Compile",
+ resolve_items = function (context)
+ local items = {}
+ for _, sourcefile in ipairs(_collect_cs_sourcefiles(context)) do
+ table.insert(items, {attrs = {Include = sourcefile}})
+ end
+ return items
+ end
+ })
+
+ register({
+ kind = "item",
+ group = "project_reference",
+ xml = "ProjectReference",
+ resolve_items = function (context)
+ local items = {}
+ for _, reffile in ipairs(_collect_project_references(context)) do
+ table.insert(items, {attrs = {Include = reffile}})
+ end
+ return items
+ end
+ })
+
+ register({
+ kind = "item",
+ group = "package_reference",
+ xml = "PackageReference",
+ resolve_items = function (context)
+ local items = {}
+ for _, pkginfo in ipairs(_collect_nuget_references(context)) do
+ local attrs = {Include = pkginfo.name}
+ if pkginfo.version then
+ attrs.Version = pkginfo.version
+ end
+ table.insert(items, {attrs = attrs})
+ end
+ return items
+ end
+ })
+
+ return entries
+end
diff --git a/xmake/rules/csharp/modules/csproj_properties.lua b/xmake/rules/csharp/modules/csproj_properties.lua
new file mode 100644
index 000000000..324e67531
--- /dev/null
+++ b/xmake/rules/csharp/modules/csproj_properties.lua
@@ -0,0 +1,197 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author JassJam
+-- @file csproj_properties.lua
+--
+
+-- check if target has multi-target frameworks set
+function _has_target_frameworks(context)
+ return #table.wrap(context.target:values("csharp.target_frameworks")) > 0
+end
+
+-- get the first element if value is a table
+function _first(value)
+ if type(value) == "table" then
+ return value[1]
+ end
+ return value
+end
+
+-- get single value from target:values()
+function _get_target_value(target, name)
+ return _first(target:values(name))
+end
+
+-- detect default target framework from dotnet sdk version, e.g. "net8.0"
+function _get_default_target_framework(context)
+ local dotnet = _first(context.target:get("toolset.cs")) or "dotnet"
+ dotnet = tostring(dotnet)
+ _g.default_target_frameworks = _g.default_target_frameworks or {}
+ local cached = _g.default_target_frameworks[dotnet]
+ if cached ~= nil then
+ return cached
+ end
+
+ local major = nil
+ local sdks = try { function ()
+ return os.iorunv(dotnet, {"--list-sdks"})
+ end }
+ if sdks then
+ for line in sdks:gmatch("[^\r\n]+") do
+ local line_major = tonumber(line:match("^%s*(%d+)%.%d+%.%d+"))
+ if line_major and (not major or line_major > major) then
+ major = line_major
+ end
+ end
+ end
+ if not major then
+ local version = try { function ()
+ return os.iorunv(dotnet, {"--version"})
+ end }
+ if version then
+ major = tonumber(version:match("^%s*(%d+)"))
+ end
+ end
+
+ local target_framework = major and ("net" .. tostring(major) .. ".0") or "net8.0"
+ _g.default_target_frameworks[dotnet] = target_framework
+ return target_framework
+end
+
+-- resolve target framework from user config or auto-detect from dotnet sdk
+function _resolve_target_framework(context)
+ local target_framework = _get_target_value(context.target, "csharp.target_framework")
+ if target_framework ~= nil and #tostring(target_framework) > 0 then
+ return target_framework
+ end
+ return _get_default_target_framework(context)
+end
+
+-- resolve assembly name from target basename
+function _resolve_assembly_name(context)
+ local basename = context.target:basename()
+ if basename ~= nil and #tostring(basename) > 0 then
+ return basename
+ end
+end
+
+
+-- register a single-value csharp.* property entry
+function _register_property(register, suffix, xml, default, extra)
+ local entry = table.join({
+ kind = "property",
+ xml = xml,
+ lua_key = "csharp." .. suffix,
+ default = default
+ }, extra or {})
+ register(entry)
+end
+
+-- register a list-value csharp.* property entry (semicolon-joined)
+function _register_list_property(register, suffix, xml, extra)
+ local entry = table.join({
+ kind = "property",
+ xml = xml,
+ lua_key = "csharp." .. suffix,
+ value_type = "list",
+ sep = ";"
+ }, extra or {})
+ register(entry)
+end
+
+-- register all csharp property and project attribute entries for csproj generation
+function main()
+ local entries = {}
+ function register(entry)
+ table.insert(entries, entry)
+ end
+
+ register({kind = "project_attribute", attr = "Sdk", lua_key = "csharp.sdk", default = "Microsoft.NET.Sdk"})
+ register({kind = "property", xml = "OutputType", resolve = function (context)
+ return context.target:is_binary() and "Exe" or "Library"
+ end})
+ _register_list_property(register, "target_frameworks", "TargetFrameworks", {when = _has_target_frameworks})
+ register({kind = "property", xml = "TargetFramework", resolve = _resolve_target_framework, when = function (context)
+ return not _has_target_frameworks(context)
+ end})
+
+ _register_property(register, "implicit_usings", "ImplicitUsings", "enable")
+ _register_property(register, "nullable", "Nullable", "enable")
+ _register_property(register, "lang_version", "LangVersion")
+ _register_property(register, "enable_default_compile_items", "EnableDefaultCompileItems", "false")
+ _register_property(register, "enable_default_embedded_resource_items", "EnableDefaultEmbeddedResourceItems")
+ _register_property(register, "enable_default_none_items", "EnableDefaultNoneItems")
+ _register_property(register, "root_namespace", "RootNamespace")
+ register({kind = "property", xml = "AssemblyName", resolve = _resolve_assembly_name})
+ _register_property(register, "generate_assembly_info", "GenerateAssemblyInfo")
+ _register_property(register, "deterministic", "Deterministic")
+ _register_property(register, "prefer_32bit", "Prefer32Bit")
+ _register_property(register, "allow_unsafe_blocks", "AllowUnsafeBlocks")
+ _register_property(register, "check_for_overflow_underflow", "CheckForOverflowUnderflow")
+ _register_property(register, "analysis_level", "AnalysisLevel")
+ _register_property(register, "enable_net_analyzers", "EnableNETAnalyzers")
+ _register_property(register, "enforce_code_style_in_build", "EnforceCodeStyleInBuild")
+ _register_list_property(register, "warnings_as_errors", "WarningsAsErrors")
+ _register_list_property(register, "warnings_not_as_errors", "WarningsNotAsErrors")
+ _register_property(register, "error_log", "ErrorLog")
+ _register_property(register, "generate_documentation_file", "GenerateDocumentationFile")
+ _register_property(register, "documentation_file", "DocumentationFile")
+
+ _register_property(register, "runtime_identifier", "RuntimeIdentifier")
+ _register_list_property(register, "runtime_identifiers", "RuntimeIdentifiers")
+ _register_property(register, "self_contained", "SelfContained")
+ _register_property(register, "use_app_host", "UseAppHost")
+ _register_property(register, "roll_forward", "RollForward")
+ _register_property(register, "publish_single_file", "PublishSingleFile")
+ _register_property(register, "publish_trimmed", "PublishTrimmed")
+ _register_property(register, "trim_mode", "TrimMode")
+ _register_property(register, "publish_ready_to_run", "PublishReadyToRun")
+ _register_property(register, "invariant_globalization", "InvariantGlobalization")
+ _register_property(register, "include_native_libraries_for_self_extract", "IncludeNativeLibrariesForSelfExtract")
+ _register_property(register, "enable_compression_in_single_file", "EnableCompressionInSingleFile")
+ _register_property(register, "publish_aot", "PublishAot")
+ _register_property(register, "strip_symbols", "StripSymbols")
+ _register_property(register, "enable_trim_analyzer", "EnableTrimAnalyzer")
+ _register_property(register, "json_serializer_is_reflection_enabled_by_default", "JsonSerializerIsReflectionEnabledByDefault")
+ _register_list_property(register, "satellite_resource_languages", "SatelliteResourceLanguages")
+
+ _register_property(register, "version", "Version")
+ _register_property(register, "assembly_version", "AssemblyVersion")
+ _register_property(register, "file_version", "FileVersion")
+ _register_property(register, "informational_version", "InformationalVersion")
+ _register_property(register, "package_id", "PackageId")
+ _register_property(register, "authors", "Authors")
+ _register_property(register, "company", "Company")
+ _register_property(register, "product", "Product")
+ _register_property(register, "description", "Description")
+ _register_property(register, "copyright", "Copyright")
+ _register_property(register, "repository_url", "RepositoryUrl")
+ _register_property(register, "repository_type", "RepositoryType")
+ _register_property(register, "package_license_expression", "PackageLicenseExpression")
+ _register_property(register, "package_project_url", "PackageProjectUrl")
+ _register_property(register, "neutral_language", "NeutralLanguage")
+ _register_property(register, "enable_preview_features", "EnablePreviewFeatures")
+
+ _register_property(register, "generate_runtime_configuration_files", "GenerateRuntimeConfigurationFiles")
+ _register_property(register, "copy_local_lock_file_assemblies", "CopyLocalLockFileAssemblies")
+ _register_property(register, "append_target_framework_to_output_path", "AppendTargetFrameworkToOutputPath", "false")
+ _register_property(register, "append_runtime_identifier_to_output_path", "AppendRuntimeIdentifierToOutputPath", "false")
+ _register_property(register, "produce_reference_assembly", "ProduceReferenceAssembly")
+ _register_property(register, "disable_implicit_framework_references", "DisableImplicitFrameworkReferences")
+ _register_property(register, "generate_target_framework_attribute", "GenerateTargetFrameworkAttribute")
+ return entries
+end
diff --git a/xmake/rules/csharp/modules/itemgroups.lua b/xmake/rules/csharp/modules/itemgroups.lua
deleted file mode 100644
index 6e939c6fd..000000000
--- a/xmake/rules/csharp/modules/itemgroups.lua
+++ /dev/null
@@ -1,166 +0,0 @@
---!A cross-platform build utility based on Lua
---
--- Licensed under the Apache License, Version 2.0 (the "License");
--- you may not use this file except in compliance with the License.
--- You may obtain a copy of the License at
---
--- http://www.apache.org/licenses/LICENSE-2.0
---
--- Unless required by applicable law or agreed to in writing, software
--- distributed under the License is distributed on an "AS IS" BASIS,
--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--- See the License for the specific language governing permissions and
--- limitations under the License.
---
--- Copyright (C) 2015-present, Xmake Open Source Community.
---
--- @author JassJam
--- @file itemsgroups.lua
---
-
--- normalize path to relative and use forward slashes
-function _normalize_relative(fromdir, targetpath)
- local relpath = path.relative(targetpath, fromdir) or targetpath
- if os.host() == "windows" then
- relpath = relpath:gsub("\\", "/")
- end
- return relpath
-end
-
--- collect .cs source files as relative paths to csprojdir
-function _collect_cs_sourcefiles(context)
- local csfiles = {}
- for _, sourcefile in ipairs(context.target:sourcefiles()) do
- if path.extension(sourcefile):lower() == ".cs" then
- local sourceabs = path.is_absolute(sourcefile) and sourcefile or path.absolute(sourcefile, os.projectdir())
- table.insert(csfiles, _normalize_relative(context.csprojdir, sourceabs))
- end
- end
- table.sort(csfiles)
- return table.unique(csfiles)
-end
-
--- collect ProjectReference paths from dependency targets
-function _collect_project_references(context)
- local references = {}
- for _, dep in ipairs(context.target:orderdeps()) do
- local depcsproj = dep:data("csharp.csproj")
- if depcsproj then
- table.insert(references, _normalize_relative(context.csprojdir, depcsproj))
- end
- end
- table.sort(references)
- return table.unique(references)
-end
-
--- extract nuget package name and version from package require string
-function _get_nuget_info(pkg)
- local requirestr = pkg:requirestr() or ""
- local splitinfo = requirestr:trim():split("%s+")
- if #splitinfo == 0 then
- return nil
- end
-
- local pkgname = splitinfo[1]
- if pkgname:find("::", 1, true) then
- pkgname = pkgname:split("::", {plain = true})
- pkgname = pkgname[#pkgname]
- end
- local pkgname_raw = pkgname:match("(.-)%[.*%]$")
- if pkgname_raw and #pkgname_raw > 0 then
- pkgname = pkgname_raw
- end
- if not pkgname or #pkgname == 0 then
- return nil
- end
-
- local version
- local versionobj = pkg:version()
- if versionobj then
- version = tostring(versionobj)
- end
- if not version and #splitinfo > 1 then
- local require_version = table.concat(table.slice(splitinfo, 2), " ")
- if require_version ~= "latest" then
- version = require_version
- end
- end
- return pkgname, version
-end
-
--- collect PackageReference entries from nuget packages
-function _collect_nuget_references(context)
- local versions = {}
- for _, pkg in ipairs(context.target:orderpkgs()) do
- local namespace = pkg:namespace()
- local requirestr = pkg:requirestr() or ""
- if namespace == "nuget" or requirestr:startswith("nuget::") then
- local pkgname, version = _get_nuget_info(pkg)
- if pkgname then
- if version or versions[pkgname] == nil then
- versions[pkgname] = version or false
- end
- end
- end
- end
-
- local references = {}
- for pkgname, version in table.orderpairs(versions) do
- table.insert(references, {name = pkgname, version = version or nil})
- end
- table.sort(references, function (a, b) return a.name < b.name end)
- return references
-end
-
--- register all item group entries (Compile, ProjectReference, PackageReference)
-function main()
- local entries = {}
- local function register(entry)
- table.insert(entries, entry)
- end
-
- register({
- kind = "item",
- group = "compile",
- xml = "Compile",
- resolve_items = function (context)
- local items = {}
- for _, sourcefile in ipairs(_collect_cs_sourcefiles(context)) do
- table.insert(items, {attrs = {Include = sourcefile}})
- end
- return items
- end
- })
-
- register({
- kind = "item",
- group = "project_reference",
- xml = "ProjectReference",
- resolve_items = function (context)
- local items = {}
- for _, reffile in ipairs(_collect_project_references(context)) do
- table.insert(items, {attrs = {Include = reffile}})
- end
- return items
- end
- })
-
- register({
- kind = "item",
- group = "package_reference",
- xml = "PackageReference",
- resolve_items = function (context)
- local items = {}
- for _, pkginfo in ipairs(_collect_nuget_references(context)) do
- local attrs = {Include = pkginfo.name}
- if pkginfo.version then
- attrs.Version = pkginfo.version
- end
- table.insert(items, {attrs = attrs})
- end
- return items
- end
- })
-
- return entries
-end
diff --git a/xmake/rules/csharp/modules/properties.lua b/xmake/rules/csharp/modules/properties.lua
deleted file mode 100644
index eabebd006..000000000
--- a/xmake/rules/csharp/modules/properties.lua
+++ /dev/null
@@ -1,197 +0,0 @@
---!A cross-platform build utility based on Lua
---
--- Licensed under the Apache License, Version 2.0 (the "License");
--- you may not use this file except in compliance with the License.
--- You may obtain a copy of the License at
---
--- http://www.apache.org/licenses/LICENSE-2.0
---
--- Unless required by applicable law or agreed to in writing, software
--- distributed under the License is distributed on an "AS IS" BASIS,
--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--- See the License for the specific language governing permissions and
--- limitations under the License.
---
--- Copyright (C) 2015-present, Xmake Open Source Community.
---
--- @author JassJam
--- @file properties.lua
---
-
--- check if target has multi-target frameworks set
-function _has_target_frameworks(context)
- return #table.wrap(context.target:values("csharp.target_frameworks")) > 0
-end
-
--- get the first element if value is a table
-function _first(value)
- if type(value) == "table" then
- return value[1]
- end
- return value
-end
-
--- get single value from target:values()
-function _get_target_value(target, name)
- return _first(target:values(name))
-end
-
--- detect default target framework from dotnet sdk version, e.g. "net8.0"
-function _get_default_target_framework(context)
- local dotnet = _first(context.target:get("toolset.cs")) or "dotnet"
- dotnet = tostring(dotnet)
- _g.default_target_frameworks = _g.default_target_frameworks or {}
- local cached = _g.default_target_frameworks[dotnet]
- if cached ~= nil then
- return cached
- end
-
- local major = nil
- local sdks = try { function ()
- return os.iorunv(dotnet, {"--list-sdks"})
- end }
- if sdks then
- for line in sdks:gmatch("[^\r\n]+") do
- local line_major = tonumber(line:match("^%s*(%d+)%.%d+%.%d+"))
- if line_major and (not major or line_major > major) then
- major = line_major
- end
- end
- end
- if not major then
- local version = try { function ()
- return os.iorunv(dotnet, {"--version"})
- end }
- if version then
- major = tonumber(version:match("^%s*(%d+)"))
- end
- end
-
- local target_framework = major and ("net" .. tostring(major) .. ".0") or "net8.0"
- _g.default_target_frameworks[dotnet] = target_framework
- return target_framework
-end
-
--- resolve target framework from user config or auto-detect from dotnet sdk
-function _resolve_target_framework(context)
- local target_framework = _get_target_value(context.target, "csharp.target_framework")
- if target_framework ~= nil and #tostring(target_framework) > 0 then
- return target_framework
- end
- return _get_default_target_framework(context)
-end
-
--- resolve assembly name from target basename
-function _resolve_assembly_name(context)
- local basename = context.target:basename()
- if basename ~= nil and #tostring(basename) > 0 then
- return basename
- end
-end
-
-
--- register a single-value csharp.* property entry
-function _register_property(register, suffix, xml, default, extra)
- local entry = table.join({
- kind = "property",
- xml = xml,
- lua_key = "csharp." .. suffix,
- default = default
- }, extra or {})
- register(entry)
-end
-
--- register a list-value csharp.* property entry (semicolon-joined)
-function _register_list_property(register, suffix, xml, extra)
- local entry = table.join({
- kind = "property",
- xml = xml,
- lua_key = "csharp." .. suffix,
- value_type = "list",
- sep = ";"
- }, extra or {})
- register(entry)
-end
-
--- register all csharp property and project attribute entries for csproj generation
-function main()
- local entries = {}
- function register(entry)
- table.insert(entries, entry)
- end
-
- register({kind = "project_attribute", attr = "Sdk", lua_key = "csharp.sdk", default = "Microsoft.NET.Sdk"})
- register({kind = "property", xml = "OutputType", resolve = function (context)
- return context.target:is_binary() and "Exe" or "Library"
- end})
- _register_list_property(register, "target_frameworks", "TargetFrameworks", {when = _has_target_frameworks})
- register({kind = "property", xml = "TargetFramework", resolve = _resolve_target_framework, when = function (context)
- return not _has_target_frameworks(context)
- end})
-
- _register_property(register, "implicit_usings", "ImplicitUsings", "enable")
- _register_property(register, "nullable", "Nullable", "enable")
- _register_property(register, "lang_version", "LangVersion")
- _register_property(register, "enable_default_compile_items", "EnableDefaultCompileItems", "false")
- _register_property(register, "enable_default_embedded_resource_items", "EnableDefaultEmbeddedResourceItems")
- _register_property(register, "enable_default_none_items", "EnableDefaultNoneItems")
- _register_property(register, "root_namespace", "RootNamespace")
- register({kind = "property", xml = "AssemblyName", resolve = _resolve_assembly_name})
- _register_property(register, "generate_assembly_info", "GenerateAssemblyInfo")
- _register_property(register, "deterministic", "Deterministic")
- _register_property(register, "prefer_32bit", "Prefer32Bit")
- _register_property(register, "allow_unsafe_blocks", "AllowUnsafeBlocks")
- _register_property(register, "check_for_overflow_underflow", "CheckForOverflowUnderflow")
- _register_property(register, "analysis_level", "AnalysisLevel")
- _register_property(register, "enable_net_analyzers", "EnableNETAnalyzers")
- _register_property(register, "enforce_code_style_in_build", "EnforceCodeStyleInBuild")
- _register_list_property(register, "warnings_as_errors", "WarningsAsErrors")
- _register_list_property(register, "warnings_not_as_errors", "WarningsNotAsErrors")
- _register_property(register, "error_log", "ErrorLog")
- _register_property(register, "generate_documentation_file", "GenerateDocumentationFile")
- _register_property(register, "documentation_file", "DocumentationFile")
-
- _register_property(register, "runtime_identifier", "RuntimeIdentifier")
- _register_list_property(register, "runtime_identifiers", "RuntimeIdentifiers")
- _register_property(register, "self_contained", "SelfContained")
- _register_property(register, "use_app_host", "UseAppHost")
- _register_property(register, "roll_forward", "RollForward")
- _register_property(register, "publish_single_file", "PublishSingleFile")
- _register_property(register, "publish_trimmed", "PublishTrimmed")
- _register_property(register, "trim_mode", "TrimMode")
- _register_property(register, "publish_ready_to_run", "PublishReadyToRun")
- _register_property(register, "invariant_globalization", "InvariantGlobalization")
- _register_property(register, "include_native_libraries_for_self_extract", "IncludeNativeLibrariesForSelfExtract")
- _register_property(register, "enable_compression_in_single_file", "EnableCompressionInSingleFile")
- _register_property(register, "publish_aot", "PublishAot")
- _register_property(register, "strip_symbols", "StripSymbols")
- _register_property(register, "enable_trim_analyzer", "EnableTrimAnalyzer")
- _register_property(register, "json_serializer_is_reflection_enabled_by_default", "JsonSerializerIsReflectionEnabledByDefault")
- _register_list_property(register, "satellite_resource_languages", "SatelliteResourceLanguages")
-
- _register_property(register, "version", "Version")
- _register_property(register, "assembly_version", "AssemblyVersion")
- _register_property(register, "file_version", "FileVersion")
- _register_property(register, "informational_version", "InformationalVersion")
- _register_property(register, "package_id", "PackageId")
- _register_property(register, "authors", "Authors")
- _register_property(register, "company", "Company")
- _register_property(register, "product", "Product")
- _register_property(register, "description", "Description")
- _register_property(register, "copyright", "Copyright")
- _register_property(register, "repository_url", "RepositoryUrl")
- _register_property(register, "repository_type", "RepositoryType")
- _register_property(register, "package_license_expression", "PackageLicenseExpression")
- _register_property(register, "package_project_url", "PackageProjectUrl")
- _register_property(register, "neutral_language", "NeutralLanguage")
- _register_property(register, "enable_preview_features", "EnablePreviewFeatures")
-
- _register_property(register, "generate_runtime_configuration_files", "GenerateRuntimeConfigurationFiles")
- _register_property(register, "copy_local_lock_file_assemblies", "CopyLocalLockFileAssemblies")
- _register_property(register, "append_target_framework_to_output_path", "AppendTargetFrameworkToOutputPath", "false")
- _register_property(register, "append_runtime_identifier_to_output_path", "AppendRuntimeIdentifierToOutputPath", "false")
- _register_property(register, "produce_reference_assembly", "ProduceReferenceAssembly")
- _register_property(register, "disable_implicit_framework_references", "DisableImplicitFrameworkReferences")
- _register_property(register, "generate_target_framework_attribute", "GenerateTargetFrameworkAttribute")
- return entries
-end
--
cgit v1.3.1
From 40ced1a19868a7ab57f6d1322c76b02e23a4aa97 Mon Sep 17 00:00:00 2001
From: ruki
Date: Tue, 17 Mar 2026 22:43:03 +0800
Subject: improve get default target framework
---
xmake/rules/csharp/modules/csproj_properties.lua | 39 +++++-------------------
xmake/toolchains/dotnet/xmake.lua | 3 ++
2 files changed, 11 insertions(+), 31 deletions(-)
diff --git a/xmake/rules/csharp/modules/csproj_properties.lua b/xmake/rules/csharp/modules/csproj_properties.lua
index 324e67531..75d80e5a8 100644
--- a/xmake/rules/csharp/modules/csproj_properties.lua
+++ b/xmake/rules/csharp/modules/csproj_properties.lua
@@ -36,40 +36,17 @@ function _get_target_value(target, name)
return _first(target:values(name))
end
--- detect default target framework from dotnet sdk version, e.g. "net8.0"
+-- get default target framework from dotnet toolchain sdk version, e.g. "net8.0"
function _get_default_target_framework(context)
- local dotnet = _first(context.target:get("toolset.cs")) or "dotnet"
- dotnet = tostring(dotnet)
- _g.default_target_frameworks = _g.default_target_frameworks or {}
- local cached = _g.default_target_frameworks[dotnet]
- if cached ~= nil then
- return cached
- end
-
- local major = nil
- local sdks = try { function ()
- return os.iorunv(dotnet, {"--list-sdks"})
- end }
- if sdks then
- for line in sdks:gmatch("[^\r\n]+") do
- local line_major = tonumber(line:match("^%s*(%d+)%.%d+%.%d+"))
- if line_major and (not major or line_major > major) then
- major = line_major
- end
+ local major
+ local toolchain = context.target:toolchain("dotnet")
+ if toolchain then
+ local sdkver = toolchain:config("sdkver")
+ if sdkver then
+ major = tonumber(tostring(sdkver):match("^(%d+)"))
end
end
- if not major then
- local version = try { function ()
- return os.iorunv(dotnet, {"--version"})
- end }
- if version then
- major = tonumber(version:match("^%s*(%d+)"))
- end
- end
-
- local target_framework = major and ("net" .. tostring(major) .. ".0") or "net8.0"
- _g.default_target_frameworks[dotnet] = target_framework
- return target_framework
+ return "net" .. tostring(major or 8) .. ".0"
end
-- resolve target framework from user config or auto-detect from dotnet sdk
diff --git a/xmake/toolchains/dotnet/xmake.lua b/xmake/toolchains/dotnet/xmake.lua
index 64fad0ec0..e26136a0a 100644
--- a/xmake/toolchains/dotnet/xmake.lua
+++ b/xmake/toolchains/dotnet/xmake.lua
@@ -50,6 +50,9 @@ toolchain("dotnet")
end
toolchain:config_set("bindir", sdkinfo.bindir)
toolchain:config_set("sdkdir", sdkinfo.sdkdir)
+ if sdkinfo.sdkver then
+ toolchain:config_set("sdkver", sdkinfo.sdkver)
+ end
return true
end)
--
cgit v1.3.1
From f48ca26675a3b9eac14374f98cc175a087e2e3e1 Mon Sep 17 00:00:00 2001
From: ruki
Date: Tue, 17 Mar 2026 22:45:32 +0800
Subject: improve tests
---
tests/projects/csharp/console_with_runtime_json/src/Program.cs | 2 +-
tests/projects/csharp/console_with_runtime_json/xmake.lua | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/tests/projects/csharp/console_with_runtime_json/src/Program.cs b/tests/projects/csharp/console_with_runtime_json/src/Program.cs
index 690faf873..fd836a339 100644
--- a/tests/projects/csharp/console_with_runtime_json/src/Program.cs
+++ b/tests/projects/csharp/console_with_runtime_json/src/Program.cs
@@ -1,6 +1,6 @@
using System.Text.Json;
-var runtimeFile = Path.Combine(AppContext.BaseDirectory, "runtime.json");
+var runtimeFile = Path.Combine(Directory.GetCurrentDirectory(), "runtime.json");
if (!File.Exists(runtimeFile)) {
Console.WriteLine("runtime.json missing");
return;
diff --git a/tests/projects/csharp/console_with_runtime_json/xmake.lua b/tests/projects/csharp/console_with_runtime_json/xmake.lua
index 474eda548..979f74307 100644
--- a/tests/projects/csharp/console_with_runtime_json/xmake.lua
+++ b/tests/projects/csharp/console_with_runtime_json/xmake.lua
@@ -4,3 +4,4 @@ target("app")
set_kind("binary")
add_rules("csharp")
add_files("src/Program.cs")
+ set_rundir("src")
--
cgit v1.3.1
From 27bd61c80153023faec1ca3e3aee787e02f161dd Mon Sep 17 00:00:00 2001
From: ruki
Date: Tue, 17 Mar 2026 22:48:28 +0800
Subject: install csharp target
---
xmake/modules/target/action/install/main.lua | 2 -
xmake/rules/csharp/install.lua | 79 +++++++++++++++++++++++++++
xmake/rules/csharp/installcmd.lua | 82 ++++++++++++++++++++++++++++
xmake/rules/csharp/xmake.lua | 2 +
4 files changed, 163 insertions(+), 2 deletions(-)
create mode 100644 xmake/rules/csharp/install.lua
create mode 100644 xmake/rules/csharp/installcmd.lua
diff --git a/xmake/modules/target/action/install/main.lua b/xmake/modules/target/action/install/main.lua
index 2e92122e8..466ebe551 100644
--- a/xmake/modules/target/action/install/main.lua
+++ b/xmake/modules/target/action/install/main.lua
@@ -49,8 +49,6 @@ function _get_target_includedir(target, opt)
return path.join(opt.installdir, opt.includedir)
end
-
-
-- copy file with symlinks
function _copy_file_with_symlinks(srcfile, outputdir)
if os.islink(srcfile) then
diff --git a/xmake/rules/csharp/install.lua b/xmake/rules/csharp/install.lua
new file mode 100644
index 000000000..fcd730c8e
--- /dev/null
+++ b/xmake/rules/csharp/install.lua
@@ -0,0 +1,79 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author ruki
+-- @file install.lua
+--
+
+-- imports
+import("core.project.config")
+
+-- get dotnet program
+function _get_dotnet(target)
+ return target:tool("cs") or "dotnet"
+end
+
+-- get build configuration from mode
+function _get_configuration()
+ local mode = config.mode() or "release"
+ if mode:lower() == "debug" then
+ return "Debug"
+ end
+ return "Release"
+end
+
+-- get output directory for dotnet publish
+function _get_outputdir(target)
+ if target:is_binary() then
+ return target:bindir()
+ else
+ return target:libdir()
+ end
+end
+
+-- install csharp target using dotnet publish
+function main(target)
+ local installdir = target:installdir()
+ if not installdir then
+ return
+ end
+
+ -- get output directory based on target kind
+ local outputdir = _get_outputdir(target)
+ if not outputdir then
+ return
+ end
+
+ -- run dotnet publish
+ local csprojfile = target:data("csharp.csproj")
+ local argv = {"publish"}
+ if csprojfile then
+ table.insert(argv, csprojfile)
+ end
+ table.join2(argv, {"--nologo", "--no-build",
+ "--configuration", _get_configuration(),
+ "--output", outputdir})
+ local dotnet = _get_dotnet(target)
+ os.vrunv(dotnet, argv)
+
+ -- install extra files (add_installfiles)
+ local srcfiles, dstfiles = target:installfiles(installdir)
+ if srcfiles and dstfiles then
+ for idx, srcfile in ipairs(srcfiles) do
+ os.vcp(srcfile, dstfiles[idx])
+ end
+ end
+end
diff --git a/xmake/rules/csharp/installcmd.lua b/xmake/rules/csharp/installcmd.lua
new file mode 100644
index 000000000..0bcc9ff37
--- /dev/null
+++ b/xmake/rules/csharp/installcmd.lua
@@ -0,0 +1,82 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author ruki
+-- @file installcmd.lua
+--
+
+-- imports
+import("core.project.config")
+
+-- get dotnet program
+function _get_dotnet(target)
+ return target:tool("cs") or "dotnet"
+end
+
+-- get build configuration from mode
+function _get_configuration()
+ local mode = config.mode() or "release"
+ if mode:lower() == "debug" then
+ return "Debug"
+ end
+ return "Release"
+end
+
+-- install csharp target for xpack using dotnet publish
+function main(target, batchcmds, opt)
+ local package = opt.package
+ if not package then
+ return
+ end
+
+ local installdir = package:installdir()
+ if not installdir then
+ return
+ end
+
+ -- get output directory based on target kind
+ local outputdir
+ if target:is_binary() then
+ outputdir = package:installdir("bin")
+ else
+ outputdir = package:installdir("lib")
+ end
+
+ -- run dotnet publish to a temporary publish directory, then copy to install directory
+ local publishdir = path.join(target:autogendir(), "rules", "csharp", "publish")
+ local csprojfile = target:data("csharp.csproj")
+ local argv = {"publish"}
+ if csprojfile then
+ table.insert(argv, csprojfile)
+ end
+ table.join2(argv, {"--nologo", "--no-build",
+ "--configuration", _get_configuration(),
+ "--output", publishdir})
+ local dotnet = _get_dotnet(target)
+ batchcmds:vrunv(dotnet, argv)
+
+ -- copy published files to output directory
+ batchcmds:mkdir(outputdir)
+ batchcmds:cp(path.join(publishdir, "**"), outputdir, {rootdir = publishdir})
+
+ -- install extra files (add_installfiles)
+ local srcfiles, dstfiles = target:installfiles(installdir)
+ if srcfiles and dstfiles then
+ for idx, srcfile in ipairs(srcfiles) do
+ batchcmds:cp(srcfile, dstfiles[idx])
+ end
+ end
+end
diff --git a/xmake/rules/csharp/xmake.lua b/xmake/rules/csharp/xmake.lua
index ddd686bfc..4030c7457 100644
--- a/xmake/rules/csharp/xmake.lua
+++ b/xmake/rules/csharp/xmake.lua
@@ -116,6 +116,8 @@ rule("csharp.build")
set_sourcekinds("cs")
on_config("config")
on_build("build")
+ on_install("install")
+ on_installcmd("installcmd")
rule("csharp")
add_deps("csharp.build")
--
cgit v1.3.1
From 0513166f8232518a428499aa0190e79027b8916c Mon Sep 17 00:00:00 2001
From: ruki
Date: Tue, 17 Mar 2026 22:49:09 +0800
Subject: fix publish csharp
---
xmake/rules/csharp/install.lua | 7 ++++---
xmake/rules/csharp/installcmd.lua | 5 +++--
2 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/xmake/rules/csharp/install.lua b/xmake/rules/csharp/install.lua
index fcd730c8e..03e2cd24d 100644
--- a/xmake/rules/csharp/install.lua
+++ b/xmake/rules/csharp/install.lua
@@ -35,9 +35,10 @@ function _get_configuration()
return "Release"
end
--- get output directory for dotnet publish
+-- get output directory based on target kind
+-- on windows, shared libraries (dll) should also go to bindir
function _get_outputdir(target)
- if target:is_binary() then
+ if target:is_binary() or (target:is_shared() and target:is_plat("windows", "mingw")) then
return target:bindir()
else
return target:libdir()
@@ -63,7 +64,7 @@ function main(target)
if csprojfile then
table.insert(argv, csprojfile)
end
- table.join2(argv, {"--nologo", "--no-build",
+ table.join2(argv, {"--nologo",
"--configuration", _get_configuration(),
"--output", outputdir})
local dotnet = _get_dotnet(target)
diff --git a/xmake/rules/csharp/installcmd.lua b/xmake/rules/csharp/installcmd.lua
index 0bcc9ff37..25de92989 100644
--- a/xmake/rules/csharp/installcmd.lua
+++ b/xmake/rules/csharp/installcmd.lua
@@ -48,8 +48,9 @@ function main(target, batchcmds, opt)
end
-- get output directory based on target kind
+ -- on windows, shared libraries (dll) should also go to bindir
local outputdir
- if target:is_binary() then
+ if target:is_binary() or (target:is_shared() and target:is_plat("windows", "mingw")) then
outputdir = package:installdir("bin")
else
outputdir = package:installdir("lib")
@@ -62,7 +63,7 @@ function main(target, batchcmds, opt)
if csprojfile then
table.insert(argv, csprojfile)
end
- table.join2(argv, {"--nologo", "--no-build",
+ table.join2(argv, {"--nologo",
"--configuration", _get_configuration(),
"--output", publishdir})
local dotnet = _get_dotnet(target)
--
cgit v1.3.1
From 9c3345d7357d1ede85b9f24d6721986315e28de6 Mon Sep 17 00:00:00 2001
From: ruki
Date: Tue, 17 Mar 2026 22:50:20 +0800
Subject: rename files
---
xmake/rules/csharp/config.lua | 2 +-
xmake/rules/csharp/generator/csproj.lua | 257 +++++++++++++++++++++++
xmake/rules/csharp/generator/itemgroups.lua | 163 ++++++++++++++
xmake/rules/csharp/generator/properties.lua | 174 +++++++++++++++
xmake/rules/csharp/modules/csproj_generator.lua | 257 -----------------------
xmake/rules/csharp/modules/csproj_itemgroups.lua | 163 --------------
xmake/rules/csharp/modules/csproj_properties.lua | 174 ---------------
7 files changed, 595 insertions(+), 595 deletions(-)
create mode 100644 xmake/rules/csharp/generator/csproj.lua
create mode 100644 xmake/rules/csharp/generator/itemgroups.lua
create mode 100644 xmake/rules/csharp/generator/properties.lua
delete mode 100644 xmake/rules/csharp/modules/csproj_generator.lua
delete mode 100644 xmake/rules/csharp/modules/csproj_itemgroups.lua
delete mode 100644 xmake/rules/csharp/modules/csproj_properties.lua
diff --git a/xmake/rules/csharp/config.lua b/xmake/rules/csharp/config.lua
index 8b7186888..3f6b71844 100644
--- a/xmake/rules/csharp/config.lua
+++ b/xmake/rules/csharp/config.lua
@@ -20,7 +20,7 @@
-- imports
import("core.project.depend")
-import("modules.csproj_generator", {rootdir = os.scriptdir(), alias = "generate_csproj"})
+import("generator.csproj", {rootdir = os.scriptdir(), alias = "generate_csproj"})
function main(target)
diff --git a/xmake/rules/csharp/generator/csproj.lua b/xmake/rules/csharp/generator/csproj.lua
new file mode 100644
index 000000000..e28cbfc19
--- /dev/null
+++ b/xmake/rules/csharp/generator/csproj.lua
@@ -0,0 +1,257 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author JassJam
+-- @file csproj_generator.lua
+--
+
+-- imports
+import("properties")
+import("itemgroups")
+
+-- escape special xml characters
+function _xml_escape(value)
+ value = tostring(value or "")
+ value = value:gsub("&", "&")
+ value = value:gsub("<", "<")
+ value = value:gsub(">", ">")
+ value = value:gsub("\"", """)
+ value = value:gsub("'", "'")
+ return value
+end
+
+-- format key-value pairs as xml attributes string, e.g. ` Sdk="Microsoft.NET.Sdk"`
+function _format_attributes(attrs)
+ if type(attrs) ~= "table" then
+ return ""
+ end
+ local keys = {}
+ for key, value in table.orderpairs(attrs) do
+ if value ~= nil and value ~= "" then
+ table.insert(keys, key)
+ end
+ end
+ table.sort(keys)
+ if #keys == 0 then
+ return ""
+ end
+ local chunks = {}
+ for _, key in ipairs(keys) do
+ table.insert(chunks, string.format(" %s=\"%s\"", key, _xml_escape(attrs[key])))
+ end
+ return table.concat(chunks)
+end
+
+-- get single csharp value from target:values(), with default fallback
+function _get_csharp_value(target, name, defaultval)
+ local val = target:values(name)
+ if type(val) == "table" then
+ val = val[1]
+ end
+ if val == nil or val == "" then
+ return defaultval
+ end
+ return val
+end
+
+-- resolve a registry entry value, supports custom resolve function, list type and single value
+function _resolve_registry_value(entry, target, context)
+ if entry.resolve then
+ return entry.resolve(context)
+ end
+ if entry.value_type == "list" then
+ local values = table.wrap(target:values(entry.lua_key))
+ if #values == 0 and entry.default ~= nil then
+ values = table.wrap(entry.default)
+ end
+ if #values > 0 then
+ local items = {}
+ for _, value in ipairs(values) do
+ if value ~= nil and value ~= "" then
+ table.insert(items, tostring(value))
+ end
+ end
+ if #items > 0 then
+ return table.concat(items, entry.sep or ";")
+ end
+ end
+ return nil
+ end
+ return _get_csharp_value(target, entry.lua_key, entry.default)
+end
+
+-- collect element attributes, e.g. Sdk="Microsoft.NET.Sdk"
+function _collect_project_attributes(target, context, registry_entries)
+ local attrs = {}
+ for _, entry in ipairs(registry_entries) do
+ if entry.kind == "project_attribute" then
+ if not entry.when or entry.when(context) then
+ local value = _resolve_registry_value(entry, target, context)
+ if value ~= nil and value ~= "" then
+ attrs[entry.attr] = value
+ end
+ end
+ end
+ end
+ return attrs
+end
+
+-- collect entries from registered csharp.* properties
+function _collect_property_entries(target, context, registry_entries)
+ local entries = {}
+ for _, entry in ipairs(registry_entries) do
+ if entry.kind == "property" then
+ if not entry.when or entry.when(context) then
+ local value = _resolve_registry_value(entry, target, context)
+ if value ~= nil and value ~= "" then
+ table.insert(entries, {xml = entry.xml, value = value})
+ end
+ end
+ end
+ end
+ return entries
+end
+
+-- normalize item entry to {xml, attrs, value} format
+function _normalize_item_entry(item, default_xml)
+ if type(item) == "string" then
+ return {xml = default_xml, attrs = {Include = item}}
+ elseif type(item) ~= "table" then
+ return nil
+ end
+ local xml = item.xml or default_xml
+ if not xml or #tostring(xml) == 0 then
+ return nil
+ end
+ local attrs = item.attrs
+ if type(attrs) ~= "table" then
+ attrs = {}
+ for key, value in table.orderpairs(item) do
+ if type(key) == "string" and key ~= "xml" and key ~= "value" and key ~= "attrs" then
+ attrs[key] = value
+ end
+ end
+ end
+ return {xml = xml, attrs = attrs, value = item.value}
+end
+
+-- collect entries (Compile, ProjectReference, PackageReference, ..)
+function _collect_item_groups(context, registry_entries)
+ local groups = {}
+ local groupmap = {}
+ function _group(name)
+ local g = groupmap[name]
+ if not g then
+ g = {name = name, items = {}}
+ groupmap[name] = g
+ table.insert(groups, g)
+ end
+ return g
+ end
+ for _, entry in ipairs(registry_entries) do
+ if entry.kind == "item" then
+ if not entry.when or entry.when(context) then
+ for _, item in ipairs(table.wrap(entry.resolve_items and entry.resolve_items(context) or {})) do
+ local normalized = _normalize_item_entry(item, entry.xml)
+ if normalized then
+ table.insert(_group(entry.group or entry.xml).items, normalized)
+ end
+ end
+ end
+ end
+ end
+ return groups
+end
+
+-- collect custom properties from target:values("csharp.properties")
+-- value format: "Name=Value", e.g. set_values("csharp.properties", "MyProp=value")
+function _collect_custom_property_entries(target)
+ local entries = {}
+ for _, item in ipairs(table.wrap(target:values("csharp.properties"))) do
+ local name, value = tostring(item):match("^%s*([^=]+)%s*=(.*)$")
+ if name and #value > 0 then
+ table.insert(entries, {xml = name:trim(), value = value})
+ end
+ end
+ return entries
+end
+
+-- render section to file
+function _render_property_group(file, entries)
+ if #entries == 0 then
+ return
+ end
+ file:print(" ")
+ for _, entry in ipairs(entries) do
+ file:print(" <%s>%s%s>", entry.xml, _xml_escape(entry.value), entry.xml)
+ end
+ file:print(" ")
+end
+
+-- render sections to file
+function _render_item_groups(file, item_groups)
+ for _, group in ipairs(item_groups) do
+ if #group.items > 0 then
+ file:print(" ")
+ for _, item in ipairs(group.items) do
+ local attrs = _format_attributes(item.attrs)
+ if item.value ~= nil and item.value ~= "" then
+ file:print(" <%s%s>%s%s>", item.xml, attrs, _xml_escape(item.value), item.xml)
+ else
+ file:print(" <%s%s />", item.xml, attrs)
+ end
+ end
+ file:print(" ")
+ end
+ end
+end
+
+-- generate .csproj file for the target, write to tmpfile first then copy if different
+function main(target, csprojfile, opt)
+ opt = opt or {}
+
+ local csprojdir = path.directory(csprojfile)
+ local context = {
+ target = target,
+ csprojfile = csprojfile,
+ csprojdir = csprojdir,
+ opt = opt
+ }
+
+ -- collect project properties
+ local property_registry_entries = properties()
+ local item_registry_entries = itemgroups()
+
+ local project_attributes = _collect_project_attributes(target, context, property_registry_entries)
+ local property_entries = _collect_property_entries(target, context, property_registry_entries)
+ local custom_property_entries = _collect_custom_property_entries(target)
+ table.join2(property_entries, custom_property_entries)
+
+ local item_groups = _collect_item_groups(context, item_registry_entries)
+
+ -- generate csproj
+ local tmpfile = os.tmpfile() .. ".csproj"
+ local file = io.open(tmpfile, "w")
+ file:print("", _format_attributes(project_attributes))
+ _render_property_group(file, property_entries)
+ _render_item_groups(file, item_groups)
+ file:print("")
+ file:close()
+
+ os.mkdir(csprojdir)
+ os.cp(tmpfile, csprojfile, {copy_if_different = true})
+ os.rm(tmpfile)
+end
diff --git a/xmake/rules/csharp/generator/itemgroups.lua b/xmake/rules/csharp/generator/itemgroups.lua
new file mode 100644
index 000000000..79f13b4fb
--- /dev/null
+++ b/xmake/rules/csharp/generator/itemgroups.lua
@@ -0,0 +1,163 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author JassJam
+-- @file csproj_itemsgroups.lua
+--
+
+-- normalize path to relative and use forward slashes
+function _normalize_relative(fromdir, targetpath)
+ local relpath = path.relative(targetpath, fromdir) or targetpath
+ return path.unix(relpath)
+end
+
+-- collect .cs source files as relative paths to csprojdir
+function _collect_cs_sourcefiles(context)
+ local csfiles = {}
+ for _, sourcefile in ipairs(context.target:sourcefiles()) do
+ if path.extension(sourcefile):lower() == ".cs" then
+ local sourceabs = path.is_absolute(sourcefile) and sourcefile or path.absolute(sourcefile, os.projectdir())
+ table.insert(csfiles, _normalize_relative(context.csprojdir, sourceabs))
+ end
+ end
+ table.sort(csfiles)
+ return table.unique(csfiles)
+end
+
+-- collect ProjectReference paths from dependency targets
+function _collect_project_references(context)
+ local references = {}
+ for _, dep in ipairs(context.target:orderdeps()) do
+ local depcsproj = dep:data("csharp.csproj")
+ if depcsproj then
+ table.insert(references, _normalize_relative(context.csprojdir, depcsproj))
+ end
+ end
+ table.sort(references)
+ return table.unique(references)
+end
+
+-- extract nuget package name and version from package require string
+function _get_nuget_info(pkg)
+ local requirestr = pkg:requirestr() or ""
+ local splitinfo = requirestr:trim():split("%s+")
+ if #splitinfo == 0 then
+ return nil
+ end
+
+ local pkgname = splitinfo[1]
+ if pkgname:find("::", 1, true) then
+ pkgname = pkgname:split("::", {plain = true})
+ pkgname = pkgname[#pkgname]
+ end
+ local pkgname_raw = pkgname:match("(.-)%[.*%]$")
+ if pkgname_raw and #pkgname_raw > 0 then
+ pkgname = pkgname_raw
+ end
+ if not pkgname or #pkgname == 0 then
+ return nil
+ end
+
+ local version
+ local versionobj = pkg:version()
+ if versionobj then
+ version = tostring(versionobj)
+ end
+ if not version and #splitinfo > 1 then
+ local require_version = table.concat(table.slice(splitinfo, 2), " ")
+ if require_version ~= "latest" then
+ version = require_version
+ end
+ end
+ return pkgname, version
+end
+
+-- collect PackageReference entries from nuget packages
+function _collect_nuget_references(context)
+ local versions = {}
+ for _, pkg in ipairs(context.target:orderpkgs()) do
+ local namespace = pkg:namespace()
+ local requirestr = pkg:requirestr() or ""
+ if namespace == "nuget" or requirestr:startswith("nuget::") then
+ local pkgname, version = _get_nuget_info(pkg)
+ if pkgname then
+ if version or versions[pkgname] == nil then
+ versions[pkgname] = version or false
+ end
+ end
+ end
+ end
+
+ local references = {}
+ for pkgname, version in table.orderpairs(versions) do
+ table.insert(references, {name = pkgname, version = version or nil})
+ end
+ table.sort(references, function (a, b) return a.name < b.name end)
+ return references
+end
+
+-- register all item group entries (Compile, ProjectReference, PackageReference)
+function main()
+ local entries = {}
+ local function register(entry)
+ table.insert(entries, entry)
+ end
+
+ register({
+ kind = "item",
+ group = "compile",
+ xml = "Compile",
+ resolve_items = function (context)
+ local items = {}
+ for _, sourcefile in ipairs(_collect_cs_sourcefiles(context)) do
+ table.insert(items, {attrs = {Include = sourcefile}})
+ end
+ return items
+ end
+ })
+
+ register({
+ kind = "item",
+ group = "project_reference",
+ xml = "ProjectReference",
+ resolve_items = function (context)
+ local items = {}
+ for _, reffile in ipairs(_collect_project_references(context)) do
+ table.insert(items, {attrs = {Include = reffile}})
+ end
+ return items
+ end
+ })
+
+ register({
+ kind = "item",
+ group = "package_reference",
+ xml = "PackageReference",
+ resolve_items = function (context)
+ local items = {}
+ for _, pkginfo in ipairs(_collect_nuget_references(context)) do
+ local attrs = {Include = pkginfo.name}
+ if pkginfo.version then
+ attrs.Version = pkginfo.version
+ end
+ table.insert(items, {attrs = attrs})
+ end
+ return items
+ end
+ })
+
+ return entries
+end
diff --git a/xmake/rules/csharp/generator/properties.lua b/xmake/rules/csharp/generator/properties.lua
new file mode 100644
index 000000000..75d80e5a8
--- /dev/null
+++ b/xmake/rules/csharp/generator/properties.lua
@@ -0,0 +1,174 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, Xmake Open Source Community.
+--
+-- @author JassJam
+-- @file csproj_properties.lua
+--
+
+-- check if target has multi-target frameworks set
+function _has_target_frameworks(context)
+ return #table.wrap(context.target:values("csharp.target_frameworks")) > 0
+end
+
+-- get the first element if value is a table
+function _first(value)
+ if type(value) == "table" then
+ return value[1]
+ end
+ return value
+end
+
+-- get single value from target:values()
+function _get_target_value(target, name)
+ return _first(target:values(name))
+end
+
+-- get default target framework from dotnet toolchain sdk version, e.g. "net8.0"
+function _get_default_target_framework(context)
+ local major
+ local toolchain = context.target:toolchain("dotnet")
+ if toolchain then
+ local sdkver = toolchain:config("sdkver")
+ if sdkver then
+ major = tonumber(tostring(sdkver):match("^(%d+)"))
+ end
+ end
+ return "net" .. tostring(major or 8) .. ".0"
+end
+
+-- resolve target framework from user config or auto-detect from dotnet sdk
+function _resolve_target_framework(context)
+ local target_framework = _get_target_value(context.target, "csharp.target_framework")
+ if target_framework ~= nil and #tostring(target_framework) > 0 then
+ return target_framework
+ end
+ return _get_default_target_framework(context)
+end
+
+-- resolve assembly name from target basename
+function _resolve_assembly_name(context)
+ local basename = context.target:basename()
+ if basename ~= nil and #tostring(basename) > 0 then
+ return basename
+ end
+end
+
+
+-- register a single-value csharp.* property entry
+function _register_property(register, suffix, xml, default, extra)
+ local entry = table.join({
+ kind = "property",
+ xml = xml,
+ lua_key = "csharp." .. suffix,
+ default = default
+ }, extra or {})
+ register(entry)
+end
+
+-- register a list-value csharp.* property entry (semicolon-joined)
+function _register_list_property(register, suffix, xml, extra)
+ local entry = table.join({
+ kind = "property",
+ xml = xml,
+ lua_key = "csharp." .. suffix,
+ value_type = "list",
+ sep = ";"
+ }, extra or {})
+ register(entry)
+end
+
+-- register all csharp property and project attribute entries for csproj generation
+function main()
+ local entries = {}
+ function register(entry)
+ table.insert(entries, entry)
+ end
+
+ register({kind = "project_attribute", attr = "Sdk", lua_key = "csharp.sdk", default = "Microsoft.NET.Sdk"})
+ register({kind = "property", xml = "OutputType", resolve = function (context)
+ return context.target:is_binary() and "Exe" or "Library"
+ end})
+ _register_list_property(register, "target_frameworks", "TargetFrameworks", {when = _has_target_frameworks})
+ register({kind = "property", xml = "TargetFramework", resolve = _resolve_target_framework, when = function (context)
+ return not _has_target_frameworks(context)
+ end})
+
+ _register_property(register, "implicit_usings", "ImplicitUsings", "enable")
+ _register_property(register, "nullable", "Nullable", "enable")
+ _register_property(register, "lang_version", "LangVersion")
+ _register_property(register, "enable_default_compile_items", "EnableDefaultCompileItems", "false")
+ _register_property(register, "enable_default_embedded_resource_items", "EnableDefaultEmbeddedResourceItems")
+ _register_property(register, "enable_default_none_items", "EnableDefaultNoneItems")
+ _register_property(register, "root_namespace", "RootNamespace")
+ register({kind = "property", xml = "AssemblyName", resolve = _resolve_assembly_name})
+ _register_property(register, "generate_assembly_info", "GenerateAssemblyInfo")
+ _register_property(register, "deterministic", "Deterministic")
+ _register_property(register, "prefer_32bit", "Prefer32Bit")
+ _register_property(register, "allow_unsafe_blocks", "AllowUnsafeBlocks")
+ _register_property(register, "check_for_overflow_underflow", "CheckForOverflowUnderflow")
+ _register_property(register, "analysis_level", "AnalysisLevel")
+ _register_property(register, "enable_net_analyzers", "EnableNETAnalyzers")
+ _register_property(register, "enforce_code_style_in_build", "EnforceCodeStyleInBuild")
+ _register_list_property(register, "warnings_as_errors", "WarningsAsErrors")
+ _register_list_property(register, "warnings_not_as_errors", "WarningsNotAsErrors")
+ _register_property(register, "error_log", "ErrorLog")
+ _register_property(register, "generate_documentation_file", "GenerateDocumentationFile")
+ _register_property(register, "documentation_file", "DocumentationFile")
+
+ _register_property(register, "runtime_identifier", "RuntimeIdentifier")
+ _register_list_property(register, "runtime_identifiers", "RuntimeIdentifiers")
+ _register_property(register, "self_contained", "SelfContained")
+ _register_property(register, "use_app_host", "UseAppHost")
+ _register_property(register, "roll_forward", "RollForward")
+ _register_property(register, "publish_single_file", "PublishSingleFile")
+ _register_property(register, "publish_trimmed", "PublishTrimmed")
+ _register_property(register, "trim_mode", "TrimMode")
+ _register_property(register, "publish_ready_to_run", "PublishReadyToRun")
+ _register_property(register, "invariant_globalization", "InvariantGlobalization")
+ _register_property(register, "include_native_libraries_for_self_extract", "IncludeNativeLibrariesForSelfExtract")
+ _register_property(register, "enable_compression_in_single_file", "EnableCompressionInSingleFile")
+ _register_property(register, "publish_aot", "PublishAot")
+ _register_property(register, "strip_symbols", "StripSymbols")
+ _register_property(register, "enable_trim_analyzer", "EnableTrimAnalyzer")
+ _register_property(register, "json_serializer_is_reflection_enabled_by_default", "JsonSerializerIsReflectionEnabledByDefault")
+ _register_list_property(register, "satellite_resource_languages", "SatelliteResourceLanguages")
+
+ _register_property(register, "version", "Version")
+ _register_property(register, "assembly_version", "AssemblyVersion")
+ _register_property(register, "file_version", "FileVersion")
+ _register_property(register, "informational_version", "InformationalVersion")
+ _register_property(register, "package_id", "PackageId")
+ _register_property(register, "authors", "Authors")
+ _register_property(register, "company", "Company")
+ _register_property(register, "product", "Product")
+ _register_property(register, "description", "Description")
+ _register_property(register, "copyright", "Copyright")
+ _register_property(register, "repository_url", "RepositoryUrl")
+ _register_property(register, "repository_type", "RepositoryType")
+ _register_property(register, "package_license_expression", "PackageLicenseExpression")
+ _register_property(register, "package_project_url", "PackageProjectUrl")
+ _register_property(register, "neutral_language", "NeutralLanguage")
+ _register_property(register, "enable_preview_features", "EnablePreviewFeatures")
+
+ _register_property(register, "generate_runtime_configuration_files", "GenerateRuntimeConfigurationFiles")
+ _register_property(register, "copy_local_lock_file_assemblies", "CopyLocalLockFileAssemblies")
+ _register_property(register, "append_target_framework_to_output_path", "AppendTargetFrameworkToOutputPath", "false")
+ _register_property(register, "append_runtime_identifier_to_output_path", "AppendRuntimeIdentifierToOutputPath", "false")
+ _register_property(register, "produce_reference_assembly", "ProduceReferenceAssembly")
+ _register_property(register, "disable_implicit_framework_references", "DisableImplicitFrameworkReferences")
+ _register_property(register, "generate_target_framework_attribute", "GenerateTargetFrameworkAttribute")
+ return entries
+end
diff --git a/xmake/rules/csharp/modules/csproj_generator.lua b/xmake/rules/csharp/modules/csproj_generator.lua
deleted file mode 100644
index 0755b41a0..000000000
--- a/xmake/rules/csharp/modules/csproj_generator.lua
+++ /dev/null
@@ -1,257 +0,0 @@
---!A cross-platform build utility based on Lua
---
--- Licensed under the Apache License, Version 2.0 (the "License");
--- you may not use this file except in compliance with the License.
--- You may obtain a copy of the License at
---
--- http://www.apache.org/licenses/LICENSE-2.0
---
--- Unless required by applicable law or agreed to in writing, software
--- distributed under the License is distributed on an "AS IS" BASIS,
--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--- See the License for the specific language governing permissions and
--- limitations under the License.
---
--- Copyright (C) 2015-present, Xmake Open Source Community.
---
--- @author JassJam
--- @file csproj_generator.lua
---
-
--- imports
-import("csproj_properties")
-import("csproj_itemgroups")
-
--- escape special xml characters
-function _xml_escape(value)
- value = tostring(value or "")
- value = value:gsub("&", "&")
- value = value:gsub("<", "<")
- value = value:gsub(">", ">")
- value = value:gsub("\"", """)
- value = value:gsub("'", "'")
- return value
-end
-
--- format key-value pairs as xml attributes string, e.g. ` Sdk="Microsoft.NET.Sdk"`
-function _format_attributes(attrs)
- if type(attrs) ~= "table" then
- return ""
- end
- local keys = {}
- for key, value in table.orderpairs(attrs) do
- if value ~= nil and value ~= "" then
- table.insert(keys, key)
- end
- end
- table.sort(keys)
- if #keys == 0 then
- return ""
- end
- local chunks = {}
- for _, key in ipairs(keys) do
- table.insert(chunks, string.format(" %s=\"%s\"", key, _xml_escape(attrs[key])))
- end
- return table.concat(chunks)
-end
-
--- get single csharp value from target:values(), with default fallback
-function _get_csharp_value(target, name, defaultval)
- local val = target:values(name)
- if type(val) == "table" then
- val = val[1]
- end
- if val == nil or val == "" then
- return defaultval
- end
- return val
-end
-
--- resolve a registry entry value, supports custom resolve function, list type and single value
-function _resolve_registry_value(entry, target, context)
- if entry.resolve then
- return entry.resolve(context)
- end
- if entry.value_type == "list" then
- local values = table.wrap(target:values(entry.lua_key))
- if #values == 0 and entry.default ~= nil then
- values = table.wrap(entry.default)
- end
- if #values > 0 then
- local items = {}
- for _, value in ipairs(values) do
- if value ~= nil and value ~= "" then
- table.insert(items, tostring(value))
- end
- end
- if #items > 0 then
- return table.concat(items, entry.sep or ";")
- end
- end
- return nil
- end
- return _get_csharp_value(target, entry.lua_key, entry.default)
-end
-
--- collect element attributes, e.g. Sdk="Microsoft.NET.Sdk"
-function _collect_project_attributes(target, context, registry_entries)
- local attrs = {}
- for _, entry in ipairs(registry_entries) do
- if entry.kind == "project_attribute" then
- if not entry.when or entry.when(context) then
- local value = _resolve_registry_value(entry, target, context)
- if value ~= nil and value ~= "" then
- attrs[entry.attr] = value
- end
- end
- end
- end
- return attrs
-end
-
--- collect entries from registered csharp.* properties
-function _collect_property_entries(target, context, registry_entries)
- local entries = {}
- for _, entry in ipairs(registry_entries) do
- if entry.kind == "property" then
- if not entry.when or entry.when(context) then
- local value = _resolve_registry_value(entry, target, context)
- if value ~= nil and value ~= "" then
- table.insert(entries, {xml = entry.xml, value = value})
- end
- end
- end
- end
- return entries
-end
-
--- normalize item entry to {xml, attrs, value} format
-function _normalize_item_entry(item, default_xml)
- if type(item) == "string" then
- return {xml = default_xml, attrs = {Include = item}}
- elseif type(item) ~= "table" then
- return nil
- end
- local xml = item.xml or default_xml
- if not xml or #tostring(xml) == 0 then
- return nil
- end
- local attrs = item.attrs
- if type(attrs) ~= "table" then
- attrs = {}
- for key, value in table.orderpairs(item) do
- if type(key) == "string" and key ~= "xml" and key ~= "value" and key ~= "attrs" then
- attrs[key] = value
- end
- end
- end
- return {xml = xml, attrs = attrs, value = item.value}
-end
-
--- collect entries (Compile, ProjectReference, PackageReference, ..)
-function _collect_item_groups(context, registry_entries)
- local groups = {}
- local groupmap = {}
- function _group(name)
- local g = groupmap[name]
- if not g then
- g = {name = name, items = {}}
- groupmap[name] = g
- table.insert(groups, g)
- end
- return g
- end
- for _, entry in ipairs(registry_entries) do
- if entry.kind == "item" then
- if not entry.when or entry.when(context) then
- for _, item in ipairs(table.wrap(entry.resolve_items and entry.resolve_items(context) or {})) do
- local normalized = _normalize_item_entry(item, entry.xml)
- if normalized then
- table.insert(_group(entry.group or entry.xml).items, normalized)
- end
- end
- end
- end
- end
- return groups
-end
-
--- collect custom properties from target:values("csharp.properties")
--- value format: "Name=Value", e.g. set_values("csharp.properties", "MyProp=value")
-function _collect_custom_property_entries(target)
- local entries = {}
- for _, item in ipairs(table.wrap(target:values("csharp.properties"))) do
- local name, value = tostring(item):match("^%s*([^=]+)%s*=(.*)$")
- if name and #value > 0 then
- table.insert(entries, {xml = name:trim(), value = value})
- end
- end
- return entries
-end
-
--- render section to file
-function _render_property_group(file, entries)
- if #entries == 0 then
- return
- end
- file:print(" ")
- for _, entry in ipairs(entries) do
- file:print(" <%s>%s%s>", entry.xml, _xml_escape(entry.value), entry.xml)
- end
- file:print(" ")
-end
-
--- render sections to file
-function _render_item_groups(file, item_groups)
- for _, group in ipairs(item_groups) do
- if #group.items > 0 then
- file:print(" ")
- for _, item in ipairs(group.items) do
- local attrs = _format_attributes(item.attrs)
- if item.value ~= nil and item.value ~= "" then
- file:print(" <%s%s>%s%s>", item.xml, attrs, _xml_escape(item.value), item.xml)
- else
- file:print(" <%s%s />", item.xml, attrs)
- end
- end
- file:print(" ")
- end
- end
-end
-
--- generate .csproj file for the target, write to tmpfile first then copy if different
-function main(target, csprojfile, opt)
- opt = opt or {}
-
- local csprojdir = path.directory(csprojfile)
- local context = {
- target = target,
- csprojfile = csprojfile,
- csprojdir = csprojdir,
- opt = opt
- }
-
- -- collect project properties
- local property_registry_entries = csproj_properties()
- local item_registry_entries = csproj_itemgroups()
-
- local project_attributes = _collect_project_attributes(target, context, property_registry_entries)
- local property_entries = _collect_property_entries(target, context, property_registry_entries)
- local custom_property_entries = _collect_custom_property_entries(target)
- table.join2(property_entries, custom_property_entries)
-
- local item_groups = _collect_item_groups(context, item_registry_entries)
-
- -- generate csproj
- local tmpfile = os.tmpfile() .. ".csproj"
- local file = io.open(tmpfile, "w")
- file:print("", _format_attributes(project_attributes))
- _render_property_group(file, property_entries)
- _render_item_groups(file, item_groups)
- file:print("")
- file:close()
-
- os.mkdir(csprojdir)
- os.cp(tmpfile, csprojfile, {copy_if_different = true})
- os.rm(tmpfile)
-end
diff --git a/xmake/rules/csharp/modules/csproj_itemgroups.lua b/xmake/rules/csharp/modules/csproj_itemgroups.lua
deleted file mode 100644
index 79f13b4fb..000000000
--- a/xmake/rules/csharp/modules/csproj_itemgroups.lua
+++ /dev/null
@@ -1,163 +0,0 @@
---!A cross-platform build utility based on Lua
---
--- Licensed under the Apache License, Version 2.0 (the "License");
--- you may not use this file except in compliance with the License.
--- You may obtain a copy of the License at
---
--- http://www.apache.org/licenses/LICENSE-2.0
---
--- Unless required by applicable law or agreed to in writing, software
--- distributed under the License is distributed on an "AS IS" BASIS,
--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--- See the License for the specific language governing permissions and
--- limitations under the License.
---
--- Copyright (C) 2015-present, Xmake Open Source Community.
---
--- @author JassJam
--- @file csproj_itemsgroups.lua
---
-
--- normalize path to relative and use forward slashes
-function _normalize_relative(fromdir, targetpath)
- local relpath = path.relative(targetpath, fromdir) or targetpath
- return path.unix(relpath)
-end
-
--- collect .cs source files as relative paths to csprojdir
-function _collect_cs_sourcefiles(context)
- local csfiles = {}
- for _, sourcefile in ipairs(context.target:sourcefiles()) do
- if path.extension(sourcefile):lower() == ".cs" then
- local sourceabs = path.is_absolute(sourcefile) and sourcefile or path.absolute(sourcefile, os.projectdir())
- table.insert(csfiles, _normalize_relative(context.csprojdir, sourceabs))
- end
- end
- table.sort(csfiles)
- return table.unique(csfiles)
-end
-
--- collect ProjectReference paths from dependency targets
-function _collect_project_references(context)
- local references = {}
- for _, dep in ipairs(context.target:orderdeps()) do
- local depcsproj = dep:data("csharp.csproj")
- if depcsproj then
- table.insert(references, _normalize_relative(context.csprojdir, depcsproj))
- end
- end
- table.sort(references)
- return table.unique(references)
-end
-
--- extract nuget package name and version from package require string
-function _get_nuget_info(pkg)
- local requirestr = pkg:requirestr() or ""
- local splitinfo = requirestr:trim():split("%s+")
- if #splitinfo == 0 then
- return nil
- end
-
- local pkgname = splitinfo[1]
- if pkgname:find("::", 1, true) then
- pkgname = pkgname:split("::", {plain = true})
- pkgname = pkgname[#pkgname]
- end
- local pkgname_raw = pkgname:match("(.-)%[.*%]$")
- if pkgname_raw and #pkgname_raw > 0 then
- pkgname = pkgname_raw
- end
- if not pkgname or #pkgname == 0 then
- return nil
- end
-
- local version
- local versionobj = pkg:version()
- if versionobj then
- version = tostring(versionobj)
- end
- if not version and #splitinfo > 1 then
- local require_version = table.concat(table.slice(splitinfo, 2), " ")
- if require_version ~= "latest" then
- version = require_version
- end
- end
- return pkgname, version
-end
-
--- collect PackageReference entries from nuget packages
-function _collect_nuget_references(context)
- local versions = {}
- for _, pkg in ipairs(context.target:orderpkgs()) do
- local namespace = pkg:namespace()
- local requirestr = pkg:requirestr() or ""
- if namespace == "nuget" or requirestr:startswith("nuget::") then
- local pkgname, version = _get_nuget_info(pkg)
- if pkgname then
- if version or versions[pkgname] == nil then
- versions[pkgname] = version or false
- end
- end
- end
- end
-
- local references = {}
- for pkgname, version in table.orderpairs(versions) do
- table.insert(references, {name = pkgname, version = version or nil})
- end
- table.sort(references, function (a, b) return a.name < b.name end)
- return references
-end
-
--- register all item group entries (Compile, ProjectReference, PackageReference)
-function main()
- local entries = {}
- local function register(entry)
- table.insert(entries, entry)
- end
-
- register({
- kind = "item",
- group = "compile",
- xml = "Compile",
- resolve_items = function (context)
- local items = {}
- for _, sourcefile in ipairs(_collect_cs_sourcefiles(context)) do
- table.insert(items, {attrs = {Include = sourcefile}})
- end
- return items
- end
- })
-
- register({
- kind = "item",
- group = "project_reference",
- xml = "ProjectReference",
- resolve_items = function (context)
- local items = {}
- for _, reffile in ipairs(_collect_project_references(context)) do
- table.insert(items, {attrs = {Include = reffile}})
- end
- return items
- end
- })
-
- register({
- kind = "item",
- group = "package_reference",
- xml = "PackageReference",
- resolve_items = function (context)
- local items = {}
- for _, pkginfo in ipairs(_collect_nuget_references(context)) do
- local attrs = {Include = pkginfo.name}
- if pkginfo.version then
- attrs.Version = pkginfo.version
- end
- table.insert(items, {attrs = attrs})
- end
- return items
- end
- })
-
- return entries
-end
diff --git a/xmake/rules/csharp/modules/csproj_properties.lua b/xmake/rules/csharp/modules/csproj_properties.lua
deleted file mode 100644
index 75d80e5a8..000000000
--- a/xmake/rules/csharp/modules/csproj_properties.lua
+++ /dev/null
@@ -1,174 +0,0 @@
---!A cross-platform build utility based on Lua
---
--- Licensed under the Apache License, Version 2.0 (the "License");
--- you may not use this file except in compliance with the License.
--- You may obtain a copy of the License at
---
--- http://www.apache.org/licenses/LICENSE-2.0
---
--- Unless required by applicable law or agreed to in writing, software
--- distributed under the License is distributed on an "AS IS" BASIS,
--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--- See the License for the specific language governing permissions and
--- limitations under the License.
---
--- Copyright (C) 2015-present, Xmake Open Source Community.
---
--- @author JassJam
--- @file csproj_properties.lua
---
-
--- check if target has multi-target frameworks set
-function _has_target_frameworks(context)
- return #table.wrap(context.target:values("csharp.target_frameworks")) > 0
-end
-
--- get the first element if value is a table
-function _first(value)
- if type(value) == "table" then
- return value[1]
- end
- return value
-end
-
--- get single value from target:values()
-function _get_target_value(target, name)
- return _first(target:values(name))
-end
-
--- get default target framework from dotnet toolchain sdk version, e.g. "net8.0"
-function _get_default_target_framework(context)
- local major
- local toolchain = context.target:toolchain("dotnet")
- if toolchain then
- local sdkver = toolchain:config("sdkver")
- if sdkver then
- major = tonumber(tostring(sdkver):match("^(%d+)"))
- end
- end
- return "net" .. tostring(major or 8) .. ".0"
-end
-
--- resolve target framework from user config or auto-detect from dotnet sdk
-function _resolve_target_framework(context)
- local target_framework = _get_target_value(context.target, "csharp.target_framework")
- if target_framework ~= nil and #tostring(target_framework) > 0 then
- return target_framework
- end
- return _get_default_target_framework(context)
-end
-
--- resolve assembly name from target basename
-function _resolve_assembly_name(context)
- local basename = context.target:basename()
- if basename ~= nil and #tostring(basename) > 0 then
- return basename
- end
-end
-
-
--- register a single-value csharp.* property entry
-function _register_property(register, suffix, xml, default, extra)
- local entry = table.join({
- kind = "property",
- xml = xml,
- lua_key = "csharp." .. suffix,
- default = default
- }, extra or {})
- register(entry)
-end
-
--- register a list-value csharp.* property entry (semicolon-joined)
-function _register_list_property(register, suffix, xml, extra)
- local entry = table.join({
- kind = "property",
- xml = xml,
- lua_key = "csharp." .. suffix,
- value_type = "list",
- sep = ";"
- }, extra or {})
- register(entry)
-end
-
--- register all csharp property and project attribute entries for csproj generation
-function main()
- local entries = {}
- function register(entry)
- table.insert(entries, entry)
- end
-
- register({kind = "project_attribute", attr = "Sdk", lua_key = "csharp.sdk", default = "Microsoft.NET.Sdk"})
- register({kind = "property", xml = "OutputType", resolve = function (context)
- return context.target:is_binary() and "Exe" or "Library"
- end})
- _register_list_property(register, "target_frameworks", "TargetFrameworks", {when = _has_target_frameworks})
- register({kind = "property", xml = "TargetFramework", resolve = _resolve_target_framework, when = function (context)
- return not _has_target_frameworks(context)
- end})
-
- _register_property(register, "implicit_usings", "ImplicitUsings", "enable")
- _register_property(register, "nullable", "Nullable", "enable")
- _register_property(register, "lang_version", "LangVersion")
- _register_property(register, "enable_default_compile_items", "EnableDefaultCompileItems", "false")
- _register_property(register, "enable_default_embedded_resource_items", "EnableDefaultEmbeddedResourceItems")
- _register_property(register, "enable_default_none_items", "EnableDefaultNoneItems")
- _register_property(register, "root_namespace", "RootNamespace")
- register({kind = "property", xml = "AssemblyName", resolve = _resolve_assembly_name})
- _register_property(register, "generate_assembly_info", "GenerateAssemblyInfo")
- _register_property(register, "deterministic", "Deterministic")
- _register_property(register, "prefer_32bit", "Prefer32Bit")
- _register_property(register, "allow_unsafe_blocks", "AllowUnsafeBlocks")
- _register_property(register, "check_for_overflow_underflow", "CheckForOverflowUnderflow")
- _register_property(register, "analysis_level", "AnalysisLevel")
- _register_property(register, "enable_net_analyzers", "EnableNETAnalyzers")
- _register_property(register, "enforce_code_style_in_build", "EnforceCodeStyleInBuild")
- _register_list_property(register, "warnings_as_errors", "WarningsAsErrors")
- _register_list_property(register, "warnings_not_as_errors", "WarningsNotAsErrors")
- _register_property(register, "error_log", "ErrorLog")
- _register_property(register, "generate_documentation_file", "GenerateDocumentationFile")
- _register_property(register, "documentation_file", "DocumentationFile")
-
- _register_property(register, "runtime_identifier", "RuntimeIdentifier")
- _register_list_property(register, "runtime_identifiers", "RuntimeIdentifiers")
- _register_property(register, "self_contained", "SelfContained")
- _register_property(register, "use_app_host", "UseAppHost")
- _register_property(register, "roll_forward", "RollForward")
- _register_property(register, "publish_single_file", "PublishSingleFile")
- _register_property(register, "publish_trimmed", "PublishTrimmed")
- _register_property(register, "trim_mode", "TrimMode")
- _register_property(register, "publish_ready_to_run", "PublishReadyToRun")
- _register_property(register, "invariant_globalization", "InvariantGlobalization")
- _register_property(register, "include_native_libraries_for_self_extract", "IncludeNativeLibrariesForSelfExtract")
- _register_property(register, "enable_compression_in_single_file", "EnableCompressionInSingleFile")
- _register_property(register, "publish_aot", "PublishAot")
- _register_property(register, "strip_symbols", "StripSymbols")
- _register_property(register, "enable_trim_analyzer", "EnableTrimAnalyzer")
- _register_property(register, "json_serializer_is_reflection_enabled_by_default", "JsonSerializerIsReflectionEnabledByDefault")
- _register_list_property(register, "satellite_resource_languages", "SatelliteResourceLanguages")
-
- _register_property(register, "version", "Version")
- _register_property(register, "assembly_version", "AssemblyVersion")
- _register_property(register, "file_version", "FileVersion")
- _register_property(register, "informational_version", "InformationalVersion")
- _register_property(register, "package_id", "PackageId")
- _register_property(register, "authors", "Authors")
- _register_property(register, "company", "Company")
- _register_property(register, "product", "Product")
- _register_property(register, "description", "Description")
- _register_property(register, "copyright", "Copyright")
- _register_property(register, "repository_url", "RepositoryUrl")
- _register_property(register, "repository_type", "RepositoryType")
- _register_property(register, "package_license_expression", "PackageLicenseExpression")
- _register_property(register, "package_project_url", "PackageProjectUrl")
- _register_property(register, "neutral_language", "NeutralLanguage")
- _register_property(register, "enable_preview_features", "EnablePreviewFeatures")
-
- _register_property(register, "generate_runtime_configuration_files", "GenerateRuntimeConfigurationFiles")
- _register_property(register, "copy_local_lock_file_assemblies", "CopyLocalLockFileAssemblies")
- _register_property(register, "append_target_framework_to_output_path", "AppendTargetFrameworkToOutputPath", "false")
- _register_property(register, "append_runtime_identifier_to_output_path", "AppendRuntimeIdentifierToOutputPath", "false")
- _register_property(register, "produce_reference_assembly", "ProduceReferenceAssembly")
- _register_property(register, "disable_implicit_framework_references", "DisableImplicitFrameworkReferences")
- _register_property(register, "generate_target_framework_attribute", "GenerateTargetFrameworkAttribute")
- return entries
-end
--
cgit v1.3.1
From 800d9e9fb6891dcc861c24f17340f5a88209e2ea Mon Sep 17 00:00:00 2001
From: ruki
Date: Tue, 17 Mar 2026 23:23:00 +0800
Subject: improve check test
---
tests/projects/csharp/console/test.lua | 8 ++++----
tests/projects/csharp/console_with_runtime_json/test.lua | 8 ++++----
tests/projects/csharp/exe_with_library/test.lua | 8 ++++----
tests/projects/csharp/exe_with_package/test.lua | 8 ++++----
tests/projects/csharp/multiple_library/test.lua | 8 ++++----
tests/projects/csharp/web_project/test.lua | 8 ++++----
6 files changed, 24 insertions(+), 24 deletions(-)
diff --git a/tests/projects/csharp/console/test.lua b/tests/projects/csharp/console/test.lua
index 5ab8adc69..e67becab3 100644
--- a/tests/projects/csharp/console/test.lua
+++ b/tests/projects/csharp/console/test.lua
@@ -1,10 +1,10 @@
-import("lib.detect.find_tool")
+import("detect.sdks.find_dotnet")
function test_build(t)
- local dotnet = find_tool("dotnet")
- if dotnet then
+ local dotnet = find_dotnet()
+ if dotnet and dotnet.sdkver then
t:build()
else
- return t:skip("dotnet not found")
+ return t:skip("dotnet sdk not found")
end
end
diff --git a/tests/projects/csharp/console_with_runtime_json/test.lua b/tests/projects/csharp/console_with_runtime_json/test.lua
index 5ab8adc69..e67becab3 100644
--- a/tests/projects/csharp/console_with_runtime_json/test.lua
+++ b/tests/projects/csharp/console_with_runtime_json/test.lua
@@ -1,10 +1,10 @@
-import("lib.detect.find_tool")
+import("detect.sdks.find_dotnet")
function test_build(t)
- local dotnet = find_tool("dotnet")
- if dotnet then
+ local dotnet = find_dotnet()
+ if dotnet and dotnet.sdkver then
t:build()
else
- return t:skip("dotnet not found")
+ return t:skip("dotnet sdk not found")
end
end
diff --git a/tests/projects/csharp/exe_with_library/test.lua b/tests/projects/csharp/exe_with_library/test.lua
index 5ab8adc69..e67becab3 100644
--- a/tests/projects/csharp/exe_with_library/test.lua
+++ b/tests/projects/csharp/exe_with_library/test.lua
@@ -1,10 +1,10 @@
-import("lib.detect.find_tool")
+import("detect.sdks.find_dotnet")
function test_build(t)
- local dotnet = find_tool("dotnet")
- if dotnet then
+ local dotnet = find_dotnet()
+ if dotnet and dotnet.sdkver then
t:build()
else
- return t:skip("dotnet not found")
+ return t:skip("dotnet sdk not found")
end
end
diff --git a/tests/projects/csharp/exe_with_package/test.lua b/tests/projects/csharp/exe_with_package/test.lua
index 5ab8adc69..e67becab3 100644
--- a/tests/projects/csharp/exe_with_package/test.lua
+++ b/tests/projects/csharp/exe_with_package/test.lua
@@ -1,10 +1,10 @@
-import("lib.detect.find_tool")
+import("detect.sdks.find_dotnet")
function test_build(t)
- local dotnet = find_tool("dotnet")
- if dotnet then
+ local dotnet = find_dotnet()
+ if dotnet and dotnet.sdkver then
t:build()
else
- return t:skip("dotnet not found")
+ return t:skip("dotnet sdk not found")
end
end
diff --git a/tests/projects/csharp/multiple_library/test.lua b/tests/projects/csharp/multiple_library/test.lua
index 5ab8adc69..e67becab3 100644
--- a/tests/projects/csharp/multiple_library/test.lua
+++ b/tests/projects/csharp/multiple_library/test.lua
@@ -1,10 +1,10 @@
-import("lib.detect.find_tool")
+import("detect.sdks.find_dotnet")
function test_build(t)
- local dotnet = find_tool("dotnet")
- if dotnet then
+ local dotnet = find_dotnet()
+ if dotnet and dotnet.sdkver then
t:build()
else
- return t:skip("dotnet not found")
+ return t:skip("dotnet sdk not found")
end
end
diff --git a/tests/projects/csharp/web_project/test.lua b/tests/projects/csharp/web_project/test.lua
index 5ab8adc69..e67becab3 100644
--- a/tests/projects/csharp/web_project/test.lua
+++ b/tests/projects/csharp/web_project/test.lua
@@ -1,10 +1,10 @@
-import("lib.detect.find_tool")
+import("detect.sdks.find_dotnet")
function test_build(t)
- local dotnet = find_tool("dotnet")
- if dotnet then
+ local dotnet = find_dotnet()
+ if dotnet and dotnet.sdkver then
t:build()
else
- return t:skip("dotnet not found")
+ return t:skip("dotnet sdk not found")
end
end
--
cgit v1.3.1
From 4fc4db0777f0c12af374ef0676b610630158e9f2 Mon Sep 17 00:00:00 2001
From: ruki
Date: Tue, 17 Mar 2026 23:26:57 +0800
Subject: add dotnet for linux and macos
---
xmake/platforms/bsd/xmake.lua | 2 +-
xmake/platforms/linux/xmake.lua | 2 +-
xmake/platforms/macosx/xmake.lua | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/xmake/platforms/bsd/xmake.lua b/xmake/platforms/bsd/xmake.lua
index 3c34e2420..98143d019 100644
--- a/xmake/platforms/bsd/xmake.lua
+++ b/xmake/platforms/bsd/xmake.lua
@@ -30,7 +30,7 @@ platform("bsd")
set_installdir("/usr/local")
- set_toolchains("envs", "gcc", "clang", "yasm", "nasm", "fasm", "cuda", "go", "rust", "gfortran", "zig")
+ set_toolchains("envs", "gcc", "clang", "yasm", "nasm", "fasm", "cuda", "go", "rust", "gfortran", "zig", "dotnet")
set_menu {
config =
diff --git a/xmake/platforms/linux/xmake.lua b/xmake/platforms/linux/xmake.lua
index 8b0232241..b411dc4d3 100644
--- a/xmake/platforms/linux/xmake.lua
+++ b/xmake/platforms/linux/xmake.lua
@@ -36,7 +36,7 @@ platform("linux")
--
-- TODO Perhaps we should handle it better, or remove the cross toolchain.
set_toolchains("envs", "gcc", "clang",
- "cross", "yasm", "nasm", "fasm", "cuda", "go", "rust", "swift", "gfortran", "zig", "fpc", "nim")
+ "cross", "yasm", "nasm", "fasm", "cuda", "go", "rust", "swift", "gfortran", "zig", "fpc", "nim", "dotnet")
set_menu {
config =
diff --git a/xmake/platforms/macosx/xmake.lua b/xmake/platforms/macosx/xmake.lua
index 348c61f55..869d83843 100644
--- a/xmake/platforms/macosx/xmake.lua
+++ b/xmake/platforms/macosx/xmake.lua
@@ -29,7 +29,7 @@ platform("macosx")
set_formats("symbol", "$(name).dSYM")
set_installdir("/usr/local")
- set_toolchains("envs", "xcode", "clang", "gcc", "yasm", "nasm", "cuda", "rust", "go", "gfortran", "zig", "fpc", "nim")
+ set_toolchains("envs", "xcode", "clang", "gcc", "yasm", "nasm", "cuda", "rust", "go", "gfortran", "zig", "fpc", "nim", "dotnet")
set_menu {
config =
--
cgit v1.3.1
From c1e59483fabb0daef020f11964964e3686ea7a7e Mon Sep 17 00:00:00 2001
From: ruki
Date: Tue, 17 Mar 2026 23:43:30 +0800
Subject: fix dotnet on msys
---
tests/projects/csharp/console/test.lua | 3 +++
tests/projects/csharp/console_with_runtime_json/test.lua | 3 +++
tests/projects/csharp/exe_with_library/test.lua | 3 +++
tests/projects/csharp/exe_with_package/test.lua | 3 +++
tests/projects/csharp/multiple_library/test.lua | 3 +++
tests/projects/csharp/web_project/test.lua | 3 +++
6 files changed, 18 insertions(+)
diff --git a/tests/projects/csharp/console/test.lua b/tests/projects/csharp/console/test.lua
index e67becab3..d909316b7 100644
--- a/tests/projects/csharp/console/test.lua
+++ b/tests/projects/csharp/console/test.lua
@@ -1,6 +1,9 @@
import("detect.sdks.find_dotnet")
function test_build(t)
+ if is_subhost("msys") then
+ return t:skip("csharp not supported on msys")
+ end
local dotnet = find_dotnet()
if dotnet and dotnet.sdkver then
t:build()
diff --git a/tests/projects/csharp/console_with_runtime_json/test.lua b/tests/projects/csharp/console_with_runtime_json/test.lua
index e67becab3..d909316b7 100644
--- a/tests/projects/csharp/console_with_runtime_json/test.lua
+++ b/tests/projects/csharp/console_with_runtime_json/test.lua
@@ -1,6 +1,9 @@
import("detect.sdks.find_dotnet")
function test_build(t)
+ if is_subhost("msys") then
+ return t:skip("csharp not supported on msys")
+ end
local dotnet = find_dotnet()
if dotnet and dotnet.sdkver then
t:build()
diff --git a/tests/projects/csharp/exe_with_library/test.lua b/tests/projects/csharp/exe_with_library/test.lua
index e67becab3..d909316b7 100644
--- a/tests/projects/csharp/exe_with_library/test.lua
+++ b/tests/projects/csharp/exe_with_library/test.lua
@@ -1,6 +1,9 @@
import("detect.sdks.find_dotnet")
function test_build(t)
+ if is_subhost("msys") then
+ return t:skip("csharp not supported on msys")
+ end
local dotnet = find_dotnet()
if dotnet and dotnet.sdkver then
t:build()
diff --git a/tests/projects/csharp/exe_with_package/test.lua b/tests/projects/csharp/exe_with_package/test.lua
index e67becab3..d909316b7 100644
--- a/tests/projects/csharp/exe_with_package/test.lua
+++ b/tests/projects/csharp/exe_with_package/test.lua
@@ -1,6 +1,9 @@
import("detect.sdks.find_dotnet")
function test_build(t)
+ if is_subhost("msys") then
+ return t:skip("csharp not supported on msys")
+ end
local dotnet = find_dotnet()
if dotnet and dotnet.sdkver then
t:build()
diff --git a/tests/projects/csharp/multiple_library/test.lua b/tests/projects/csharp/multiple_library/test.lua
index e67becab3..d909316b7 100644
--- a/tests/projects/csharp/multiple_library/test.lua
+++ b/tests/projects/csharp/multiple_library/test.lua
@@ -1,6 +1,9 @@
import("detect.sdks.find_dotnet")
function test_build(t)
+ if is_subhost("msys") then
+ return t:skip("csharp not supported on msys")
+ end
local dotnet = find_dotnet()
if dotnet and dotnet.sdkver then
t:build()
diff --git a/tests/projects/csharp/web_project/test.lua b/tests/projects/csharp/web_project/test.lua
index e67becab3..d909316b7 100644
--- a/tests/projects/csharp/web_project/test.lua
+++ b/tests/projects/csharp/web_project/test.lua
@@ -1,6 +1,9 @@
import("detect.sdks.find_dotnet")
function test_build(t)
+ if is_subhost("msys") then
+ return t:skip("csharp not supported on msys")
+ end
local dotnet = find_dotnet()
if dotnet and dotnet.sdkver then
t:build()
--
cgit v1.3.1
From 3874b16f91e29868a4350b20d2c091af08284d44 Mon Sep 17 00:00:00 2001
From: ruki
Date: Tue, 17 Mar 2026 23:54:05 +0800
Subject: rename test files
---
tests/projects/csharp/exe_with_library/.gitignore | 6 ------
tests/projects/csharp/exe_with_library/src/app/Program.cs | 4 ----
tests/projects/csharp/exe_with_library/src/lib/Greeter.cs | 7 -------
tests/projects/csharp/exe_with_library/test.lua | 13 -------------
tests/projects/csharp/exe_with_library/xmake.lua | 12 ------------
tests/projects/csharp/exe_with_package/.gitignore | 6 ------
tests/projects/csharp/exe_with_package/src/Program.cs | 4 ----
tests/projects/csharp/exe_with_package/test.lua | 13 -------------
tests/projects/csharp/exe_with_package/xmake.lua | 8 --------
tests/projects/csharp/nuget_package/.gitignore | 6 ++++++
tests/projects/csharp/nuget_package/src/Program.cs | 4 ++++
tests/projects/csharp/nuget_package/test.lua | 13 +++++++++++++
tests/projects/csharp/nuget_package/xmake.lua | 8 ++++++++
tests/projects/csharp/shared_library/.gitignore | 6 ++++++
tests/projects/csharp/shared_library/src/app/Program.cs | 4 ++++
tests/projects/csharp/shared_library/src/lib/Greeter.cs | 7 +++++++
tests/projects/csharp/shared_library/test.lua | 13 +++++++++++++
tests/projects/csharp/shared_library/xmake.lua | 12 ++++++++++++
18 files changed, 73 insertions(+), 73 deletions(-)
delete mode 100644 tests/projects/csharp/exe_with_library/.gitignore
delete mode 100644 tests/projects/csharp/exe_with_library/src/app/Program.cs
delete mode 100644 tests/projects/csharp/exe_with_library/src/lib/Greeter.cs
delete mode 100644 tests/projects/csharp/exe_with_library/test.lua
delete mode 100644 tests/projects/csharp/exe_with_library/xmake.lua
delete mode 100644 tests/projects/csharp/exe_with_package/.gitignore
delete mode 100644 tests/projects/csharp/exe_with_package/src/Program.cs
delete mode 100644 tests/projects/csharp/exe_with_package/test.lua
delete mode 100644 tests/projects/csharp/exe_with_package/xmake.lua
create mode 100644 tests/projects/csharp/nuget_package/.gitignore
create mode 100644 tests/projects/csharp/nuget_package/src/Program.cs
create mode 100644 tests/projects/csharp/nuget_package/test.lua
create mode 100644 tests/projects/csharp/nuget_package/xmake.lua
create mode 100644 tests/projects/csharp/shared_library/.gitignore
create mode 100644 tests/projects/csharp/shared_library/src/app/Program.cs
create mode 100644 tests/projects/csharp/shared_library/src/lib/Greeter.cs
create mode 100644 tests/projects/csharp/shared_library/test.lua
create mode 100644 tests/projects/csharp/shared_library/xmake.lua
diff --git a/tests/projects/csharp/exe_with_library/.gitignore b/tests/projects/csharp/exe_with_library/.gitignore
deleted file mode 100644
index 6b8d3af38..000000000
--- a/tests/projects/csharp/exe_with_library/.gitignore
+++ /dev/null
@@ -1,6 +0,0 @@
-.xmake/
-build/
-bin/
-obj/
-vsxmake*/
-.vs/
diff --git a/tests/projects/csharp/exe_with_library/src/app/Program.cs b/tests/projects/csharp/exe_with_library/src/app/Program.cs
deleted file mode 100644
index 3cf7cb84a..000000000
--- a/tests/projects/csharp/exe_with_library/src/app/Program.cs
+++ /dev/null
@@ -1,4 +0,0 @@
-using MyLib;
-
-Console.WriteLine(Greeter.Greet("xmake"));
-
diff --git a/tests/projects/csharp/exe_with_library/src/lib/Greeter.cs b/tests/projects/csharp/exe_with_library/src/lib/Greeter.cs
deleted file mode 100644
index cf895138a..000000000
--- a/tests/projects/csharp/exe_with_library/src/lib/Greeter.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace MyLib;
-
-public static class Greeter {
- public static string Greet(string name) {
- return $"hello {name}!";
- }
-}
diff --git a/tests/projects/csharp/exe_with_library/test.lua b/tests/projects/csharp/exe_with_library/test.lua
deleted file mode 100644
index d909316b7..000000000
--- a/tests/projects/csharp/exe_with_library/test.lua
+++ /dev/null
@@ -1,13 +0,0 @@
-import("detect.sdks.find_dotnet")
-
-function test_build(t)
- if is_subhost("msys") then
- return t:skip("csharp not supported on msys")
- end
- local dotnet = find_dotnet()
- if dotnet and dotnet.sdkver then
- t:build()
- else
- return t:skip("dotnet sdk not found")
- end
-end
diff --git a/tests/projects/csharp/exe_with_library/xmake.lua b/tests/projects/csharp/exe_with_library/xmake.lua
deleted file mode 100644
index f8549444e..000000000
--- a/tests/projects/csharp/exe_with_library/xmake.lua
+++ /dev/null
@@ -1,12 +0,0 @@
-add_rules("mode.debug", "mode.release")
-
-target("mylib")
- set_kind("shared")
- add_rules("csharp")
- add_files("src/lib/*.cs")
-
-target("app")
- set_kind("binary")
- add_rules("csharp")
- add_deps("mylib")
- add_files("src/app/*.cs")
diff --git a/tests/projects/csharp/exe_with_package/.gitignore b/tests/projects/csharp/exe_with_package/.gitignore
deleted file mode 100644
index 6b8d3af38..000000000
--- a/tests/projects/csharp/exe_with_package/.gitignore
+++ /dev/null
@@ -1,6 +0,0 @@
-.xmake/
-build/
-bin/
-obj/
-vsxmake*/
-.vs/
diff --git a/tests/projects/csharp/exe_with_package/src/Program.cs b/tests/projects/csharp/exe_with_package/src/Program.cs
deleted file mode 100644
index 518015e2a..000000000
--- a/tests/projects/csharp/exe_with_package/src/Program.cs
+++ /dev/null
@@ -1,4 +0,0 @@
-using Humanizer;
-
-Console.WriteLine(1234.ToWords());
-
diff --git a/tests/projects/csharp/exe_with_package/test.lua b/tests/projects/csharp/exe_with_package/test.lua
deleted file mode 100644
index d909316b7..000000000
--- a/tests/projects/csharp/exe_with_package/test.lua
+++ /dev/null
@@ -1,13 +0,0 @@
-import("detect.sdks.find_dotnet")
-
-function test_build(t)
- if is_subhost("msys") then
- return t:skip("csharp not supported on msys")
- end
- local dotnet = find_dotnet()
- if dotnet and dotnet.sdkver then
- t:build()
- else
- return t:skip("dotnet sdk not found")
- end
-end
diff --git a/tests/projects/csharp/exe_with_package/xmake.lua b/tests/projects/csharp/exe_with_package/xmake.lua
deleted file mode 100644
index 6ec474822..000000000
--- a/tests/projects/csharp/exe_with_package/xmake.lua
+++ /dev/null
@@ -1,8 +0,0 @@
-add_rules("mode.debug", "mode.release")
-add_requires("nuget::Humanizer.Core 2.14.1")
-
-target("app")
- set_kind("binary")
- add_rules("csharp")
- add_files("src/Program.cs")
- add_packages("nuget::Humanizer.Core")
diff --git a/tests/projects/csharp/nuget_package/.gitignore b/tests/projects/csharp/nuget_package/.gitignore
new file mode 100644
index 000000000..6b8d3af38
--- /dev/null
+++ b/tests/projects/csharp/nuget_package/.gitignore
@@ -0,0 +1,6 @@
+.xmake/
+build/
+bin/
+obj/
+vsxmake*/
+.vs/
diff --git a/tests/projects/csharp/nuget_package/src/Program.cs b/tests/projects/csharp/nuget_package/src/Program.cs
new file mode 100644
index 000000000..518015e2a
--- /dev/null
+++ b/tests/projects/csharp/nuget_package/src/Program.cs
@@ -0,0 +1,4 @@
+using Humanizer;
+
+Console.WriteLine(1234.ToWords());
+
diff --git a/tests/projects/csharp/nuget_package/test.lua b/tests/projects/csharp/nuget_package/test.lua
new file mode 100644
index 000000000..d909316b7
--- /dev/null
+++ b/tests/projects/csharp/nuget_package/test.lua
@@ -0,0 +1,13 @@
+import("detect.sdks.find_dotnet")
+
+function test_build(t)
+ if is_subhost("msys") then
+ return t:skip("csharp not supported on msys")
+ end
+ local dotnet = find_dotnet()
+ if dotnet and dotnet.sdkver then
+ t:build()
+ else
+ return t:skip("dotnet sdk not found")
+ end
+end
diff --git a/tests/projects/csharp/nuget_package/xmake.lua b/tests/projects/csharp/nuget_package/xmake.lua
new file mode 100644
index 000000000..6ec474822
--- /dev/null
+++ b/tests/projects/csharp/nuget_package/xmake.lua
@@ -0,0 +1,8 @@
+add_rules("mode.debug", "mode.release")
+add_requires("nuget::Humanizer.Core 2.14.1")
+
+target("app")
+ set_kind("binary")
+ add_rules("csharp")
+ add_files("src/Program.cs")
+ add_packages("nuget::Humanizer.Core")
diff --git a/tests/projects/csharp/shared_library/.gitignore b/tests/projects/csharp/shared_library/.gitignore
new file mode 100644
index 000000000..6b8d3af38
--- /dev/null
+++ b/tests/projects/csharp/shared_library/.gitignore
@@ -0,0 +1,6 @@
+.xmake/
+build/
+bin/
+obj/
+vsxmake*/
+.vs/
diff --git a/tests/projects/csharp/shared_library/src/app/Program.cs b/tests/projects/csharp/shared_library/src/app/Program.cs
new file mode 100644
index 000000000..3cf7cb84a
--- /dev/null
+++ b/tests/projects/csharp/shared_library/src/app/Program.cs
@@ -0,0 +1,4 @@
+using MyLib;
+
+Console.WriteLine(Greeter.Greet("xmake"));
+
diff --git a/tests/projects/csharp/shared_library/src/lib/Greeter.cs b/tests/projects/csharp/shared_library/src/lib/Greeter.cs
new file mode 100644
index 000000000..cf895138a
--- /dev/null
+++ b/tests/projects/csharp/shared_library/src/lib/Greeter.cs
@@ -0,0 +1,7 @@
+namespace MyLib;
+
+public static class Greeter {
+ public static string Greet(string name) {
+ return $"hello {name}!";
+ }
+}
diff --git a/tests/projects/csharp/shared_library/test.lua b/tests/projects/csharp/shared_library/test.lua
new file mode 100644
index 000000000..d909316b7
--- /dev/null
+++ b/tests/projects/csharp/shared_library/test.lua
@@ -0,0 +1,13 @@
+import("detect.sdks.find_dotnet")
+
+function test_build(t)
+ if is_subhost("msys") then
+ return t:skip("csharp not supported on msys")
+ end
+ local dotnet = find_dotnet()
+ if dotnet and dotnet.sdkver then
+ t:build()
+ else
+ return t:skip("dotnet sdk not found")
+ end
+end
diff --git a/tests/projects/csharp/shared_library/xmake.lua b/tests/projects/csharp/shared_library/xmake.lua
new file mode 100644
index 000000000..f8549444e
--- /dev/null
+++ b/tests/projects/csharp/shared_library/xmake.lua
@@ -0,0 +1,12 @@
+add_rules("mode.debug", "mode.release")
+
+target("mylib")
+ set_kind("shared")
+ add_rules("csharp")
+ add_files("src/lib/*.cs")
+
+target("app")
+ set_kind("binary")
+ add_rules("csharp")
+ add_deps("mylib")
+ add_files("src/app/*.cs")
--
cgit v1.3.1
From b80b83826ccb9c711f5cd0100f45f51fbb3ab22d Mon Sep 17 00:00:00 2001
From: ruki
Date: Tue, 17 Mar 2026 23:58:07 +0800
Subject: fix build csharp for macos
---
xmake/rules/csharp/xmake.lua | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/xmake/rules/csharp/xmake.lua b/xmake/rules/csharp/xmake.lua
index 4030c7457..9a0a6e43b 100644
--- a/xmake/rules/csharp/xmake.lua
+++ b/xmake/rules/csharp/xmake.lua
@@ -114,6 +114,13 @@
--
rule("csharp.build")
set_sourcekinds("cs")
+ on_load(function (target)
+ -- dotnet always outputs .dll for libraries, and no prefix
+ if target:is_shared() or target:is_static() then
+ target:set("prefixname", "")
+ target:set("extension", ".dll")
+ end
+ end)
on_config("config")
on_build("build")
on_install("install")
--
cgit v1.3.1
From 2134b93d3f87d8f4ea63e0c49b2075ba056995d9 Mon Sep 17 00:00:00 2001
From: ruki
Date: Wed, 18 Mar 2026 00:00:37 +0800
Subject: fix file name
---
xmake/rules/csharp/generator/csproj.lua | 2 +-
xmake/rules/csharp/generator/itemgroups.lua | 2 +-
xmake/rules/csharp/generator/properties.lua | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/xmake/rules/csharp/generator/csproj.lua b/xmake/rules/csharp/generator/csproj.lua
index e28cbfc19..aa4e0434c 100644
--- a/xmake/rules/csharp/generator/csproj.lua
+++ b/xmake/rules/csharp/generator/csproj.lua
@@ -15,7 +15,7 @@
-- Copyright (C) 2015-present, Xmake Open Source Community.
--
-- @author JassJam
--- @file csproj_generator.lua
+-- @file csproj.lua
--
-- imports
diff --git a/xmake/rules/csharp/generator/itemgroups.lua b/xmake/rules/csharp/generator/itemgroups.lua
index 79f13b4fb..2c0528665 100644
--- a/xmake/rules/csharp/generator/itemgroups.lua
+++ b/xmake/rules/csharp/generator/itemgroups.lua
@@ -15,7 +15,7 @@
-- Copyright (C) 2015-present, Xmake Open Source Community.
--
-- @author JassJam
--- @file csproj_itemsgroups.lua
+-- @file itemgroups.lua
--
-- normalize path to relative and use forward slashes
diff --git a/xmake/rules/csharp/generator/properties.lua b/xmake/rules/csharp/generator/properties.lua
index 75d80e5a8..15702eae4 100644
--- a/xmake/rules/csharp/generator/properties.lua
+++ b/xmake/rules/csharp/generator/properties.lua
@@ -15,7 +15,7 @@
-- Copyright (C) 2015-present, Xmake Open Source Community.
--
-- @author JassJam
--- @file csproj_properties.lua
+-- @file properties.lua
--
-- check if target has multi-target frameworks set
--
cgit v1.3.1