Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Source Generators #15

Merged
merged 33 commits into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
1929fbf
Work In Progress for Source Generators
otac0n Nov 16, 2023
895377d
More WIP, untested as yet.
otac0n Nov 22, 2023
c49ef38
Added testing project and Debug profile.
otac0n Nov 22, 2023
4876374
More WIP
otac0n Nov 30, 2023
2f5e133
DEBUG ONLY
otac0n Nov 30, 2023
e52fd32
Added TODOs and constants.
otac0n Dec 1, 2023
0926a74
More WIP
otac0n Dec 1, 2023
3c23796
WIP Closing in on a backwards compatibility story from the MSBuild side.
otac0n Dec 2, 2023
da93ffe
Basic working implementation of the source generator with fall-back t…
otac0n Dec 2, 2023
0f492b3
Updated test and template.
otac0n Dec 2, 2023
ad6f408
Removed Abstractions.
otac0n Dec 2, 2023
7ccbf7a
Spelling fix.
otac0n Dec 2, 2023
ceaa08a
Added legacy compilation tests.
otac0n Dec 2, 2023
cd8aff6
Fixed generator references.
otac0n Dec 2, 2023
d909822
Encode path characters to allow uniqueness of output names.
otac0n Dec 2, 2023
5222587
Update to the latest project tempate (with mismatches included).
otac0n Dec 2, 2023
cfe9c72
Swapped to my previously implemented PathUtils for encoding.
otac0n Dec 2, 2023
55fa457
Avoid path leakage by using relative paths.
otac0n Dec 2, 2023
789ee57
Fix comments and add more logging.
otac0n Dec 2, 2023
b69edc1
Update paths to be relative to the generated file.
otac0n Dec 3, 2023
a966921
Regenerate weave files.
otac0n Dec 3, 2023
f9465b5
Generated project now has files with proper paths.
otac0n Dec 3, 2023
4b06696
Updated test cases for the currently working generation.
otac0n Dec 3, 2023
fb55db8
Enabled failing tests.
otac0n Dec 3, 2023
ddf631a
Output netstandard library to the analyzers path.
otac0n Dec 4, 2023
6f5c15f
Build in VS2022.
otac0n Dec 4, 2023
bb30ba3
Update System.CodeDom references.
otac0n Dec 4, 2023
7f9f98f
Don't run the update metadata target if there are no WeaveTemplate it…
otac0n Dec 4, 2023
5dccad0
Disable compilation of generated-source tests, to allow the build to …
otac0n Dec 4, 2023
70abf33
Revert to net46.
otac0n Dec 4, 2023
1fb8125
Fixes for PR.
otac0n Dec 5, 2023
b130009
Copied the targets file to the output folder for use by other project…
otac0n Dec 5, 2023
3dee04b
Revert unneeded changes.
otac0n Dec 5, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@namespace Weave.Tests.Generated.ConfigTest.AbsentConfig
@accessibility public
Hello, world!
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello, world!
2 changes: 2 additions & 0 deletions Weave.Tests.Generated/ConfigTest/CompiledConfig/_config.weave
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@namespace Weave.Tests.Generated.ConfigTest.CompiledConfig
@accessibility public
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello, world!
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@namespace Weave.Tests.Generated.ConfigTest.GeneratedConfig
@accessibility public
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello, world!
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@namespace Weave.Tests.Generated.ConfigTest.LegacyCompiledConfig
@accessibility public
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello, world!
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@namespace Weave.Tests.Generated.ConfigTest.LegacyGeneratedConfig
@accessibility public
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello, world!
2 changes: 2 additions & 0 deletions Weave.Tests.Generated/ConfigTest/NoneConfig/_config.weave
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@namespace Weave.Tests.Generated.ConfigTest.NoneConfig
@accessibility public
4 changes: 4 additions & 0 deletions Weave.Tests.Generated/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// <auto-generated />
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Weave.Tests")]
51 changes: 51 additions & 0 deletions Weave.Tests.Generated/Weave.Tests.Generated.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Pegasus" Version="4.2.0-alpha0009" GeneratePathProperty="true" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Weave\Weave.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" SetTargetFramework="TargetFramework=netstandard2.0" />
</ItemGroup>
<ItemGroup>
<WeaveTemplate Include="ConfigTest\AbsentConfig\TestAbsentConfig.weave" />
<None Remove="ConfigTest\CompiledConfig\_config.weave" />
<WeaveTemplate Include="ConfigTest\CompiledConfig\_config.weave">
<UseSourceGeneration>false</UseSourceGeneration>
</WeaveTemplate>
<None Remove="ConfigTest\CompiledConfig\TestCompiledConfig.weave" />
<WeaveTemplate Include="ConfigTest\CompiledConfig\TestCompiledConfig.weave" />
<None Remove="ConfigTest\GeneratedConfig\_config.weave" />
<WeaveTemplate Include="ConfigTest\GeneratedConfig\_config.weave" />
<None Remove="ConfigTest\GeneratedConfig\TestGeneratedConfig.weave" />
<WeaveTemplate Include="ConfigTest\GeneratedConfig\TestGeneratedConfig.weave" />
<WeaveTemplate Include="ConfigTest\LegacyCompiledConfig\_config.weave">
<UseSourceGeneration>false</UseSourceGeneration>
</WeaveTemplate>
<None Remove="ConfigTest\LegacyCompiledConfig\_config.weave" />
<None Remove="ConfigTest\LegacyCompiledConfig\LegacyCompiledConfig.weave" />
<WeaveTemplate Include="ConfigTest\LegacyCompiledConfig\LegacyCompiledConfig.weave">
<UseSourceGeneration>false</UseSourceGeneration>
</WeaveTemplate>
<None Remove="ConfigTest\LegacyGeneratedConfig\_config.weave" />
<WeaveTemplate Include="ConfigTest\LegacyGeneratedConfig\_config.weave" />
<None Remove="ConfigTest\LegacyGeneratedConfig\LegacyGeneratedConfig.weave" />
<WeaveTemplate Include="ConfigTest\LegacyGeneratedConfig\LegacyGeneratedConfig.weave">
<UseSourceGeneration>false</UseSourceGeneration>
</WeaveTemplate>
<None Remove="ConfigTest\NoneConfig\TestNoneConfig.weave" />
<WeaveTemplate Include="ConfigTest\NoneConfig\TestNoneConfig.weave" />
</ItemGroup>
<PropertyGroup>
<GetTargetPathDependsOn>$(GetTargetPathDependsOn);GetDependencyTargetPaths</GetTargetPathDependsOn>
</PropertyGroup>
<Import Project="..\Weave\bin\$(Configuration)\Weave.targets" />
<Target Name="GetDependencyTargetPaths">
<ItemGroup>
<TargetPathWithTargetPlatformMoniker Include="$(PKGPegasus)\lib\netstandard2.0\Pegasus.Common.dll" IncludeRuntimeDependency="false" />
</ItemGroup>
</Target>
</Project>
81 changes: 81 additions & 0 deletions Weave.Tests/GeneratedCode/ConfigTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright © John Gietzen. All Rights Reserved. This source is subject to the MIT license. Please see license.md for more information.

