Compare commits

...

15 Commits

Author SHA1 Message Date
c78907aeeb test commit 2025-05-21 16:22:20 +01:00
1d5b4a4ab7 add: README 2025-03-07 16:31:40 +00:00
1dc11c5f76 update: upgrade to godot 4.4.0 2025-03-04 12:26:15 +00:00
7e6b950fee add: Patchable items and frames 2025-03-04 11:58:14 +00:00
f32feecd4f add: Template Define 2025-02-22 03:56:27 +00:00
b03b10b4ed improve: clean up 2025-02-20 10:42:46 +00:00
607d5cb6a5 fix: passthrough should point to polonium registry 2025-02-20 09:31:11 +00:00
f70cab525b add: registry pass through 2025-02-19 17:09:07 +00:00
cadb3e02ac improve: better version control 2025-02-18 21:22:47 +00:00
3a08d8ae16 fix: missing type 2025-02-18 16:05:37 +00:00
b4fabd01bb Merge pull request 'draft_texture_button' (#1) from draft_texture_button into master
Reviewed-on: #1
2025-02-18 14:30:12 +00:00
dce78db64c add: sdk 2025-02-18 11:41:05 +00:00
4d3d06f8d1 refactor: redesign project structures 2025-02-17 02:49:21 +00:00
f69ceb5f4d refactor: redesign project structure 2025-02-16 22:36:38 +00:00
44099d41d0 draft: texture button 2025-02-15 08:25:01 +00:00
11 changed files with 366 additions and 91 deletions

8
.gitignore vendored
View File

@@ -2,4 +2,10 @@ bin/
obj/
/packages/
riderModule.iml
/_ReSharper.Caches/
/_ReSharper.Caches/
/summerizer.py
/NuGet.config
/Polonium.Tasks.sln.DotSettings.user
/VersionInfo.props
.idea/
/summerizer

View File

@@ -1,13 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Rider ignored files
/.idea.Polonium.Tasks.iml
/modules.xml
/projectSettingsUpdater.xml
/contentModel.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@@ -1,11 +1,19 @@
<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<CustomTasksFolder>$(MSBuildThisFileDirectory)tasks/netstandard2.0/</CustomTasksFolder>
<CustomTasksAssembly>$(CustomTasksFolder)$(MSBuildThisFileName).dll</CustomTasksAssembly>
<TasksFolder>$(MSBuildThisFileDirectory)tasks/netstandard2.0/</TasksFolder>
<TasksAssembly>$(TasksFolder)$(MSBuildThisFileName).dll</TasksAssembly>
</PropertyGroup>
<UsingTask
TaskName="GenerateProxyNodesTask"
AssemblyFile="$(TasksAssembly)"
/>
<UsingTask
TaskName="GenerateTextureSetTask"
AssemblyFile="$(TasksAssembly)"
/>
<UsingTask
TaskName="GenerateProxyNodesTask"
AssemblyFile="$(CustomTasksAssembly)"
TaskName="GenerateRegistryPassThrough"
AssemblyFile="$(TasksAssembly)"
/>
</Project>

View File

@@ -1,3 +1,6 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="CheckTasksAssembly" BeforeTargets="CoreCompile">
<Message Text="TasksAssembly: $(TasksAssembly)" Importance="High" />
</Target>
</Project>

View File

@@ -1,5 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="VersionInfo.props" Condition="Exists('VersionInfo.props')" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<IsPackable>true</IsPackable>
@@ -8,7 +8,7 @@
<RootNamespace>Polonium.Tasks</RootNamespace>
<AssemblyName>Polonium.Tasks</AssemblyName>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>0.1.1-x</Version>
<Version>$(PoloniumTasksVersion)</Version>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<GenerateDependencyFile>true</GenerateDependencyFile>
<TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
@@ -18,53 +18,22 @@
</PropertyGroup>
<ItemGroup>
<PackageReference
Include="Microsoft.Build"
Version="17.12.6"
PrivateAssets="all"
/>
<PackageReference
Include="Microsoft.Build.Tasks.Core"
Version="17.12.6"
PrivateAssets="all"
/>
<PackageReference
Include="Microsoft.CodeAnalysis"
Version="4.12.0"
PrivateAssets="all"
/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build" Version="17.12.6" PrivateAssets="all" />
<PackageReference Include="Microsoft.Build.Tasks.Core" Version="17.12.6" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis" Version="4.12.0" PrivateAssets="all" />
<EmbeddedResource Update="Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Compile Update="Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<AdditionalFiles Remove="AnalyzerReleases.Shipped.md" />
<AdditionalFiles Remove="AnalyzerReleases.Unshipped.md" />
</ItemGroup>
<ItemGroup>
<None
Include="Package\build\$(AssemblyName).targets"
Pack="true"
PackagePath="build"
/>
<None
Include="Package\build\$(AssemblyName).props"
Pack="true"
PackagePath="build"
/>
<AdditionalFiles Remove="AnalyzerReleases.Shipped.md" />
<AdditionalFiles Remove="AnalyzerReleases.Unshipped.md" />
<None Include="Package/build/**/*" Pack="true" PackagePath="build" />
<None Include="VersionInfo.props" Condition="Exists('VersionInfo.props')" Pack="true" PackagePath="build" />
</ItemGroup>
<Target Name="CopyProjectReferencesToPackage" DependsOnTargets="ResolveReferences">
<ItemGroup>
@@ -75,15 +44,15 @@
</ItemGroup>
</Target>
<Target
Name="AddBuildDependencyFileToBuiltProjectOutputGroupOutput"
BeforeTargets="BuiltProjectOutputGroup"
Name="AddBuildDependencyFileToBuiltProjectOutputGroupOutput"
BeforeTargets="BuiltProjectOutputGroup"
Condition="'$(GenerateDependencyFile)' == 'true'"
>
<ItemGroup>
<BuiltProjectOutputGroupOutput
Include="$(ProjectDepsFilePath)"
TargetPath="$(ProjectDepsFileName)"
FinalOutputPath="$(ProjectDepsFilePath)"
<BuiltProjectOutputGroupOutput
Include="$(ProjectDepsFilePath)"
TargetPath="$(ProjectDepsFileName)"
FinalOutputPath="$(ProjectDepsFilePath)"
/>
</ItemGroup>
</Target>
@@ -95,15 +64,9 @@
</Target>
<Target Name="CopyPackageToLocalFeed" AfterTargets="Pack">
<Message Text="Executing Copy Pack Task" Importance="high" />
<Message Text="OutputPath: $(OutputPath)" Importance="high" />
<ItemGroup>
<NuGetPackages Include="$(OutputPath)../*.nupkg" />
</ItemGroup>
<Copy SourceFiles="@(NuGetPackages)" DestinationFolder="/NuGetFeed"/>
</Target>
<Target Name="RestoreNoCache" BeforeTargets="Build">
<Message Text="Restoring packages with no chache" Importance="high"/>
<Exec Command="dotnet restore --no-cache"/>
</Target>
</Project>

33
README.md Normal file
View File

@@ -0,0 +1,33 @@
# Polonium.Tasks
**Project Description**
Polonium.Tasks is a set of MSBuild tasks created to support Godot C# projects using the Polonium library. It streamlines building, cleaning, packaging, and code generation steps by automating operations around scripts, templates, and resources.
**Key Features**
- **GenerateProxyNodesTask**: Examines source directories and generates script proxies based on `[ProxyNode]` in Polonium, should not be used manually in custom project.
- **GenerateRegistryPassThrough**: Produces bridging files for `[RegistryPassThrough]` attributes in Polonium, should not be used manually in custom project
- **GenerateTextureSetTask**: Helps build and bundle multiple textures (e.g., button states) into a coherent set.
- **Custom Build Workflows**: Extends typical MSBuild processes to manage Godot-specific tasks.
**Installation**
1. Add the package to your project via `.csproj`:
```xml
<PackageReference Include="Polonium.Tasks" Version="x.x.x" />
```
2. Restore the packages and build your project.
**Usage Example**
```xml
<Target Name="Prepare" BeforeTargets="BeforeBuild">
<GenerateProxyNodesTask
SourceDirectory="$(ProjectDir)Package/embedded/GlobalClasses"
TemplateDirectory="$(ProjectDir)Package/embedded/polonium_templates"
AttributeName="ProxyNode"
/>
</Target>
```
When you run a build, these tasks will automatically generate or update relevant files, saving you manual overhead.
**License**
This project is licensed under the [MIT License](https://opensource.org/licenses/MIT).

12
publish Normal file → Executable file
View File

@@ -1,2 +1,12 @@
#!/bin/bash
dotnet nuget push "$(ls -t ./bin/Debug/Polonium.Tasks.*.nupkg | head -n 1)" --source hangman-lab
SCRIPT_DIR=$(dirname "$(realpath "${BASH_SOURCE[0]}")")/
LATEST_PACKAGE=$(ls -t "${SCRIPT_DIR}"bin/Debug/Polonium.Tasks.*.nupkg 2>/dev/null | head -n 1)
if [[ -z "$LATEST_PACKAGE" ]]; then
echo "❌ Error: No .nupkg file found in ${SCRIPT_DIR}/bin/Debug/"
exit 1
fi
echo "🚀 Pushing NuGet package: $LATEST_PACKAGE"
dotnet nuget push "$LATEST_PACKAGE" --source hangman-lab --skip-duplicate

82
src/PoloniumTask.cs Normal file
View File

@@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Build.Utilities;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Polonium.Tasks;
public abstract class PoloniumTask : Task
{
protected static bool HasGetterOnly(PropertyDeclarationSyntax property)
{
var registryAttribute = property.AttributeLists
.SelectMany(a => a.Attributes)
.FirstOrDefault(a => a.Name.ToString() == "RegistryPassThrough");
if (registryAttribute?.ArgumentList != null)
{
foreach (var argument in registryAttribute.ArgumentList.Arguments)
{
if (argument.Expression is LiteralExpressionSyntax literal)
{
if (literal.Kind() == SyntaxKind.TrueLiteralExpression)
return true;
if (literal.Kind() == SyntaxKind.FalseLiteralExpression)
return false;
}
}
}
return false;
}
protected static string GetDisplayName(ClassDeclarationSyntax cls)
{
string name = cls.Identifier.Text;
SyntaxNode node = cls.Parent;
while (node is not null)
{
if(node is NamespaceDeclarationSyntax ns)
name = ns.Name.ToString() + "." + name;
else if (node is FileScopedNamespaceDeclarationSyntax fs)
name = fs.Name.ToString() + "." + name;
node = node.Parent;
}
return name;
}
protected static string GetRelativePath(string basePath, string fullPath)
{
Uri baseUri = new Uri(basePath.EndsWith(Path.DirectorySeparatorChar.ToString()) ? basePath : basePath + Path.DirectorySeparatorChar);
Uri fullUri = new Uri(fullPath);
return Uri.UnescapeDataString(baseUri.MakeRelativeUri(fullUri).ToString().Replace('/', Path.DirectorySeparatorChar));
}
protected static string GetDisplayName(TypeSyntax type)
{
if (type is IdentifierNameSyntax identifierName)
return identifierName.Identifier.Text;
if (type is QualifiedNameSyntax qualifiedName)
return GetDisplayName(qualifiedName.Left) + "." + GetDisplayName(qualifiedName.Right);
if (type is GenericNameSyntax genericName)
{
string typeName = genericName.Identifier.Text;
string typeArguments = string.Join(", ", genericName.TypeArgumentList.Arguments.Select(GetDisplayName));
return $"{typeName}<{typeArguments}>";
}
if (type is AliasQualifiedNameSyntax aliasQualifiedName)
return $"{aliasQualifiedName.Alias.Identifier.Text}::{GetDisplayName(aliasQualifiedName.Name)}";
if (type is PredefinedTypeSyntax predefinedType)
return predefinedType.Keyword.Text;
return type.ToString();
}
protected static IEnumerable<string> GetUsings(string code)
{
SyntaxTree tree = CSharpSyntaxTree.ParseText(code);
CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
return root.Usings
.Select(u => u.ToString());
}
}

View File

@@ -1,17 +1,16 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Polonium.Tasks;
using Polonium.Tasks;
public class GenerateProxyNodesTask : Task
// ReSharper disable once CheckNamespace
public class GenerateProxyNodesTask : PoloniumTask
{
[Required]
public string SourceDirectory { get; set; }
@@ -47,7 +46,7 @@ public class GenerateProxyNodesTask : Task
.AppendLine("// meta-default: true")
.AppendLine("using _BINDINGS_NAMESPACE_;")
.AppendLine("using System;")
.AppendLine("public partial class _CLASS_ : GlobalClasses._CLASS_")
.AppendLine($"public partial class _CLASS_ : {GetDisplayName(cls)}")
.AppendLine("{");
IEnumerable<MethodDeclarationSyntax> methods = cls.Members
.OfType<MethodDeclarationSyntax>()
@@ -55,6 +54,17 @@ public class GenerateProxyNodesTask : Task
.SelectMany(a => a.Attributes)
.Any(attr => attr.Name.ToString().Contains("ProxyMethod"))
);
IEnumerable<PropertyDeclarationSyntax> properties = cls.Members
.OfType<PropertyDeclarationSyntax>()
.Where(m => m.AttributeLists
.SelectMany(a => a.Attributes)
.Any(attr => attr.Name.ToString().Contains("ProxyProperty"))
);
foreach (PropertyDeclarationSyntax prop in properties)
{
sbx.AppendLine($" public override {prop.Type.ToString()} {prop.Identifier.ToString()} => base.{prop.Identifier.ToString()};");
}
foreach (MethodDeclarationSyntax proxyMethod in methods)
{
string methodReturnType = proxyMethod.ReturnType.ToString();
@@ -87,19 +97,6 @@ public class GenerateProxyNodesTask : Task
}
}
private string GetDisplayName(ClassDeclarationSyntax cls)
{
string name = cls.Identifier.Text;
SyntaxNode node = cls.Parent;
while (node is not null)
{
if(node is NamespaceDeclarationSyntax ns)
name = ns.Name.ToString() + "." + name;
else if (node is FileScopedNamespaceDeclarationSyntax fs)
name = fs.Name.ToString() + "." + name;
node = node.Parent;
}
return name;
}
}

