Skip to content

Commit

Permalink
Adding WinMD generator (#383)
Browse files Browse the repository at this point in the history
* Adding WinMD generator

* Authoring pr feedback.

* Remove workaround
  • Loading branch information
manodasanW authored Aug 29, 2020
1 parent 6d6b3f0 commit 0a22529
Show file tree
Hide file tree
Showing 7 changed files with 2,199 additions and 0 deletions.
18 changes: 18 additions & 0 deletions Authoring/AuthoringSample/AuthoringSample.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>preview</LangVersion>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<CsWinRTEnableLogging>true</CsWinRTEnableLogging>
</PropertyGroup>

<ItemGroup>
<CompilerVisibleProperty Include="AssemblyName" />
<CompilerVisibleProperty Include="AssemblyVersion" />
<CompilerVisibleProperty Include="CsWinRTEnableLogging" />
<ProjectReference Include="..\..\Projections\Windows\Windows.csproj" />
<ProjectReference Include="..\WinRT.SourceGenerator\WinRT.SourceGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>

</Project>
209 changes: 209 additions & 0 deletions Authoring/AuthoringSample/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
using System;
using System.Reflection;
using Windows.Foundation.Metadata;

namespace MyTypes
{
public delegate void ExampleDelegate(UInt32 value);
public delegate int ExampleDelegateDouble(double newvalue);

public class ExampleClass
{
public ExampleClass()
{
}

public event ExampleDelegate SampleEvent;

public ExampleClass(ExampleClass other)
{
other.MethodA();
}

public ExampleClass(int alpha)
{
MethodB(alpha, alpha);
}

public void MethodA()
{
System.Console.WriteLine("Yay!");
}

public int MethodB(int param1, double param2)
{
return 4;
}

public ExampleClass Get()
{
return null;
}

private static void HelperMethod(ExampleClass instance)
{
instance.MethodA();
}

public double ExampleDouble { get; set; }
}

[Version(3)]
public interface ITest2
{
[Version(5)]
double GetTest(int test);
int GetTest2();
double GetSetDouble { get; set; }
}

internal enum PrivateEnum
{
privatetest,
privatetest2
}

public enum TestEnum
{
test,
test2
}

public enum NumericEnum : int
{
five = -5,
six = 6
}

public enum UnsignedEnum : uint
{
zero,
one,
two,
three
}

public struct MyStruct
{
public int test, test3;
public double test2;
}

public class Test : ITest2
{
public int GetSetInt { get; set; }
public double GetSetDouble { get; set; }
public int GetInt { get; }

public double GetTest(int test)
{
return test;
}

public int GetTest2()
{
return 4;
}
}

public class ExampleClass2 : ExampleClass
{
public void Delta()
{
}

public int Method()
{
return 4;
}

public int Method2(int param1, double param2)
{
return 4;
}

public void Pen()
{
}

public int Method3(double p1, double param2, int param3)
{
return 4;
}

public double Method4(double m4, double m5, int m6, int m7)
{
return 4;
}
}

public class Test3 : Test
{

}


public class Test8 : Windows.Foundation.IWwwFormUrlDecoderEntry
{
public string Name => throw new NotImplementedException();

public string Value => throw new NotImplementedException();
}

public class NewTest4 : ITest4
{
public double GetSetDouble { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }

public event ExampleDelegate TestEvent;

public double GetTest(int test)
{
throw new NotImplementedException();
}

public int GetTest2()
{
throw new NotImplementedException();
}

public int GetTest4()
{
throw new NotImplementedException();
}
}

public interface ITest4 : ITest2
{
int GetTest4();

public event ExampleDelegate TestEvent;
}

public class TestCustom
{
public TestCustom(int num)
{
_num = num;
}

public int GetNum()
{
return _num;
}

private int _num;
}

internal class SomeMoreCode
{
public static void Stuff()
{
const ExampleClass instance1 = null;
}

ExampleClass MyProp2
{
get { return null; }
}
}
}
98 changes: 98 additions & 0 deletions Authoring/WinRT.SourceGenerator/Generator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
using Microsoft.CodeAnalysis;
using System;
using System.IO;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;

namespace Generator
{
[Generator]
public class SourceGenerator : ISourceGenerator
{
private string GetAssemblyName(SourceGeneratorContext context)
{
context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.AssemblyName", out var assemblyName);
return assemblyName;
}

private string GetAssemblyVersion(SourceGeneratorContext context)
{
context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.AssemblyVersion", out var assemblyVersion);
return assemblyVersion;
}

public static string GetGeneratedFilesDir(SourceGeneratorContext context)
{
// TODO: determine correct location to write to.
string winmdDir = Path.Combine(Directory.GetCurrentDirectory(), "Generated Files");
Directory.CreateDirectory(winmdDir);
return winmdDir;
}

private string GetWinmdOutputFile(SourceGeneratorContext context)
{
return Path.Combine(GetGeneratedFilesDir(context), GetAssemblyName(context) + ".winmd");
}

private void GenerateWinMD(MetadataBuilder metadataBuilder, string outputFile)
{
Logger.Log("Writing " + outputFile);
var managedPeBuilder = new ManagedPEBuilder(
new PEHeaderBuilder(
machine: Machine.I386,
imageCharacteristics: Characteristics.ExecutableImage | Characteristics.Dll | Characteristics.Bit32Machine),
new MetadataRootBuilder(metadataBuilder, "WindowsRuntime 1.4"),
new BlobBuilder(),
flags: CorFlags.ILOnly);

var peBlob = new BlobBuilder();
managedPeBuilder.Serialize(peBlob);

using var fs = new FileStream(outputFile, FileMode.Create, FileAccess.Write);
peBlob.WriteContentTo(fs);
}

public void Execute(SourceGeneratorContext context)
{
Logger.Initialize(context);

try
{
string assembly = GetAssemblyName(context);
string version = GetAssemblyVersion(context);
MetadataBuilder metadataBuilder = new MetadataBuilder();

var writer = new WinRTTypeWriter(
assembly,
version,
metadataBuilder);
foreach (SyntaxTree tree in context.Compilation.SyntaxTrees)
{
writer.Model = context.Compilation.GetSemanticModel(tree);
writer.Visit(tree.GetRoot());
}
writer.FinalizeGeneration();

GenerateWinMD(metadataBuilder, GetWinmdOutputFile(context));
}
catch(Exception e)
{
Logger.Log(e.ToString());
if(e.InnerException != null)
{
Logger.Log(e.InnerException.ToString());
}
Logger.Close();
throw;
}

Logger.Log("Done");
Logger.Close();
}

public void Initialize(InitializationContext context)
{
}
}
}
30 changes: 30 additions & 0 deletions Authoring/WinRT.SourceGenerator/Logger.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Microsoft.CodeAnalysis;
using System.IO;

namespace Generator
{
class Logger
{
public static void Initialize(SourceGeneratorContext context)
{
context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.CsWinRTEnableLogging", out var enableLogging);
if(enableLogging != null && bool.Parse(enableLogging))
{
string logFile = Path.Combine(SourceGenerator.GetGeneratedFilesDir(context), "log.txt");
fileLogger = File.CreateText(logFile);
}
}

public static void Log(string text)
{
fileLogger?.WriteLine(text);
}

public static void Close()
{
fileLogger?.Close();
}

private static TextWriter fileLogger;
}
}
13 changes: 13 additions & 0 deletions Authoring/WinRT.SourceGenerator/WinRT.SourceGenerator.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>preview</LangVersion>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.7.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.0" PrivateAssets="all" />
</ItemGroup>

</Project>
Loading

0 comments on commit 0a22529

Please sign in to comment.