summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tests/projects/csharp/console/.gitignore6
-rw-r--r--tests/projects/csharp/console/src/Program.cs2
-rw-r--r--tests/projects/csharp/console/test.lua13
-rw-r--r--tests/projects/csharp/console/xmake.lua6
-rw-r--r--tests/projects/csharp/console_with_runtime_json/.gitignore7
-rw-r--r--tests/projects/csharp/console_with_runtime_json/src/Program.cs15
-rw-r--r--tests/projects/csharp/console_with_runtime_json/src/runtime.json7
-rw-r--r--tests/projects/csharp/console_with_runtime_json/test.lua13
-rw-r--r--tests/projects/csharp/console_with_runtime_json/xmake.lua7
-rw-r--r--tests/projects/csharp/multiple_library/.gitignore6
-rw-r--r--tests/projects/csharp/multiple_library/src/libalpha/Alpha.cs7
-rw-r--r--tests/projects/csharp/multiple_library/src/libbeta/Beta.cs9
-rw-r--r--tests/projects/csharp/multiple_library/src/sample/Program.cs3
-rw-r--r--tests/projects/csharp/multiple_library/test.lua13
-rw-r--r--tests/projects/csharp/multiple_library/xmake.lua18
-rw-r--r--tests/projects/csharp/nuget_package/.gitignore6
-rw-r--r--tests/projects/csharp/nuget_package/src/Program.cs4
-rw-r--r--tests/projects/csharp/nuget_package/test.lua13
-rw-r--r--tests/projects/csharp/nuget_package/xmake.lua8
-rw-r--r--tests/projects/csharp/shared_library/.gitignore6
-rw-r--r--tests/projects/csharp/shared_library/src/app/Program.cs4
-rw-r--r--tests/projects/csharp/shared_library/src/lib/Greeter.cs7
-rw-r--r--tests/projects/csharp/shared_library/test.lua13
-rw-r--r--tests/projects/csharp/shared_library/xmake.lua12
-rw-r--r--tests/projects/csharp/web_project/.gitignore7
-rw-r--r--tests/projects/csharp/web_project/src/Program.cs8
-rw-r--r--tests/projects/csharp/web_project/test.lua13
-rw-r--r--tests/projects/csharp/web_project/xmake.lua7
-rw-r--r--xmake/core/tool/compiler.lua83
-rw-r--r--xmake/languages/csharp/load.lua49
-rw-r--r--xmake/languages/csharp/xmake.lua74
-rw-r--r--xmake/modules/core/tools/dotnet.lua126
-rw-r--r--xmake/modules/core/tools/dotnet/has_flags.lua50
-rw-r--r--xmake/modules/detect/sdks/find_dotnet.lua149
-rw-r--r--xmake/modules/detect/tools/find_dotnet.lua53
-rw-r--r--xmake/modules/package/manager/nuget/find_package.lua182
-rw-r--r--xmake/modules/target/action/install/main.lua2
-rw-r--r--xmake/platforms/bsd/xmake.lua2
-rw-r--r--xmake/platforms/linux/xmake.lua2
-rw-r--r--xmake/platforms/macosx/xmake.lua2
-rw-r--r--xmake/platforms/windows/xmake.lua2
-rw-r--r--xmake/repository/templates/csharp/console/src/Program.cs9
-rw-r--r--xmake/repository/templates/csharp/console/xmake.lua7
-rw-r--r--xmake/repository/templates/csharp/shared/src/foo.cs7
-rw-r--r--xmake/repository/templates/csharp/shared/src/main.cs7
-rw-r--r--xmake/repository/templates/csharp/shared/xmake.lua10
-rw-r--r--xmake/repository/templates/csharp/static/src/foo.cs7
-rw-r--r--xmake/repository/templates/csharp/static/src/main.cs7
-rw-r--r--xmake/repository/templates/csharp/static/xmake.lua10
-rw-r--r--xmake/rules/csharp/build.lua86
-rw-r--r--xmake/rules/csharp/config.lua47
-rw-r--r--xmake/rules/csharp/generator/csproj.lua257
-rw-r--r--xmake/rules/csharp/generator/itemgroups.lua163
-rw-r--r--xmake/rules/csharp/generator/properties.lua174
-rw-r--r--xmake/rules/csharp/install.lua80
-rw-r--r--xmake/rules/csharp/installcmd.lua83
-rw-r--r--xmake/rules/csharp/xmake.lua131
-rw-r--r--xmake/toolchains/dotnet/xmake.lua65
58 files changed, 1997 insertions, 169 deletions
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/test.lua b/tests/projects/csharp/console/test.lua
new file mode 100644
index 000000000..d909316b7
--- /dev/null
+++ b/tests/projects/csharp/console/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/console/xmake.lua b/tests/projects/csharp/console/xmake.lua
new file mode 100644
index 000000000..3288c96a8
--- /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")
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..fd836a339
--- /dev/null
+++ b/tests/projects/csharp/console_with_runtime_json/src/Program.cs
@@ -0,0 +1,15 @@
+using System.Text.Json;
+
+var runtimeFile = Path.Combine(Directory.GetCurrentDirectory(), "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/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..d909316b7
--- /dev/null
+++ b/tests/projects/csharp/console_with_runtime_json/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/console_with_runtime_json/xmake.lua b/tests/projects/csharp/console_with_runtime_json/xmake.lua
new file mode 100644
index 000000000..979f74307
--- /dev/null
+++ b/tests/projects/csharp/console_with_runtime_json/xmake.lua
@@ -0,0 +1,7 @@
+add_rules("mode.debug", "mode.release")
+
+target("app")
+ set_kind("binary")
+ add_rules("csharp")
+ add_files("src/Program.cs")
+ set_rundir("src")
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..a10ed6d73
--- /dev/null
+++ b/tests/projects/csharp/multiple_library/src/libalpha/Alpha.cs
@@ -0,0 +1,7 @@
+namespace LibAlpha;
+
+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
new file mode 100644
index 000000000..c96944dc5
--- /dev/null
+++ b/tests/projects/csharp/multiple_library/src/libbeta/Beta.cs
@@ -0,0 +1,9 @@
+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/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/test.lua b/tests/projects/csharp/multiple_library/test.lua
new file mode 100644
index 000000000..d909316b7
--- /dev/null
+++ b/tests/projects/csharp/multiple_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/multiple_library/xmake.lua b/tests/projects/csharp/multiple_library/xmake.lua
new file mode 100644
index 000000000..db3f65811
--- /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")
+
+target("libbeta")
+ set_kind("shared")
+ add_rules("csharp")
+ add_deps("libalpha")
+ add_files("src/libbeta/*.cs")
+
+target("sample")
+ set_kind("binary")
+ add_rules("csharp")
+ add_deps("libbeta")
+ add_files("src/sample/*.cs")
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")
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/test.lua b/tests/projects/csharp/web_project/test.lua
new file mode 100644
index 000000000..d909316b7
--- /dev/null
+++ b/tests/projects/csharp/web_project/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/web_project/xmake.lua b/tests/projects/csharp/web_project/xmake.lua
new file mode 100644
index 000000000..56bcad79c
--- /dev/null
+++ b/tests/projects/csharp/web_project/xmake.lua
@@ -0,0 +1,7 @@
+add_rules("mode.debug", "mode.release")
+
+target("webapp")
+ set_kind("binary")
+ add_rules("csharp")
+ add_values("csharp.sdk", "Microsoft.NET.Sdk.Web")
+ add_files("src/Program.cs")
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
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..9c2c0ce22
--- /dev/null
+++ b/xmake/languages/csharp/xmake.lua
@@ -0,0 +1,74 @@
+--!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"}
+ 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 = {
+ "config.linkdirs"
+ , "target.linkdirs"
+ , "config.links"
+ , "target.links"
+ , "config.syslinks"
+ , "target.syslinks"
+ }
+ , shared = {
+ "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" }
+ }
+ }
+
diff --git a/xmake/modules/core/tools/dotnet.lua b/xmake/modules/core/tools/dotnet.lua
new file mode 100644
index 000000000..9a502b616
--- /dev/null
+++ b/xmake/modules/core/tools/dotnet.lua
@@ -0,0 +1,126 @@
+--!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"}
+ }
+ 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
+
+-- get the .csproj file from source files
+function _get_csprojfile(opt)
+ local target = opt and opt.target
+ if target then
+ local csprojfile = target:data("csharp.csproj")
+ if csprojfile then
+ return csprojfile
+ 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, opt)
+ local argv = {"build"}
+
+ -- add .csproj file
+ local csprojfile = _get_csprojfile(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
+ return self:program(), argv
+end
+
+-- build the target file
+function build(self, sourcefiles, targetkind, targetfile, flags, opt)
+ os.mkdir(path.directory(targetfile))
+ local program, argv = buildargv(self, sourcefiles, targetkind, targetfile, flags, opt)
+ 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..f64acacd3 100644
--- a/xmake/modules/detect/sdks/find_dotnet.lua
+++ b/xmake/modules/detect/sdks/find_dotnet.lua
@@ -20,16 +20,73 @@
-- 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(sdkdir)
- -- get sdk directory from vcvars
+ -- 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 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 }
+ 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
+
+ -- 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
+ 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 +99,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
--
@@ -100,30 +142,51 @@ 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
- local dotnet = _find_dotnet(sdkdir or config.get("dotnet") or global.get("dotnet"), opt.version or config.get("dotnet_sdkver"))
+ -- find dotnet cli sdk
+ 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
+ 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/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
+ local packagesdir = _get_packagesdir_from_manifest(manifest)
+ if not manifest.libraries then
+ return
end
- local target_root
- if targets then
- for k, v in pairs(targets) do
- if k:startswith(name) then
- target_root = k
- break
+
+ 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
end
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)
- end
- return result
+
+ 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
-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
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/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 =
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/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}
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/config.lua b/xmake/rules/csharp/config.lua
new file mode 100644
index 000000000..3f6b71844
--- /dev/null
+++ b/xmake/rules/csharp/config.lua
@@ -0,0 +1,47 @@
+--!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.depend")
+import("generator.csproj", {rootdir = os.scriptdir(), alias = "generate_csproj"})
+
+function main(target)
+
+ -- compute csproj path
+ 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
+ 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 ()
+ generate_csproj(target, csprojfile)
+ end, {dependfile = dependfile, files = sourcefiles, values = depcsproj})
+
+ target:data_set("csharp.csproj", csprojfile)
+end
diff --git a/xmake/rules/csharp/generator/csproj.lua b/xmake/rules/csharp/generator/csproj.lua
new file mode 100644
index 000000000..aa4e0434c
--- /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.lua
+--
+
+-- imports
+import("properties")
+import("itemgroups")
+
+-- escape special xml characters
+function _xml_escape(value)
+ value = tostring(value or "")
+ value = value:gsub("&", "&")
+ value = value:gsub("<", "&lt;")
+ value = value:gsub(">", "&gt;")
+ value = value:gsub("\"", "&quot;")
+ value = value:gsub("'", "&apos;")
+ 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 <Project> 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 <PropertyGroup> 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 <ItemGroup> 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 <PropertyGroup> section to file
+function _render_property_group(file, entries)
+ if #entries == 0 then
+ return
+ end
+ file:print(" <PropertyGroup>")
+ for _, entry in ipairs(entries) do
+ file:print(" <%s>%s</%s>", entry.xml, _xml_escape(entry.value), entry.xml)
+ end
+ file:print(" </PropertyGroup>")
+end
+
+-- render <ItemGroup> sections to file
+function _render_item_groups(file, item_groups)
+ for _, group in ipairs(item_groups) do
+ if #group.items > 0 then
+ file:print(" <ItemGroup>")
+ for _, item in ipairs(group.items) do
+ local attrs = _format_attributes(item.attrs)
+ if item.value ~= nil and item.value ~= "" then
+ file:print(" <%s%s>%s</%s>", item.xml, attrs, _xml_escape(item.value), item.xml)
+ else
+ file:print(" <%s%s />", item.xml, attrs)
+ end
+ end
+ file:print(" </ItemGroup>")
+ 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("<Project%s>", _format_attributes(project_attributes))
+ _render_property_group(file, property_entries)
+ _render_item_groups(file, item_groups)
+ file:print("</Project>")
+ 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..2c0528665
--- /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 itemgroups.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..15702eae4
--- /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 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/install.lua b/xmake/rules/csharp/install.lua
new file mode 100644
index 000000000..03e2cd24d
--- /dev/null
+++ b/xmake/rules/csharp/install.lua
@@ -0,0 +1,80 @@
+--!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 based on target kind
+-- on windows, shared libraries (dll) should also go to bindir
+function _get_outputdir(target)
+ if target:is_binary() or (target:is_shared() and target:is_plat("windows", "mingw")) 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",
+ "--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..25de92989
--- /dev/null
+++ b/xmake/rules/csharp/installcmd.lua
@@ -0,0 +1,83 @@
+--!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
+ -- on windows, shared libraries (dll) should also go to bindir
+ local outputdir
+ if target:is_binary() or (target:is_shared() and target:is_plat("windows", "mingw")) 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",
+ "--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
new file mode 100644
index 000000000..9a0a6e43b
--- /dev/null
+++ b/xmake/rules/csharp/xmake.lua
@@ -0,0 +1,131 @@
+--!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
+--
+
+-- 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 - add custom <PropertyGroup> entries, format: "Name=Value"
+-- e.g. set_values("csharp.properties", "MyProp=value", "AnotherProp=value2")
+--
+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")
+ on_installcmd("installcmd")
+
+rule("csharp")
+ add_deps("csharp.build")
+ add_deps("utils.inherit.links")
diff --git a/xmake/toolchains/dotnet/xmake.lua b/xmake/toolchains/dotnet/xmake.lua
new file mode 100644
index 000000000..e26136a0a
--- /dev/null
+++ b/xmake/toolchains/dotnet/xmake.lua
@@ -0,0 +1,65 @@
+--!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_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("detect.sdks.find_dotnet")
+
+ -- 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
+
+ -- 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)
+ if sdkinfo.sdkver then
+ toolchain:config_set("sdkver", sdkinfo.sdkver)
+ end
+ return true
+ end)
+
+ on_load(function (toolchain)
+
+ -- 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)