View File

@@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.Build.Framework;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Polonium.Tasks;
// ReSharper disable once CheckNamespace
public class GenerateRegistryPassThrough : PoloniumTask
{
[Required] public string ProjectDir { get; set; }
public override bool Execute()
{
try
{
StringBuilder sb = new();
string csFile = Directory
.GetFiles($"{ProjectDir}src", "PoloniumRegistry.cs", SearchOption.TopDirectoryOnly)
.FirstOrDefault();
string code = File.ReadAllText(csFile!);
sb.AppendLine("#pragma warning disable IDE0130");
foreach (string use in GetUsings(code))
sb.AppendLine(use);
sb
.AppendLine("using System.Collections.Generic;")
.AppendLine("// ReSharper disable once CheckNamespace")
.AppendLine("public static partial class GlobalRegistry")
.AppendLine("{");
SyntaxTree tree = CSharpSyntaxTree.ParseText(code);
CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
ClassDeclarationSyntax clas = root
.DescendantNodes()
.OfType<ClassDeclarationSyntax>()
.FirstOrDefault(cls => cls.Identifier.Text == "PoloniumRegistry");
IEnumerable<PropertyDeclarationSyntax> properties = clas?.Members
.OfType<PropertyDeclarationSyntax>()
.Where(m => m.AttributeLists
.SelectMany(a => a.Attributes)
.Any(a => a.Name.ToString() == "RegistryPassThrough"));
if (properties is null)
return false;
foreach (PropertyDeclarationSyntax property in properties)
{
if (HasGetterOnly(property))
sb.AppendLine($" public static {GetDisplayName(property.Type)} {property.Identifier.ToString()} => PoloniumRegistry.{property.Identifier.ValueText};");
else
{
sb
.AppendLine($" public static {GetDisplayName(property.Type)} {property.Identifier.ToString()}")
.AppendLine(" {")
.AppendLine($" get => PoloniumRegistry.{property.Identifier.ValueText};")
.AppendLine($" set => PoloniumRegistry.{property.Identifier.ValueText} = value;")
.AppendLine(" }");
}
}
sb
.AppendLine("}")
.AppendLine("#pragma warning restore IDE0130");
File.WriteAllText($"{ProjectDir}Package/embedded/Patches/RegistryPassThrough.p.cs", sb.ToString());
return true;
}
catch (Exception e)
{
Log.LogErrorFromException(e);
return false;
}
}
}