namespace Weave.Tests.IntegrationTests
{
using System;
using System.IO;
using Xunit;

public class ConfigTests
{
private static readonly string ExpectedConfigOutput = "";
private static readonly string ExpectedTemplateOutput = $"Hello, world!{Environment.NewLine}";

[Fact]
public void AbsentConfig()
{
TestHelper(
Generated.ConfigTest.AbsentConfig.Templates.RenderTestAbsentConfig, ExpectedTemplateOutput);
}

[Fact]
public void CompiledConfig()
{
TestHelper(
(Generated.ConfigTest.CompiledConfig.Templates.Render_config, ExpectedConfigOutput),
(Generated.ConfigTest.CompiledConfig.Templates.RenderTestCompiledConfig, ExpectedTemplateOutput));
}

[Fact]
public void GeneratedConfig()
{
TestHelper(
(Generated.ConfigTest.GeneratedConfig.Templates.Render_config, ExpectedConfigOutput),
(Generated.ConfigTest.GeneratedConfig.Templates.RenderTestGeneratedConfig, ExpectedTemplateOutput));
}

[Fact]
public void LegacyCompiledConfig()
{
TestHelper(
(Generated.ConfigTest.LegacyCompiledConfig.Templates.Render_config, ExpectedConfigOutput),
(Generated.ConfigTest.LegacyCompiledConfig.Templates.RenderLegacyCompiledConfig, ExpectedTemplateOutput));
}

[Fact]
public void LegacyGeneratedConfig()
{
TestHelper(
(Generated.ConfigTest.LegacyGeneratedConfig.Templates.Render_config, ExpectedConfigOutput),
(Generated.ConfigTest.LegacyGeneratedConfig.Templates.RenderLegacyGeneratedConfig, ExpectedTemplateOutput));
}

[Fact]
public void NoneConfig()
{
TestHelper(
Generated.ConfigTest.NoneConfig.Templates.RenderTestNoneConfig, ExpectedTemplateOutput);
}

private static void TestHelper(params (Action<dynamic, TextWriter, string> render, string expected)[] tests)
{
foreach (var (render, expected) in tests)
{
TestHelper(render, expected);
}
}

private static void TestHelper(Action<dynamic, TextWriter, string> render, string expected)
{
var output = TestHelper(render);
Assert.Equal(expected, output);
}

private static string TestHelper(Action<dynamic, TextWriter, string> render)
{
var writer = new StringWriter();
render(null, writer, null);
return writer.ToString();
}
}
}
19 changes: 13 additions & 6 deletions Weave.Tests/Weave.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,31 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\SharedAssemblyInfo.props" />
<PropertyGroup>
<CodeAnalysisRuleSet>Weave.Tests.ruleset</CodeAnalysisRuleSet>
<DebugType>Full</DebugType>
<IsPackable>false</IsPackable>
<TargetFramework>net461</TargetFramework>
<Description />
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\stylecop.json" Link="stylecop.json" />
<AdditionalFiles Include="Weave.Tests.ruleset" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="FixMe" Version="1.0.0" PrivateAssets="All" />
<PackageReference Include="GitVersionTask" Version="4.0.0-beta0012" PrivateAssets="All" />
<PackageReference Include="GitVersion.MsBuild" Version="5.12.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="15.6.85" />
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.6.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta006" PrivateAssets="All" />
<PackageReference Include="System.CodeDom" Version="4.4.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
<PackageReference Include="System.CodeDom" Version="4.7.0" />
<PackageReference Include="xunit" Version="2.3.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Weave\Weave.csproj" />
<ProjectReference Include="..\Weave.Tests.Generated\Weave.Tests.Generated.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Remove="GeneratedCode\ConfigTests.cs" />
<None Include="GeneratedCode\ConfigTests.cs" />
</ItemGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
Expand Down
10 changes: 8 additions & 2 deletions Weave.sln
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27428.2037
# Visual Studio Version 17
VisualStudioVersion = 17.7.34003.232
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D0E57E35-59EE-487F-852B-864CFC020B46}"
ProjectSection(SolutionItems) = preProject
Expand All @@ -25,6 +25,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Weave", "Weave\Weave.csproj
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Weave.Tests", "Weave.Tests\Weave.Tests.csproj", "{F2ECD1F3-7200-4FEA-A456-873F1749E008}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Weave.Tests.Generated", "Weave.Tests.Generated\Weave.Tests.Generated.csproj", "{46D94595-1DBC-49C6-9D77-A1E5E307E21E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -39,6 +41,10 @@ Global
{F2ECD1F3-7200-4FEA-A456-873F1749E008}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F2ECD1F3-7200-4FEA-A456-873F1749E008}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F2ECD1F3-7200-4FEA-A456-873F1749E008}.Release|Any CPU.Build.0 = Release|Any CPU
{46D94595-1DBC-49C6-9D77-A1E5E307E21E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{46D94595-1DBC-49C6-9D77-A1E5E307E21E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{46D94595-1DBC-49C6-9D77-A1E5E307E21E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{46D94595-1DBC-49C6-9D77-A1E5E307E21E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
109 changes: 78 additions & 31 deletions Weave/CompileManager.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright © John Gietzen. All Rights Reserved. This source is subject to the MIT license. Please see license.md for more information.
// Copyright © John Gietzen. All Rights Reserved. This source is subject to the MIT license. Please see license.md for more information.

namespace Weave
{
Expand All @@ -13,29 +13,64 @@ namespace Weave

internal static class CompileManager
{
public static void CompileFile(string inputFile, string outputFile, Action<CompilerError> logError)
{
outputFile = outputFile ?? inputFile + ".cs";
var configFile = Path.Combine(Path.GetDirectoryName(inputFile), "_config.weave");
public static readonly string ConfigFileName = "_config.weave";

var template = ParseTemplate(inputFile, logError);
if (template == null)
public static CompileResult<Template> ParseTemplate(string inputFileContents, string inputFileName)
{
var parser = new WeaveParser();
var compileResult = new CompileResult<Template>();
try
{
return;
compileResult.Result = parser.Parse(inputFileContents, fileName: inputFileName);
}

if (File.Exists(configFile))
catch (FormatException ex)
{
var config = ParseTemplate(configFile, logError);
if (config == null)
var cursor = ex.Data["cursor"] as Cursor;
if (cursor != null && Regex.IsMatch(ex.Message, @"^WEAVE\d+:"))
{
var parts = ex.Message.Split(new[] { ':' }, 2);
compileResult.Errors.Add(new CompilerError(cursor.FileName, cursor.Line, cursor.Column, parts[0], parts[1]));
}
else
{
return;
throw;
}
}

return compileResult;
}

public static CompileResult<Template> CombineTemplateConfig(CompileResult<Template> template, CompileResult<Template> config)
{
if (config == null || (config.Result == null && config.Errors.Count == 0))
{
return template;
}

var result = new CompileResult<Template>
{
Result = new Template(template.Result, config.Result),
};

foreach (var error in config.Errors)
{
result.Errors.Add(error);
}

template = new Template(template, config);
foreach (var error in result.Errors)
{
result.Errors.Add(error);
}

var result = WeaveCompiler.Compile(template);
return result;
}

#if !NETSTANDARD
public static void CompileFile(string inputFileName, string outputFileName, Action<CompilerError> logError)
{
inputFileName = Path.GetFullPath(inputFileName);
outputFileName = Path.GetFullPath(outputFileName ?? inputFileName + ".cs");
var result = CompileFile(inputFileName, outputFileName);

var hadFatal = false;
foreach (var error in result.Errors)
Expand All @@ -46,31 +81,43 @@ public static void CompileFile(string inputFile, string outputFile, Action<Compi

if (!hadFatal)
{
Directory.CreateDirectory(Path.GetDirectoryName(outputFile));
File.WriteAllText(outputFile, result.Code);
Directory.CreateDirectory(Path.GetDirectoryName(outputFileName));
File.WriteAllText(outputFileName, result.Code);
}
}

private static Template ParseTemplate(string inputFile, Action<CompilerError> logError)
private static CompileResult CompileFile(string inputFileName, string outputFileName)
{
var subject = File.ReadAllText(inputFile);
var parser = new WeaveParser();
try
{
return parser.Parse(subject, fileName: inputFile);
}
catch (FormatException ex)
var inputResult = ParseTemplateInternal(inputFileName, outputFileName);
var inputTemplate = inputResult.Result;
if (inputTemplate == null)
{
var cursor = ex.Data["cursor"] as Cursor;
if (cursor != null && Regex.IsMatch(ex.Message, @"^WEAVE\d+:"))
var errorResult = new CompileResult();
foreach (var error in inputResult.Errors)
{
var parts = ex.Message.Split(new[] { ':' }, 2);
logError(new CompilerError(cursor.FileName, cursor.Line, cursor.Column, parts[0], parts[1]));
return null;
errorResult.Errors.Add(error);
}

throw;
return errorResult;
}

return WeaveCompiler.Compile(inputTemplate);
}

private static CompileResult<Template> ParseTemplateInternal(string inputFileName, string outputFileName)
{
var relativePath = PathUtils.MakeRelative(outputFileName, inputFileName);
var templateResult = ParseTemplate(File.ReadAllText(inputFileName), relativePath);

var configFileName = Path.Combine(Path.GetDirectoryName(inputFileName), ConfigFileName);
if (File.Exists(configFileName))
{
var configResult = ParseTemplate(File.ReadAllText(configFileName), PathUtils.MakeRelative(outputFileName, configFileName));
templateResult = CombineTemplateConfig(templateResult, configResult);
}

return templateResult;
}
#endif
}
}
Loading