summaryrefslogtreecommitdiff
path: root/xmake/rules
diff options
context:
space:
mode:
Diffstat (limited to 'xmake/rules')
-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
8 files changed, 1021 insertions, 0 deletions
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")