View File

@@ -0,0 +1,108 @@
using System;
using System.IO;
using System.Text;
using Microsoft.Build.Framework;
using Polonium.Tasks;
// ReSharper disable once CheckNamespace
public class GenerateTextureSetTask : PoloniumTask
{
[Required]
public string RootPath { get; set; }
[Required]
public string OutputPath { get; set; }
public override bool Execute()
{
try
{
if (!Directory.Exists(RootPath))
{
Log.LogError($"Root path does not exist: {RootPath}");
return false;
}
string[] textureSetDirs = Directory.GetDirectories(RootPath, "*.TextureSet", SearchOption.AllDirectories);
StringBuilder sb = new StringBuilder();
sb
.AppendLine("using System;")
.AppendLine("using Polonium.Attributes;")
.AppendLine("[AutoRegister]")
.AppendLine("public class TextureSetMapRegister")
.AppendLine("{")
.AppendLine(" public static void Register()")
.AppendLine(" {");
StringBuilder sbx = new StringBuilder();
sbx
.AppendLine("using System;")
.AppendLine("using System.Collections.Generic;")
.AppendLine("using Polonium.DataStructures;")
.AppendLine("public static partial class GlobalRegistry")
.AppendLine("{")
.AppendLine(" public static Dictionary<TextureSetName, TextureSet> TextureSetMap = new();")
.AppendLine(" public enum TextureSetName")
.AppendLine(" {");
foreach (string dir in textureSetDirs)
{
StringBuilder sby = new StringBuilder();
sby
.AppendLine("using System;")
.AppendLine("using Polonium.DataStructures;")
.AppendLine("public static partial class GlobalRegistry")
.AppendLine("{")
.AppendLine(" public static partial class TextureSets")
.AppendLine(" {");
string relativePath = GetRelativePath(RootPath, dir);
string godotPath = $"res://Resources/ButtonTextureSet/{relativePath}";
string lName = relativePath.Replace("/", "_").Replace(".TextureSet", "").Replace(".", "");
string[] parts = relativePath.Split('/');
string instanceName = parts[parts.Length - 1].Replace(".TextureSet", "");
string instanceFullName = "GlobalRegistry.TextureSets";
string indent = " ";
StringBuilder sbsy = new StringBuilder();
foreach (string part in parts)
{
if (part == parts[parts.Length - 1])
break;
sby
.AppendLine($"{indent}public static partial class {part}")
.AppendLine($"{indent}{{");
sbsy.Insert(0, $"{indent}}}");
instanceFullName += $".{part}";
indent += " ";
}
instanceFullName += $".{instanceName}";
sby
.AppendLine($"{indent} public static readonly TextureSet {instanceName} = new TextureSet(\"{godotPath}\");")
.Append(sbsy)
.AppendLine(" }")
.AppendLine("}");
File.WriteAllText($"{OutputPath}Patches/{lName}.p.cs", sby.ToString());
sbx.AppendLine($" {lName},");
sb.AppendLine($" GlobalRegistry.TextureSetMap[GlobalRegistry.TextureSetName.{lName}] = {instanceFullName};");
}
sbx
.AppendLine(" }")
.AppendLine("}");
sb
.AppendLine(" }")
.AppendLine("}");
File.WriteAllText($"{OutputPath}Patches/TextureSetName.p.cs", sbx.ToString());
File.WriteAllText($"{OutputPath}Patches/TextureSetMapRegister.p.cs", sb.ToString());
return true;
}
catch (Exception ex)
{
Log.LogErrorFromException(ex);
return false;
}
}
}