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", 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", 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", entry.xml, _xml_escape(entry.value), entry.xml)) + file:print(" <%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", item.xml, attrs, _xml_escape(item.value), item.xml)) + file:print(" <%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", 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", 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", 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", 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