Add GenerateArrays task
This commit is contained in:
parent
33cd96ba0e
commit
a2032ade65
4 changed files with 278 additions and 171 deletions
168
.gitignore
vendored
168
.gitignore
vendored
|
@ -1,168 +0,0 @@
|
||||||
## Ignore Visual Studio temporary files, build results, and
|
|
||||||
## files generated by popular Visual Studio add-ons.
|
|
||||||
|
|
||||||
# User-specific files
|
|
||||||
*.suo
|
|
||||||
*.user
|
|
||||||
*.sln.docstates
|
|
||||||
.vs
|
|
||||||
.vscode
|
|
||||||
|
|
||||||
# Build results
|
|
||||||
|
|
||||||
[Dd]ebug/
|
|
||||||
[Rr]elease/
|
|
||||||
x64/
|
|
||||||
build/
|
|
||||||
[Bb]in/
|
|
||||||
[Oo]bj/
|
|
||||||
|
|
||||||
# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
|
|
||||||
!packages/*/build/
|
|
||||||
|
|
||||||
# MSTest test Results
|
|
||||||
[Tt]est[Rr]esult*/
|
|
||||||
[Bb]uild[Ll]og.*
|
|
||||||
|
|
||||||
*_i.c
|
|
||||||
*_p.c
|
|
||||||
*.ilk
|
|
||||||
*.meta
|
|
||||||
*.obj
|
|
||||||
*.pch
|
|
||||||
*.pdb
|
|
||||||
*.pgc
|
|
||||||
*.pgd
|
|
||||||
*.rsp
|
|
||||||
*.sbr
|
|
||||||
*.tlb
|
|
||||||
*.tli
|
|
||||||
*.tlh
|
|
||||||
*.tmp
|
|
||||||
*.tmp_proj
|
|
||||||
*.log
|
|
||||||
*.vspscc
|
|
||||||
*.vssscc
|
|
||||||
.builds
|
|
||||||
*.pidb
|
|
||||||
*.scc
|
|
||||||
|
|
||||||
# Visual C++ cache files
|
|
||||||
ipch/
|
|
||||||
*.aps
|
|
||||||
*.ncb
|
|
||||||
*.opensdf
|
|
||||||
*.sdf
|
|
||||||
*.cachefile
|
|
||||||
|
|
||||||
# Visual Studio profiler
|
|
||||||
*.psess
|
|
||||||
*.vsp
|
|
||||||
*.vspx
|
|
||||||
|
|
||||||
# Guidance Automation Toolkit
|
|
||||||
*.gpState
|
|
||||||
|
|
||||||
# ReSharper is a .NET coding add-in
|
|
||||||
_ReSharper*/
|
|
||||||
*.[Rr]e[Ss]harper
|
|
||||||
|
|
||||||
# TeamCity is a build add-in
|
|
||||||
_TeamCity*
|
|
||||||
|
|
||||||
# DotCover is a Code Coverage Tool
|
|
||||||
*.dotCover
|
|
||||||
|
|
||||||
# Rider is a Visual Studio alternative
|
|
||||||
.idea/*
|
|
||||||
|
|
||||||
# NCrunch
|
|
||||||
*.ncrunch*
|
|
||||||
.*crunch*.local.xml
|
|
||||||
|
|
||||||
# Installshield output folder
|
|
||||||
[Ee]xpress/
|
|
||||||
|
|
||||||
# DocProject is a documentation generator add-in
|
|
||||||
DocProject/buildhelp/
|
|
||||||
DocProject/Help/*.HxT
|
|
||||||
DocProject/Help/*.HxC
|
|
||||||
DocProject/Help/*.hhc
|
|
||||||
DocProject/Help/*.hhk
|
|
||||||
DocProject/Help/*.hhp
|
|
||||||
DocProject/Help/Html2
|
|
||||||
DocProject/Help/html
|
|
||||||
|
|
||||||
# Click-Once directory
|
|
||||||
publish/
|
|
||||||
|
|
||||||
# Publish Web Output
|
|
||||||
*.Publish.xml
|
|
||||||
|
|
||||||
# NuGet Packages Directory
|
|
||||||
## TODO: If you have NuGet Package Restore enabled, uncomment the next line
|
|
||||||
packages/
|
|
||||||
|
|
||||||
# Windows Azure Build Output
|
|
||||||
csx
|
|
||||||
*.build.csdef
|
|
||||||
|
|
||||||
# Windows Store app package directory
|
|
||||||
AppPackages/
|
|
||||||
|
|
||||||
# Others
|
|
||||||
sql/
|
|
||||||
*.Cache
|
|
||||||
ClientBin/
|
|
||||||
[Ss]tyle[Cc]op.*
|
|
||||||
~$*
|
|
||||||
*~
|
|
||||||
*.dbmdl
|
|
||||||
*.[Pp]ublish.xml
|
|
||||||
*.pfx
|
|
||||||
*.publishsettings
|
|
||||||
packages/*
|
|
||||||
*.config
|
|
||||||
|
|
||||||
# RIA/Silverlight projects
|
|
||||||
Generated_Code/
|
|
||||||
|
|
||||||
# Backup & report files from converting an old project file to a newer
|
|
||||||
# Visual Studio version. Backup files are not needed, because we have git ;-)
|
|
||||||
_UpgradeReport_Files/
|
|
||||||
Backup*/
|
|
||||||
UpgradeLog*.XML
|
|
||||||
UpgradeLog*.htm
|
|
||||||
|
|
||||||
# SQL Server files
|
|
||||||
App_Data/*.mdf
|
|
||||||
App_Data/*.ldf
|
|
||||||
|
|
||||||
|
|
||||||
#LightSwitch generated files
|
|
||||||
GeneratedArtifacts/
|
|
||||||
_Pvt_Extensions/
|
|
||||||
ModelManifest.xml
|
|
||||||
|
|
||||||
# =========================
|
|
||||||
# Windows detritus
|
|
||||||
# =========================
|
|
||||||
|
|
||||||
# Windows image file caches
|
|
||||||
Thumbs.db
|
|
||||||
ehthumbs.db
|
|
||||||
|
|
||||||
# Folder config file
|
|
||||||
Desktop.ini
|
|
||||||
|
|
||||||
# Recycle Bin used on file shares
|
|
||||||
$RECYCLE.BIN/
|
|
||||||
|
|
||||||
# Mac desktop service store files
|
|
||||||
.DS_Store
|
|
||||||
|
|
||||||
# VS Launch Settings
|
|
||||||
launchSettings.json
|
|
||||||
|
|
||||||
# NetCore Publishing Profiles
|
|
||||||
PublishProfiles/
|
|
243
Ryujinx.CustomTasks/GenerateArrays.cs
Normal file
243
Ryujinx.CustomTasks/GenerateArrays.cs
Normal file
|
@ -0,0 +1,243 @@
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
using Microsoft.Build.Framework;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using Ryujinx.CustomTasks.SyntaxWalker;
|
||||||
|
using Ryujinx.CustomTasks.Helper;
|
||||||
|
using Task = Microsoft.Build.Utilities.Task;
|
||||||
|
|
||||||
|
namespace Ryujinx.CustomTasks
|
||||||
|
{
|
||||||
|
public class GenerateArrays : Task
|
||||||
|
{
|
||||||
|
private const string InterfaceFileName = "IArray.g.cs";
|
||||||
|
private const string ArraysFileName = "Arrays.g.cs";
|
||||||
|
|
||||||
|
private readonly List<string> _outputFiles = new List<string>();
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string ArrayNamespace { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string OutputPath { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public ITaskItem[] InputFiles { get; set; }
|
||||||
|
|
||||||
|
[Output]
|
||||||
|
public string[] OutputFiles { get; set; }
|
||||||
|
|
||||||
|
private static int GetAvailableMaxSize(IReadOnlyList<int> availableSizes, ref int index, int missingSize)
|
||||||
|
{
|
||||||
|
if (availableSizes.Count == 0 || missingSize == 1)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (availableSizes.Count < index + 1)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int size = 0;
|
||||||
|
|
||||||
|
while (size == 0 || size > missingSize && availableSizes.Count - index > 0)
|
||||||
|
{
|
||||||
|
index++;
|
||||||
|
size = availableSizes[availableSizes.Count - index];
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddGeneratedSource(string filePath, string content)
|
||||||
|
{
|
||||||
|
File.WriteAllText(filePath, content);
|
||||||
|
_outputFiles.Add(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ICollection<int> GetArraySizes(string itemPath)
|
||||||
|
{
|
||||||
|
Log.LogMessage(MessageImportance.Low, $"Searching for StructArray types in: {itemPath}");
|
||||||
|
|
||||||
|
SyntaxTree tree = CSharpSyntaxTree.ParseText(File.ReadAllText(itemPath), path: itemPath);
|
||||||
|
CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
|
||||||
|
|
||||||
|
ArraySizeCollector collector = new ArraySizeCollector();
|
||||||
|
|
||||||
|
collector.Visit(root);
|
||||||
|
|
||||||
|
foreach (int size in collector.ArraySizes)
|
||||||
|
{
|
||||||
|
Log.LogMessage(MessageImportance.Low, $"\tFound array size: {size}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return collector.ArraySizes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GenerateInterface()
|
||||||
|
{
|
||||||
|
CodeGenerator generator = new CodeGenerator();
|
||||||
|
|
||||||
|
generator.EnterScope($"namespace {ArrayNamespace}");
|
||||||
|
|
||||||
|
generator.AppendLine("/// <summary>");
|
||||||
|
generator.AppendLine("/// Array interface.");
|
||||||
|
generator.AppendLine("/// </summary>");
|
||||||
|
generator.AppendLine("/// <typeparam name=\"T\">Element type</typeparam>);");
|
||||||
|
generator.EnterScope("public interface IArray<T> where T : unmanaged");
|
||||||
|
|
||||||
|
generator.AppendLine("/// <summary>");
|
||||||
|
generator.AppendLine("/// Used to index the array.");
|
||||||
|
generator.AppendLine("/// </summary>");
|
||||||
|
generator.AppendLine("/// <param name=\"index\">Element index</param>");
|
||||||
|
generator.AppendLine("/// <returns>Element at the specified index</returns>");
|
||||||
|
generator.AppendLine("ref T this[int index] { get; }");
|
||||||
|
|
||||||
|
generator.AppendLine();
|
||||||
|
|
||||||
|
generator.AppendLine("/// <summary>");
|
||||||
|
generator.AppendLine("/// Number of elements on the array.");
|
||||||
|
generator.AppendLine("/// </summary>");
|
||||||
|
generator.AppendLine("int Length { get; }");
|
||||||
|
|
||||||
|
generator.LeaveScope();
|
||||||
|
generator.LeaveScope();
|
||||||
|
|
||||||
|
return generator.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GenerateArray(CodeGenerator generator, int size, IReadOnlyList<int> availableSizes)
|
||||||
|
{
|
||||||
|
generator.EnterScope($"public struct Array{size}<T> : IArray<T> where T : unmanaged");
|
||||||
|
|
||||||
|
generator.AppendLine("T _e0;");
|
||||||
|
|
||||||
|
if (size > 1)
|
||||||
|
{
|
||||||
|
generator.AppendLine("#pragma warning disable CS0169");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (availableSizes.Count == 0)
|
||||||
|
{
|
||||||
|
for (int i = 1; i < size; i++)
|
||||||
|
{
|
||||||
|
generator.AppendLine($"T _e{i};");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int counter = 1;
|
||||||
|
int currentSize = 1;
|
||||||
|
int maxSizeIndex = 0;
|
||||||
|
int maxSize = 0;
|
||||||
|
|
||||||
|
while (currentSize < size)
|
||||||
|
{
|
||||||
|
if (maxSize == 0 || currentSize + maxSize > size)
|
||||||
|
{
|
||||||
|
maxSize = GetAvailableMaxSize(availableSizes, ref maxSizeIndex, size - currentSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
generator.AppendLine(maxSize > 1
|
||||||
|
? counter == 1
|
||||||
|
? $"Array{maxSize}<T> _other;"
|
||||||
|
: $"Array{maxSize}<T> _other{counter};"
|
||||||
|
: $"T _e{counter};"
|
||||||
|
);
|
||||||
|
|
||||||
|
counter++;
|
||||||
|
currentSize += maxSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size > 1)
|
||||||
|
{
|
||||||
|
generator.AppendLine("#pragma warning restore CS0169");
|
||||||
|
}
|
||||||
|
|
||||||
|
generator.AppendLine();
|
||||||
|
generator.AppendLine($"public int Length => {size};");
|
||||||
|
generator.AppendLine("public ref T this[int index] => ref AsSpan()[index];");
|
||||||
|
generator.AppendLine("public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref _e0, Length);");
|
||||||
|
|
||||||
|
generator.LeaveScope();
|
||||||
|
generator.AppendLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Execute()
|
||||||
|
{
|
||||||
|
string interfaceFilePath = Path.Combine(OutputPath, InterfaceFileName);
|
||||||
|
string arraysFilePath = Path.Combine(OutputPath, ArraysFileName);
|
||||||
|
List<int> arraySizes = new List<int>();
|
||||||
|
|
||||||
|
File.Delete(interfaceFilePath);
|
||||||
|
File.Delete(arraysFilePath);
|
||||||
|
|
||||||
|
foreach (var item in InputFiles)
|
||||||
|
{
|
||||||
|
string fullPath = item.GetMetadata("FullPath");
|
||||||
|
|
||||||
|
if (fullPath.EndsWith(".g.cs") || fullPath.Contains("obj\\Debug\\") || fullPath.Contains("obj\\Release\\"))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (int size in GetArraySizes(fullPath))
|
||||||
|
{
|
||||||
|
if (!arraySizes.Contains(size))
|
||||||
|
{
|
||||||
|
arraySizes.Add(size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arraySizes.Count == 0)
|
||||||
|
{
|
||||||
|
Log.LogWarning("No StructArray types found. Skipping code generation.");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
arraySizes.Sort();
|
||||||
|
|
||||||
|
AddGeneratedSource(interfaceFilePath, GenerateInterface());
|
||||||
|
|
||||||
|
CodeGenerator generator = new CodeGenerator();
|
||||||
|
List<int> sizesAvailable = new List<int>();
|
||||||
|
|
||||||
|
generator.AppendLine("using System;");
|
||||||
|
generator.AppendLine("using System.Runtime.InteropServices;");
|
||||||
|
generator.AppendLine();
|
||||||
|
|
||||||
|
generator.EnterScope($"namespace {ArrayNamespace}");
|
||||||
|
|
||||||
|
// Always generate Arrays for 1, 2 and 3
|
||||||
|
GenerateArray(generator, 1, sizesAvailable);
|
||||||
|
sizesAvailable.Add(1);
|
||||||
|
GenerateArray(generator, 2, sizesAvailable);
|
||||||
|
sizesAvailable.Add(2);
|
||||||
|
GenerateArray(generator, 3, sizesAvailable);
|
||||||
|
sizesAvailable.Add(3);
|
||||||
|
|
||||||
|
foreach (var size in arraySizes)
|
||||||
|
{
|
||||||
|
if (!sizesAvailable.Contains(size))
|
||||||
|
{
|
||||||
|
GenerateArray(generator, size, sizesAvailable);
|
||||||
|
sizesAvailable.Add(size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
generator.LeaveScope();
|
||||||
|
|
||||||
|
AddGeneratedSource(arraysFilePath, generator.ToString());
|
||||||
|
|
||||||
|
OutputFiles = _outputFiles.ToArray();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,11 +2,12 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
<LangVersion>8</LangVersion>
|
||||||
<IsPackable>true</IsPackable>
|
<IsPackable>true</IsPackable>
|
||||||
|
|
||||||
<PackageId>Ryujinx.CustomTasks</PackageId>
|
<PackageId>Ryujinx.CustomTasks</PackageId>
|
||||||
<Title>Ryujinx.CustomTasks</Title>
|
<Title>Ryujinx.CustomTasks</Title>
|
||||||
<Description>A collection of custom MSBuild tasks.</Description>
|
<Description>A collection of custom MSBuild tasks.</Description>
|
||||||
<Version>1.0.0</Version>
|
|
||||||
<RepositoryUrl>https://github.com/Ryujinx/Ryujinx.CustomTasks</RepositoryUrl>
|
<RepositoryUrl>https://github.com/Ryujinx/Ryujinx.CustomTasks</RepositoryUrl>
|
||||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
|
|
||||||
|
@ -19,7 +20,6 @@
|
||||||
<NoWarn>NU5100</NoWarn>
|
<NoWarn>NU5100</NoWarn>
|
||||||
<!-- Tell the SDK to generate a deps.json file -->
|
<!-- Tell the SDK to generate a deps.json file -->
|
||||||
<GenerateDependencyFile>true</GenerateDependencyFile>
|
<GenerateDependencyFile>true</GenerateDependencyFile>
|
||||||
<LangVersion>8</LangVersion>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -34,9 +34,10 @@
|
||||||
|
|
||||||
<!-- This is the target we defined above. It's purpose is to add all of our PackageReference and ProjectReference's runtime assets to our package output. -->
|
<!-- This is the target we defined above. It's purpose is to add all of our PackageReference and ProjectReference's runtime assets to our package output. -->
|
||||||
<!-- Currently we have to manually specify the runtime assets, because for some reason ReferenceCopyLocalPaths is always empty. -->
|
<!-- Currently we have to manually specify the runtime assets, because for some reason ReferenceCopyLocalPaths is always empty. -->
|
||||||
|
<!-- TODO: Copy dependencies to the lib directory instead of the tasks directory -->
|
||||||
<Target Name="CopyProjectReferencesToPackage" DependsOnTargets="ResolveReferences">
|
<Target Name="CopyProjectReferencesToPackage" DependsOnTargets="ResolveReferences">
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<BuildOutputInPackage Include="bin\$(Configuration)\*\Microsoft.*.dll" />
|
<BuildOutputInPackage Include="bin\$(Configuration)\*\Microsoft.CodeAnalysis*.dll" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
|
|
31
Ryujinx.CustomTasks/SyntaxWalker/ArraySizeCollector.cs
Normal file
31
Ryujinx.CustomTasks/SyntaxWalker/ArraySizeCollector.cs
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
using Microsoft.CodeAnalysis.CSharp;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.CustomTasks.SyntaxWalker
|
||||||
|
{
|
||||||
|
class ArraySizeCollector : CSharpSyntaxWalker
|
||||||
|
{
|
||||||
|
public ICollection<int> ArraySizes { get; } = new List<int>();
|
||||||
|
|
||||||
|
private void AddArrayString(string name)
|
||||||
|
{
|
||||||
|
if (!name.StartsWith("Array") || !name.Contains("<"))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string rawArrayType = name.Split('<')[0];
|
||||||
|
|
||||||
|
if (int.TryParse(rawArrayType.Substring(5), out int size) && !ArraySizes.Contains(size))
|
||||||
|
{
|
||||||
|
ArraySizes.Add(size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://learn.microsoft.com/en-us/dotnet/api/microsoft.codeanalysis.csharp.syntax.genericnamesyntax?view=roslyn-dotnet-4.3.0
|
||||||
|
public override void VisitGenericName(GenericNameSyntax node) => AddArrayString(node.ToString());
|
||||||
|
// https://learn.microsoft.com/en-us/dotnet/api/microsoft.codeanalysis.csharp.syntax.identifiernamesyntax?view=roslyn-dotnet-4.3.0
|
||||||
|
public override void VisitIdentifierName(IdentifierNameSyntax node) => AddArrayString(node.ToString());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue