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:
Carl de Billy 2018-02-07 13:10:32 -05:00 коммит произвёл GitHub
Родитель a307a6a184 cb1e09ec2b
Коммит 7cb6ded699
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
12 изменённых файлов: 148 добавлений и 52 удалений

Просмотреть файл

@ -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