From a92e7c4087443cd91dafe14d2a5aa316a288b7c5 Mon Sep 17 00:00:00 2001 From: JassJam <0139ne@gmail.com> Date: Sat, 28 Feb 2026 15:37:56 +0000 Subject: feat: generate csproj on demand --- xmake/rules/csharp/modules/properties.lua | 307 ++++++++++++++++++++++++++++++ 1 file changed, 307 insertions(+) create mode 100644 xmake/rules/csharp/modules/properties.lua (limited to 'xmake/rules/csharp/modules/properties.lua') diff --git a/xmake/rules/csharp/modules/properties.lua b/xmake/rules/csharp/modules/properties.lua new file mode 100644 index 000000000..b8d30f1e9 --- /dev/null +++ b/xmake/rules/csharp/modules/properties.lua @@ -0,0 +1,307 @@ +--!A cross-platform build utility based on Lua +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- Copyright (C) 2015-present, Xmake Open Source Community. +-- +-- @author JassJam +-- @file properties.lua +-- + +function _has_target_frameworks(context) + return #table.wrap(context.target:values("csharp.target_frameworks")) > 0 +end + +local _default_target_framework_cached = {} + +function _first(value) + if type(value) == "table" then + return value[1] + end + return value +end + +function _get_target_value(target, name) + return _first(target:values(name)) +end + +function _collect_strings(result, value) + if value == nil then + return + end + if type(value) == "table" then + for _, item in ipairs(value) do + _collect_strings(result, item) + end + else + local sval = tostring(value) + if #sval > 0 then + table.insert(result, sval) + end + end +end + +function _extract_define_symbol(define) + define = tostring(define):trim() + if #define == 0 then + return nil + end + local symbol = define:split("=", {plain = true})[1] + if not symbol then + return nil + end + symbol = symbol:trim() + if symbol:match("^[A-Za-z_][A-Za-z0-9_]*$") then + return symbol + end + return nil +end + +function _resolve_define_constants(context) + local target = context.target + local constants = {} + + _collect_strings(constants, target:get("defines")) + local opt_defines = target:get_from("defines", "option::*") + if opt_defines then + _collect_strings(constants, opt_defines) + end + + local symbols = {} + for _, define in ipairs(constants) do + local symbol = _extract_define_symbol(define) + if symbol then + table.insert(symbols, symbol) + end + end + symbols = table.unique(symbols) + if #symbols > 0 then + return table.concat(symbols, ";") + end +end + +function _get_default_target_framework(context) + local dotnet = _first(context.target:get("toolset.cs")) or "dotnet" + dotnet = tostring(dotnet) + local cached = _default_target_framework_cached[dotnet] + if cached ~= nil then + return cached + end + + local major = nil + local sdks = try { function () + return os.iorunv(dotnet, {"--list-sdks"}) + end } + if sdks then + for line in sdks:gmatch("[^\r\n]+") do + local line_major = tonumber(line:match("^%s*(%d+)%.%d+%.%d+")) + if line_major and (not major or line_major > major) then + major = line_major + end + end + end + if not major then + local version = try { function () + return os.iorunv(dotnet, {"--version"}) + end } + if version then + major = tonumber(version:match("^%s*(%d+)")) + end + end + + local target_framework = major and ("net" .. tostring(major) .. ".0") or "net8.0" + _default_target_framework_cached[dotnet] = target_framework + return target_framework +end + +function _resolve_target_framework(context) + local target_framework = _get_target_value(context.target, "csharp.target_framework") + if target_framework ~= nil and #tostring(target_framework) > 0 then + return target_framework + end + return _get_default_target_framework(context) +end + +function _resolve_assembly_name(context) + local basename = context.target:basename() + if basename ~= nil and #tostring(basename) > 0 then + return basename + end +end + +function _resolve_optimize(context) + local optimize = _first(context.target:get("optimize")) + if optimize ~= nil then + return optimize == "none" and "false" or "true" + end +end + +function _resolve_debug_symbols(context) + local symbols = _first(context.target:get("symbols")) + if symbols ~= nil then + return symbols == "none" and "false" or "true" + end +end + +function _resolve_debug_type(context) + local symbols = _first(context.target:get("symbols")) + if symbols and symbols ~= "none" then + return "portable" + end +end + +function _resolve_platform_target(context) + local arch = (context.target:arch() or ""):lower() + local mapping = { + x86_64 = "x64", + amd64 = "x64", + x64 = "x64", + i386 = "x86", + x86 = "x86", + arm64 = "arm64", + arm = "arm", + armv7 = "arm" + } + return mapping[arch] +end + +function _resolve_warning_level(context) + local warnings = _first(context.target:get("warnings")) + local mapping = { + none = "0", + less = "2", + more = "3", + all = "4", + allextra = "4", + everything = "4", + error = "4" + } + return warnings and mapping[warnings] +end + +function _resolve_treat_warnings_as_errors(context) + local warnings = _first(context.target:get("warnings")) + if warnings == "error" then + return "true" + end +end + +function _register_property(register, suffix, xml, default, extra) + local entry = table.join({ + kind = "property", + xml = xml, + lua_key = "csharp." .. suffix, + default = default + }, extra or {}) + register(entry) +end + +function _register_list_property(register, suffix, xml, extra) + local entry = table.join({ + kind = "property", + xml = xml, + lua_key = "csharp." .. suffix, + value_type = "list", + sep = ";" + }, extra or {}) + register(entry) +end + +function main() + local entries = {} + function register(entry) + table.insert(entries, entry) + end + + register({kind = "project_attribute", attr = "Sdk", lua_key = "csharp.sdk", default = "Microsoft.NET.Sdk"}) + register({kind = "property", xml = "OutputType", resolve = function (context) + return context.target:is_binary() and "Exe" or "Library" + end}) + _register_list_property(register, "target_frameworks", "TargetFrameworks", {when = _has_target_frameworks}) + register({kind = "property", xml = "TargetFramework", resolve = _resolve_target_framework, when = function (context) + return not _has_target_frameworks(context) + end}) + + _register_property(register, "implicit_usings", "ImplicitUsings", "enable") + _register_property(register, "nullable", "Nullable", "enable") + _register_property(register, "lang_version", "LangVersion") + _register_property(register, "enable_default_compile_items", "EnableDefaultCompileItems", "false") + _register_property(register, "enable_default_embedded_resource_items", "EnableDefaultEmbeddedResourceItems") + _register_property(register, "enable_default_none_items", "EnableDefaultNoneItems") + _register_property(register, "root_namespace", "RootNamespace") + register({kind = "property", xml = "AssemblyName", resolve = _resolve_assembly_name}) + _register_property(register, "generate_assembly_info", "GenerateAssemblyInfo") + _register_property(register, "deterministic", "Deterministic") + register({kind = "property", xml = "Optimize", resolve = _resolve_optimize}) + register({kind = "property", xml = "PlatformTarget", resolve = _resolve_platform_target}) + _register_property(register, "prefer_32bit", "Prefer32Bit") + _register_property(register, "allow_unsafe_blocks", "AllowUnsafeBlocks") + _register_property(register, "check_for_overflow_underflow", "CheckForOverflowUnderflow") + register({kind = "property", xml = "WarningLevel", resolve = _resolve_warning_level}) + _register_property(register, "analysis_level", "AnalysisLevel") + _register_property(register, "enable_net_analyzers", "EnableNETAnalyzers") + _register_property(register, "enforce_code_style_in_build", "EnforceCodeStyleInBuild") + register({kind = "property", xml = "TreatWarningsAsErrors", resolve = _resolve_treat_warnings_as_errors}) + _register_list_property(register, "warnings_as_errors", "WarningsAsErrors") + _register_list_property(register, "warnings_not_as_errors", "WarningsNotAsErrors") + register({kind = "property", xml = "DefineConstants", resolve = _resolve_define_constants}) + _register_property(register, "error_log", "ErrorLog") + register({kind = "property", xml = "DebugType", resolve = _resolve_debug_type}) + register({kind = "property", xml = "DebugSymbols", resolve = _resolve_debug_symbols}) + _register_property(register, "generate_documentation_file", "GenerateDocumentationFile") + _register_property(register, "documentation_file", "DocumentationFile") + + _register_property(register, "runtime_identifier", "RuntimeIdentifier") + _register_list_property(register, "runtime_identifiers", "RuntimeIdentifiers") + _register_property(register, "self_contained", "SelfContained") + _register_property(register, "use_app_host", "UseAppHost") + _register_property(register, "roll_forward", "RollForward") + _register_property(register, "publish_single_file", "PublishSingleFile") + _register_property(register, "publish_trimmed", "PublishTrimmed") + _register_property(register, "trim_mode", "TrimMode") + _register_property(register, "publish_ready_to_run", "PublishReadyToRun") + _register_property(register, "invariant_globalization", "InvariantGlobalization") + _register_property(register, "include_native_libraries_for_self_extract", "IncludeNativeLibrariesForSelfExtract") + _register_property(register, "enable_compression_in_single_file", "EnableCompressionInSingleFile") + _register_property(register, "publish_aot", "PublishAot") + _register_property(register, "strip_symbols", "StripSymbols") + _register_property(register, "enable_trim_analyzer", "EnableTrimAnalyzer") + _register_property(register, "json_serializer_is_reflection_enabled_by_default", "JsonSerializerIsReflectionEnabledByDefault") + _register_list_property(register, "satellite_resource_languages", "SatelliteResourceLanguages") + + _register_property(register, "version", "Version") + _register_property(register, "assembly_version", "AssemblyVersion") + _register_property(register, "file_version", "FileVersion") + _register_property(register, "informational_version", "InformationalVersion") + _register_property(register, "package_id", "PackageId") + _register_property(register, "authors", "Authors") + _register_property(register, "company", "Company") + _register_property(register, "product", "Product") + _register_property(register, "description", "Description") + _register_property(register, "copyright", "Copyright") + _register_property(register, "repository_url", "RepositoryUrl") + _register_property(register, "repository_type", "RepositoryType") + _register_property(register, "package_license_expression", "PackageLicenseExpression") + _register_property(register, "package_project_url", "PackageProjectUrl") + _register_property(register, "neutral_language", "NeutralLanguage") + _register_property(register, "enable_preview_features", "EnablePreviewFeatures") + + _register_property(register, "generate_runtime_configuration_files", "GenerateRuntimeConfigurationFiles") + _register_property(register, "copy_local_lock_file_assemblies", "CopyLocalLockFileAssemblies") + _register_property(register, "append_target_framework_to_output_path", "AppendTargetFrameworkToOutputPath", "false") + _register_property(register, "append_runtime_identifier_to_output_path", "AppendRuntimeIdentifierToOutputPath", "false") + _register_property(register, "produce_reference_assembly", "ProduceReferenceAssembly") + _register_property(register, "disable_implicit_framework_references", "DisableImplicitFrameworkReferences") + _register_property(register, "generate_target_framework_attribute", "GenerateTargetFrameworkAttribute") + return entries +end -- cgit v1.3.1