Merge pull request #8 from carldebilly/dev/cdb/generator-reversed-dependency
Added ability to define generator dependencies in both ways. The previous `[SourceGeneratorDependency]` attribute has been replaced by `[GenerateBefore]` and a new `[GenerateAfter]` attribute for the opposite.
This commit is contained in:
Коммит
7cb6ded699
15
readme.md
15
readme.md
|
@ -135,10 +135,11 @@ This will open another visual studio instance, and allow for stepping through th
|
|||
* Generators should have the least possible external dependencies.
|
||||
Generators are loaded in a separate `AppDomain` but multiple assemblies versions can be
|
||||
troublesome when loaded side by side.
|
||||
|
||||
* You can add a dependency on your generator by adding the `Uno.SourceGeneration.SourceGeneratorDependency`
|
||||
attribute on your class:
|
||||
```csharp
|
||||
[SourceGeneratorDependency("Uno.ImmutableGenerator")]
|
||||
[GenerateAfter("Uno.ImmutableGenerator")] // Generate ImmutableGenerator before EqualityGenerator
|
||||
public class EqualityGenerator : SourceGenerator
|
||||
```
|
||||
For instance here, it will ensure that the `ImmutableGenerator` is executed before your `EqualityGenerator`.
|
||||
|
@ -146,10 +147,14 @@ This will open another visual studio instance, and allow for stepping through th
|
|||
from a generator are excluded from the roslyn `Compilation` object of other generators, meaning that if
|
||||
two generators use the same conditions to generate the same code, there will be a compilation
|
||||
error in the resulting code.
|
||||
* If you need a generator to use the result of another one for its own compilation, you can use
|
||||
the `[SourceGeneratorDependency]` attribute. You simply need to specify the FullName
|
||||
(namespace + type name) of another generator. If this generator is found, it will ensure it
|
||||
is executed before and the result is added to the compilation before calling yours.
|
||||
|
||||
* You can also define a generator which must be executed **after** yours. To do this, you need to declare a
|
||||
_dependent generator_:
|
||||
```csharp
|
||||
[GenerateBefore("Uno.EqualityGenerator")] // Generate ImmutableGenerator before EqualityGenerator
|
||||
public class ImmutableGenerator : SourceGenerator
|
||||
```
|
||||
|
||||
* Sometimes you may need to kill all instances of MsBuild. On Windows, the fatest way to to that
|
||||
is to open a shell in admin mode and type this line:
|
||||
```
|
||||
|
|
|
@ -18,8 +18,7 @@ using Uno.SourceGeneration;
|
|||
|
||||
namespace Uno.SampleGenerators
|
||||
{
|
||||
[SourceGeneratorDependency("Uno.SampleGenerators.MyCustomSourceGenerator")]
|
||||
[SourceGeneratorDependency("Uno.SampleGenerators.UselessGenerator")]
|
||||
[GenerateAfter("Uno.SampleGenerators.MyCustomSourceGenerator")]
|
||||
public class AnotherCustomSourceGenerator : SourceGenerator
|
||||
{
|
||||
public override void Execute(SourceGeneratorContext context)
|
||||
|
|
|
@ -18,6 +18,7 @@ using Uno.SourceGeneration;
|
|||
|
||||
namespace Uno.SampleGenerators
|
||||
{
|
||||
[GenerateBefore("Uno.SampleGenerators.AnotherCustomSourceGenerator")]
|
||||
public class UselessGenerator : SourceGenerator
|
||||
{
|
||||
public override void Execute(SourceGeneratorContext context)
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
|
||||
namespace Test {
|
||||
class MyGeneratedType
|
||||
public static class MyGeneratedType
|
||||
{
|
||||
// Project: C:\s\Uno.SourceGenerator\Uno.SourceGenerator\Uno.SampleProject.UWP\Uno.SampleProject.UWP.csproj
|
||||
// Project: C:\src\uno.sourcegeneration\src\Uno.SampleProject.UWP\Uno.SampleProject.UWP.csproj
|
||||
public const string Project = @"C:\src\uno.sourcegeneration\src\Uno.SampleProject.UWP\Uno.SampleProject.UWP.csproj";
|
||||
}
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
|
||||
namespace Test {
|
||||
class MyGeneratedType
|
||||
public static class MyGeneratedType
|
||||
{
|
||||
// Project: /Users/admin/Documents/s/Uno.SourceGenerator/Uno.SourceGenerator/Uno.SampleProject/Uno.SampleProject.csproj
|
||||
// Project: C:\src\uno.sourcegeneration\src\Uno.SampleProject\Uno.SampleProject.csproj
|
||||
public const string Project = @"C:\src\uno.sourcegeneration\src\Uno.SampleProject\Uno.SampleProject.csproj";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
// ******************************************************************
|
||||
// Copyright <20> 2015-2018 nventive inc. All rights reserved.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// ******************************************************************
|
||||
using System;
|
||||
|
||||
namespace Uno.SourceGeneration
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a dependency between source generators
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Can be defined more than once for a generator.
|
||||
/// No effect when generator is not found.
|
||||
/// </remarks>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
|
||||
public class GenerateAfterAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Fully Qualified Name (FQN: namespace + class name) of the generator to execute before.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// No effect if the generator is not found.
|
||||
/// </remarks>
|
||||
public string GeneratorToExecuteBefore { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines a dependency between source generators
|
||||
/// </summary>
|
||||
/// <param name="generatorToExecuteBefore">
|
||||
/// Fully Qualified Name (FQN: namespace + class name) of the generator to execute before
|
||||
/// </param>
|
||||
public GenerateAfterAttribute(string generatorToExecuteBefore)
|
||||
{
|
||||
GeneratorToExecuteBefore = generatorToExecuteBefore;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
// ******************************************************************
|
||||
// Copyright <20> 2015-2018 nventive inc. All rights reserved.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// ******************************************************************
|
||||
using System;
|
||||
|
||||
namespace Uno.SourceGeneration
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a dependent source generators, which should be executed after
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Can be defined more than once for a generator.
|
||||
/// No effect when generator is not found.
|
||||
/// </remarks>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
|
||||
public class GenerateBeforeAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Fully Qualified Name (FQN: namespace + class name) of the generator to execute after.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// No effect if the generator is not found.
|
||||
/// </remarks>
|
||||
public string GeneratorToExecuteAfter { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines a dependent source generators, which should be executed after
|
||||
/// </summary>
|
||||
/// <param name="generatorToExecuteAfter">
|
||||
/// Fully Qualified Name (FQN: namespace + class name) of the generator to execute after.
|
||||
/// </param>
|
||||
public GenerateBeforeAttribute(string generatorToExecuteAfter)
|
||||
{
|
||||
GeneratorToExecuteAfter = generatorToExecuteAfter;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,12 +14,6 @@
|
|||
// limitations under the License.
|
||||
//
|
||||
// ******************************************************************
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Uno.SourceGeneration
|
||||
{
|
||||
public abstract class SourceGenerator
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace Uno.SourceGeneration
|
||||
{
|
||||
/// <summary>
|
||||
/// Define a dependency between source generators
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Can be define more than once for a generator.
|
||||
/// No effect when generator is not found.
|
||||
/// </remarks>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
|
||||
public class SourceGeneratorDependencyAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Fully Qualified Name (FQN: namespace + class name) of the generator to execute before.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// No effect if the generator is not found.
|
||||
/// </remarks>
|
||||
public string DependsOn { get; }
|
||||
|
||||
public SourceGeneratorDependencyAttribute(string dependsOn)
|
||||
{
|
||||
DependsOn = dependsOn;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -78,7 +78,8 @@
|
|||
<Compile Include="..\..\Build\AssemblyVersion.cs">
|
||||
<Link>AssemblyVersion.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="SourceGeneratorDependencyAttribute.cs" />
|
||||
<Compile Include="GenerateBeforeAttribute.cs" />
|
||||
<Compile Include="GenerateAfterAttribute.cs" />
|
||||
<Compile Include="SourceGeneratorExtensions.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="SourceGenerator.cs" />
|
||||
|
|
|
@ -77,33 +77,49 @@ namespace Uno.SourceGeneration.Host
|
|||
var generatorNames = generatorsByName.Keys;
|
||||
|
||||
// Dependencies list, in the form (before, after)
|
||||
var dependencies = details.Generators
|
||||
var afterGenerators = details.Generators
|
||||
.Select(g => g.generatorType)
|
||||
.SelectMany(t => t.GetCustomAttributes<SourceGeneratorDependencyAttribute>()
|
||||
.Select(a => a.DependsOn)
|
||||
.SelectMany(t => t.GetCustomAttributes<GenerateAfterAttribute>()
|
||||
.Select(a => a.GeneratorToExecuteBefore)
|
||||
.Intersect(generatorNames, StringComparer.InvariantCultureIgnoreCase)
|
||||
.Select(dependency => ((string incoming, string outgoing)) (t.FullName, dependency)))
|
||||
.Select(dependency => ((string incoming, string outgoing)) (t.FullName, dependency)));
|
||||
|
||||
var beforeGenerators = details.Generators
|
||||
.Select(g => g.generatorType)
|
||||
.SelectMany(t => t.GetCustomAttributes<GenerateBeforeAttribute>()
|
||||
.Select(a => a.GeneratorToExecuteAfter)
|
||||
.Intersect(generatorNames, StringComparer.InvariantCultureIgnoreCase)
|
||||
.Select(dependent => ((string incoming, string outgoing))(dependent, t.FullName)));
|
||||
|
||||
var dependencies = afterGenerators.Concat(beforeGenerators)
|
||||
.Where(x => x.incoming != x.outgoing)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
if (dependencies.Any())
|
||||
{
|
||||
this.Log().Info($"Generators Ordering restrictions:\n\t{dependencies.Select(d => $"{d.incoming} -> {d.outgoing}").JoinBy("\n\t")}");
|
||||
}
|
||||
|
||||
var groupedGenerators = generatorNames.GroupSort(dependencies);
|
||||
|
||||
if (groupedGenerators == null)
|
||||
{
|
||||
this.Log().Error($"There is a cyclic dependency in the generators:\n\t{dependencies.Select(d => $"{d.incoming} -> {d.outgoing}").JoinBy("\n\t")}");
|
||||
this.Log().Error("There is a cyclic ordering in the generators. You need to fix it. You may need to set your build output to 'normal' to see dependencies list.");
|
||||
return new string[0];
|
||||
}
|
||||
|
||||
if (dependencies.Any())
|
||||
{
|
||||
this.Log().Info($"Generators Dependencies:\n\t{dependencies.Select(d => $"{d.incoming} -> {d.outgoing}").JoinBy("\n\t")}");
|
||||
this.Log().Info($"**Generators Execution Plan**\n\tConcurrently: {groupedGenerators.Select(grp=>grp.JoinBy(", ")).JoinBy("\n\tFollowed by: ")}");
|
||||
}
|
||||
|
||||
// Run
|
||||
var output = new List<string>();
|
||||
(string filePath, string content)[] generatedFilesAndContent = null;
|
||||
|
||||
//Debugger.Launch();
|
||||
|
||||
foreach (var group in groupedGenerators)
|
||||
{
|
||||
if (generatedFilesAndContent != null)
|
||||
|
|
|
@ -32,6 +32,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Uno.SampleProject.UWP", "Un
|
|||
{D45247E2-A279-472C-8D8B-CBCB83F37FB1} = {D45247E2-A279-472C-8D8B-CBCB83F37FB1}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Doc", "Doc", "{DEB6C3E9-CC58-4925-B635-083634263ABC}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
..\readme.md = ..\readme.md
|
||||
..\doc\using sourcegenerators.md = ..\doc\using sourcegenerators.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SharedMSBuildProjectFiles) = preSolution
|
||||
Uno.SourceGenerationHost.Shared\Uno.SourceGenerationHost.Shared.projitems*{0a1cc471-6080-415f-8947-e41d8534395e}*SharedItemsImports = 4
|
||||
|
|
Загрузка…
Ссылка в новой задаче