From f3a03fc985c7f738e287969f1d641a8a7a0e9297 Mon Sep 17 00:00:00 2001 From: Talen Fisher Date: Wed, 15 Apr 2020 08:13:05 -0500 Subject: [PATCH] feat: Give Generators Access to MSBuild Properties (#210) * Add abiilty to pass msbuild properties to the generator * update readme * revert to item approach * escape semicolons when creating _CodeGenToolResponseFileLines * add sample using TargetFrameworks property to show escaping works * update doc comments in transformation context * add link to sample projects in readme, update changelog Co-authored-by: talenfisher --- CHANGELOG.md | 4 ++ README.md | 25 +++++++++ .../BuildPropsConsumer.csproj | 16 ++++++ samples/BuildPropsConsumer/Program.cs | 27 ++++++++++ .../BuildPropsGenerator.csproj | 17 ++++++ .../FrameworkInfoProviderGenerator.cs | 54 +++++++++++++++++++ samples/BuildPropsGenerator/build.ps1 | 11 ++++ .../CompilationGenerator.cs | 6 +++ .../DocumentTransform.cs | 5 +- .../build/BuildPluginPackage.targets | 1 + .../AddExampleBuildPropertyAttribute.cs | 16 ++++++ .../AddExampleBuildPropertyGenerator.cs | 45 ++++++++++++++++ .../CodeGeneration.Roslyn.Tests.csproj | 5 ++ .../CodeGenerationTests.cs | 12 +++++ .../Helpers/CompilationTestsBase.cs | 2 +- .../CommandLine/Enumerable.cs | 5 ++ src/CodeGeneration.Roslyn.Tool/Program.cs | 24 +++++++++ .../build/CodeGeneration.Roslyn.Tool.targets | 5 ++ .../TransformationContext.cs | 8 ++- 19 files changed, 285 insertions(+), 3 deletions(-) create mode 100644 samples/BuildPropsConsumer/BuildPropsConsumer.csproj create mode 100644 samples/BuildPropsConsumer/Program.cs create mode 100644 samples/BuildPropsGenerator/BuildPropsGenerator.csproj create mode 100644 samples/BuildPropsGenerator/FrameworkInfoProviderGenerator.cs create mode 100644 samples/BuildPropsGenerator/build.ps1 create mode 100644 src/CodeGeneration.Roslyn.Tests.Generators/AddExampleBuildPropertyAttribute.cs create mode 100644 src/CodeGeneration.Roslyn.Tests.Generators/AddExampleBuildPropertyGenerator.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c50d4c22..4b9c7f15d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added +* Support for accessing arbitrary MSBuild properties, see [Readme section](README.md#access-msbuild-properties) ([#210]) + ## [0.7.63] - 2020-04-08 @@ -43,6 +46,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [#178]: https://github.com/AArnott/CodeGeneration.Roslyn/pull/178 [#198]: https://github.com/AArnott/CodeGeneration.Roslyn/pull/198 [#205]: https://github.com/AArnott/CodeGeneration.Roslyn/pull/205 +[#210]: https://github.com/AArnott/CodeGeneration.Roslyn/pull/210 [v0.7 migration guide]: https://github.com/AArnott/CodeGeneration.Roslyn/wiki/Migrations#v07 diff --git a/README.md b/README.md index a3c997ad8..eca87ccf1 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,7 @@ Instructions on development and using this project's source code are in [CONTRIB - [Separate out the attribute](#separate-out-the-attribute) - [Create the metapackage](#create-the-metapackage) - [Add extra `build/` content in Plugin package](#add-extra-build-content-in-plugin-package) + - [Access MSBuild Properties](#access-msbuild-properties) ## How to write your own code generator @@ -543,6 +544,30 @@ to add custom MSBuild props/targets into NuGet package's `build` folder (and hav imported when package is referenced), you'll need to use `PackageBuildFolderProjectImport` ItemGroup, as shown in `PackagedGenerator` sample. +### Accesss MSBuild Properties + +You may access MSBuild property values of the project being generated for, by first adding the property +name to the `PluginRequestedProperty` item list. For example, if you want to access the TargetFramework build +property, you would do the following in your generator's .csproj file: + +```xml + + + +``` + +Then, you can access its value like this: + +```cs +public Task> GenerateAsync(TransformationContext context, IProgress progress, CancellationToken cancellationToken) +{ + var targetFramework = context.BuildProperties["TargetFramework"]; + // ... +} +``` + +> ℹ For a sample generator that accesses MSBuild properties, see [BuildPropsGenerator](samples/BuildPropsGenerator) and its consuming project, [BuildPropsConsumer](samples/BuildPropsConsumer) + [NuPkg]: https://nuget.org/packages/CodeGeneration.Roslyn [AttrNuPkg]: https://nuget.org/packages/CodeGeneration.Roslyn.Attributes [ToolNuPkg]: https://nuget.org/packages/CodeGeneration.Roslyn.Tool diff --git a/samples/BuildPropsConsumer/BuildPropsConsumer.csproj b/samples/BuildPropsConsumer/BuildPropsConsumer.csproj new file mode 100644 index 000000000..67eb94130 --- /dev/null +++ b/samples/BuildPropsConsumer/BuildPropsConsumer.csproj @@ -0,0 +1,16 @@ + + + + Exe + netcoreapp2.2;netcoreapp3.1 + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + diff --git a/samples/BuildPropsConsumer/Program.cs b/samples/BuildPropsConsumer/Program.cs new file mode 100644 index 000000000..18e3a8f7f --- /dev/null +++ b/samples/BuildPropsConsumer/Program.cs @@ -0,0 +1,27 @@ +using System; +using CodeGeneration.Roslyn; + +namespace BuildPropsConsumer +{ + + [CodeGenerationAttribute("BuildPropsGenerator.FrameworkInfoProviderGenerator, BuildPropsGenerator")] + class FrameworkInfoProviderAttribute : Attribute { } + + [FrameworkInfoProvider] + partial class Program + { + static void Main(string[] args) + { + var program = new Program(); + var frameworks = program.TargetFrameworks; + var currentFramework = program.CurrentTargetFramework; + + Console.WriteLine("This project is build for the following frameworks: "); + + foreach(var framework in frameworks) { + var message = framework == currentFramework ? $"{framework} (current)" : framework; + Console.WriteLine(message); + } + } + } +} diff --git a/samples/BuildPropsGenerator/BuildPropsGenerator.csproj b/samples/BuildPropsGenerator/BuildPropsGenerator.csproj new file mode 100644 index 000000000..426a4078a --- /dev/null +++ b/samples/BuildPropsGenerator/BuildPropsGenerator.csproj @@ -0,0 +1,17 @@ + + + + + + netcoreapp2.1 + true + + + + + + + + + + \ No newline at end of file diff --git a/samples/BuildPropsGenerator/FrameworkInfoProviderGenerator.cs b/samples/BuildPropsGenerator/FrameworkInfoProviderGenerator.cs new file mode 100644 index 000000000..abf93c1d3 --- /dev/null +++ b/samples/BuildPropsGenerator/FrameworkInfoProviderGenerator.cs @@ -0,0 +1,54 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using CodeGeneration.Roslyn; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace BuildPropsGenerator +{ + public class FrameworkInfoProviderGenerator : ICodeGenerator + { + public FrameworkInfoProviderGenerator(AttributeData attributeData) + { + } + + public Task> GenerateAsync(TransformationContext context, IProgress progress, CancellationToken cancellationToken) + { + var partialType = CreatePartialType(); + return Task.FromResult(SyntaxFactory.List(partialType)); + + IEnumerable CreatePartialType() + { + var newPartialType = + context.ProcessingNode is ClassDeclarationSyntax classDeclaration + ? SyntaxFactory.ClassDeclaration(classDeclaration.Identifier.ValueText) + : context.ProcessingNode is StructDeclarationSyntax structDeclaration + ? SyntaxFactory.StructDeclaration(structDeclaration.Identifier.ValueText) + : default(TypeDeclarationSyntax); + if (newPartialType is null) + yield break; + yield return newPartialType + ?.AddModifiers(SyntaxFactory.Token(SyntaxKind.PartialKeyword)) + .AddMembers(CreateTargetFrameworkListProperty(), CreateCurrentTargetFrameworkProperty()); + } + MemberDeclarationSyntax CreateTargetFrameworkListProperty() + { + var collectionType = "System.Collections.Generic.List"; + var frameworks = context.BuildProperties["TargetFrameworks"]; + var quotedFrameworks = frameworks.Split(";").Select(framework => $"\"{framework}\""); + var commaDelimitedFrameworks = string.Join(',', quotedFrameworks.ToArray()); + + return SyntaxFactory.ParseMemberDeclaration($"public {collectionType} TargetFrameworks {{ get; }} = new {collectionType} {{ {commaDelimitedFrameworks} }};"); + } + MemberDeclarationSyntax CreateCurrentTargetFrameworkProperty() + { + var framework = context.BuildProperties["TargetFramework"]; + return SyntaxFactory.ParseMemberDeclaration($"public string CurrentTargetFramework {{ get; }} = \"{framework}\";"); + } + } + } +} \ No newline at end of file diff --git a/samples/BuildPropsGenerator/build.ps1 b/samples/BuildPropsGenerator/build.ps1 new file mode 100644 index 000000000..1b9f04ac8 --- /dev/null +++ b/samples/BuildPropsGenerator/build.ps1 @@ -0,0 +1,11 @@ +#!/usr/bin/env pwsh + +Write-Host "Running in $PSScriptRoot" -ForegroundColor Cyan +Push-Location $PSScriptRoot +try { + Write-Host "dotnet build" -ForegroundColor Green + dotnet build +} +finally { + Pop-Location +} \ No newline at end of file diff --git a/src/CodeGeneration.Roslyn.Engine/CompilationGenerator.cs b/src/CodeGeneration.Roslyn.Engine/CompilationGenerator.cs index 2600f5db7..15a6be7aa 100644 --- a/src/CodeGeneration.Roslyn.Engine/CompilationGenerator.cs +++ b/src/CodeGeneration.Roslyn.Engine/CompilationGenerator.cs @@ -53,6 +53,11 @@ public class CompilationGenerator /// public IReadOnlyList PluginPaths { get; set; } = new List(); + /// + /// Gets or sets the build properties to expose to generators. + /// + public IReadOnlyDictionary BuildProperties { get; set; } + /// /// Gets or sets the path to the directory that contains generated source files. /// @@ -122,6 +127,7 @@ public async Task GenerateAsync(IProgress progress = null, Cancellat compilation, inputSyntaxTree, this.ProjectDirectory, + this.BuildProperties, this.LoadPlugin, progress, cancellationToken); diff --git a/src/CodeGeneration.Roslyn.Engine/DocumentTransform.cs b/src/CodeGeneration.Roslyn.Engine/DocumentTransform.cs index 8c0a0e4ec..fc6242c66 100644 --- a/src/CodeGeneration.Roslyn.Engine/DocumentTransform.cs +++ b/src/CodeGeneration.Roslyn.Engine/DocumentTransform.cs @@ -40,6 +40,7 @@ public static class DocumentTransform /// The compilation to which the document belongs. /// The document to scan for generator attributes. /// The path of the .csproj project file. + /// MSBuild properties to expose to the generator. /// A function that can load an assembly with the given name. /// Reports warnings and errors in code generation. /// The token to monitor for cancellation requests. @@ -48,6 +49,7 @@ public static async Task TransformAsync( CSharpCompilation compilation, SyntaxTree inputDocument, string projectDirectory, + IReadOnlyDictionary buildProperties, Func assemblyLoader, IProgress progress, CancellationToken cancellationToken) @@ -91,7 +93,8 @@ public static async Task TransformAsync( compilation, projectDirectory, emittedUsings, - emittedExterns); + emittedExterns, + buildProperties); var richGenerator = generator as IRichCodeGenerator ?? new EnrichingCodeGeneratorProxy(generator); diff --git a/src/CodeGeneration.Roslyn.Plugin.Sdk/build/BuildPluginPackage.targets b/src/CodeGeneration.Roslyn.Plugin.Sdk/build/BuildPluginPackage.targets index 512fb6655..7a3cb2d30 100644 --- a/src/CodeGeneration.Roslyn.Plugin.Sdk/build/BuildPluginPackage.targets +++ b/src/CodeGeneration.Roslyn.Plugin.Sdk/build/BuildPluginPackage.targets @@ -64,6 +64,7 @@ @(_PackageBuildFolderProjectImport_props->' ', '%0D%0A') +@(PluginRequestedProperty->' ', '%0D%0A') ]]> diff --git a/src/CodeGeneration.Roslyn.Tests.Generators/AddExampleBuildPropertyAttribute.cs b/src/CodeGeneration.Roslyn.Tests.Generators/AddExampleBuildPropertyAttribute.cs new file mode 100644 index 000000000..823e8913c --- /dev/null +++ b/src/CodeGeneration.Roslyn.Tests.Generators/AddExampleBuildPropertyAttribute.cs @@ -0,0 +1,16 @@ +// Copyright (c) Andrew Arnott. All rights reserved. +// Licensed under the MS-PL license. See LICENSE.txt file in the project root for full license information. + +namespace CodeGeneration.Roslyn.Tests.Generators +{ + using System; + using System.Diagnostics; + using Validation; + + [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)] + [CodeGenerationAttribute(typeof(AddExampleBuildPropertyGenerator))] + [Conditional("CodeGeneration")] + public class AddExampleBuildPropertyAttribute : Attribute + { + } +} diff --git a/src/CodeGeneration.Roslyn.Tests.Generators/AddExampleBuildPropertyGenerator.cs b/src/CodeGeneration.Roslyn.Tests.Generators/AddExampleBuildPropertyGenerator.cs new file mode 100644 index 000000000..f0b3e2cd6 --- /dev/null +++ b/src/CodeGeneration.Roslyn.Tests.Generators/AddExampleBuildPropertyGenerator.cs @@ -0,0 +1,45 @@ +// Copyright (c) Andrew Arnott. All rights reserved. +// Licensed under the MS-PL license. See LICENSE.txt file in the project root for full license information. + +namespace CodeGeneration.Roslyn.Tests.Generators +{ + using System; + using System.Collections.Generic; + using System.Collections.Immutable; + using System.Linq; + using System.Text; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.CSharp; + using Microsoft.CodeAnalysis.CSharp.Syntax; + using Validation; + using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; + + public class AddExampleBuildPropertyGenerator : ICodeGenerator + { + public AddExampleBuildPropertyGenerator(AttributeData attributeData) + { + Requires.NotNull(attributeData, nameof(attributeData)); + } + + public Task> GenerateAsync(TransformationContext context, IProgress progress, CancellationToken cancellationToken) + { + var partialClass = GeneratePartialClass(); + return Task.FromResult(SyntaxFactory.List(partialClass)); + + IEnumerable GeneratePartialClass() + { + var classDeclaration = context.ProcessingNode as ClassDeclarationSyntax; + yield return classDeclaration + .AddMembers(CreateExampleBuildProperty()); + } + + MemberDeclarationSyntax CreateExampleBuildProperty() + { + var value = context.BuildProperties["ExampleBuildProperty"]; + return SyntaxFactory.ParseMemberDeclaration($"public string ExampleBuildProperty {{ get; }} = \"{value}\";"); + } + } + } +} \ No newline at end of file diff --git a/src/CodeGeneration.Roslyn.Tests/CodeGeneration.Roslyn.Tests.csproj b/src/CodeGeneration.Roslyn.Tests/CodeGeneration.Roslyn.Tests.csproj index b8cca48e1..efcdfb158 100644 --- a/src/CodeGeneration.Roslyn.Tests/CodeGeneration.Roslyn.Tests.csproj +++ b/src/CodeGeneration.Roslyn.Tests/CodeGeneration.Roslyn.Tests.csproj @@ -7,8 +7,13 @@ $(MSBuildWarningsAsMessages);CGR1002 OverrideCodeGenToolPath;$(GenerateCodeFromAttributesDependsOn) + c7189d5e-495c-4cab-8e18-ab8d7ab71a2e + + + + diff --git a/src/CodeGeneration.Roslyn.Tests/CodeGenerationTests.cs b/src/CodeGeneration.Roslyn.Tests/CodeGenerationTests.cs index 787c2b365..20d991278 100644 --- a/src/CodeGeneration.Roslyn.Tests/CodeGenerationTests.cs +++ b/src/CodeGeneration.Roslyn.Tests/CodeGenerationTests.cs @@ -38,6 +38,13 @@ public void NuGetRecordGeneratorWorks() record.ToBuilder(); } + [Fact] + public void AccessingBuildPropertiesWorks() + { + var objWithBuildProp = new ClassWithExampleBuildProperty(); + Assert.Equal("c7189d5e-495c-4cab-8e18-ab8d7ab71a2e", objWithBuildProp.ExampleBuildProperty); + } + public partial class Wrapper { [ExternalDuplicateWithSuffixByName("Suffix")] @@ -66,4 +73,9 @@ public partial class MultipliedBar [Test(X = 10, Y = 20)] public string Value { get; set; } } + + [AddExampleBuildProperty] + public partial class ClassWithExampleBuildProperty + { + } } diff --git a/src/CodeGeneration.Roslyn.Tests/Helpers/CompilationTestsBase.cs b/src/CodeGeneration.Roslyn.Tests/Helpers/CompilationTestsBase.cs index 29c5483a7..25e8c5cf3 100644 --- a/src/CodeGeneration.Roslyn.Tests/Helpers/CompilationTestsBase.cs +++ b/src/CodeGeneration.Roslyn.Tests/Helpers/CompilationTestsBase.cs @@ -79,7 +79,7 @@ protected static async Task GenerateAsync(string source) var diagnostics = compilation.GetDiagnostics(); Assert.Empty(diagnostics.Where(x => x.Severity >= DiagnosticSeverity.Warning)); var progress = new Progress(); - var result = await DocumentTransform.TransformAsync(compilation, tree, null, Assembly.Load, progress, CancellationToken.None); + var result = await DocumentTransform.TransformAsync(compilation, tree, null, null, Assembly.Load, progress, CancellationToken.None); return result; } diff --git a/src/CodeGeneration.Roslyn.Tool/CommandLine/Enumerable.cs b/src/CodeGeneration.Roslyn.Tool/CommandLine/Enumerable.cs index 57f6d2171..96ee4e113 100644 --- a/src/CodeGeneration.Roslyn.Tool/CommandLine/Enumerable.cs +++ b/src/CodeGeneration.Roslyn.Tool/CommandLine/Enumerable.cs @@ -64,6 +64,11 @@ public static T[] ToArray(this IEnumerable source) return System.Linq.Enumerable.ToArray(source); } + public static Dictionary ToDictionary(this IEnumerable source, Func keySelector, Func elementSelector) + { + return System.Linq.Enumerable.ToDictionary(source, keySelector, elementSelector); + } + public static T Last(this IEnumerable source) { return System.Linq.Enumerable.Last(source); diff --git a/src/CodeGeneration.Roslyn.Tool/Program.cs b/src/CodeGeneration.Roslyn.Tool/Program.cs index 03e094f95..50a7086b1 100644 --- a/src/CodeGeneration.Roslyn.Tool/Program.cs +++ b/src/CodeGeneration.Roslyn.Tool/Program.cs @@ -39,16 +39,20 @@ private static async Task Core(string[] args, CancellationToken cancellatio IReadOnlyList refs = Array.Empty(); IReadOnlyList preprocessorSymbols = Array.Empty(); IReadOnlyList plugins = Array.Empty(); + IReadOnlyList buildPropertiesList = Array.Empty(); string generatedCompileItemFile = null; string outputDirectory = null; string projectDir = null; bool version = false; + Dictionary buildProperties = new Dictionary(); + ArgumentSyntax.Parse(args, syntax => { syntax.DefineOption("version", ref version, "Show version of this tool (and exit)."); syntax.DefineOptionList("r|reference", ref refs, "Paths to assemblies being referenced"); syntax.DefineOptionList("d|define", ref preprocessorSymbols, "Preprocessor symbols"); syntax.DefineOptionList("plugin", ref plugins, "Paths to generator plugin assemblies"); + syntax.DefineOptionList("buildProperty", ref buildPropertiesList, false, "MSBuild properties to expose to generators"); syntax.DefineOption("out", ref outputDirectory, true, "The directory to write generated source files to"); syntax.DefineOption("projectDir", ref projectDir, true, "The absolute path of the directory where the project file is located"); syntax.DefineOption("generatedFilesList", ref generatedCompileItemFile, "The path to the file to create with a list of generated source files"); @@ -72,6 +76,20 @@ private static async Task Core(string[] args, CancellationToken cancellatio return 2; } + foreach (var prop in buildPropertiesList) + { + var i = prop.IndexOf("="); + + if (i <= 0) + { + continue; + } + + var key = prop.Substring(0, i); + var value = prop.Substring(i + 1); + buildProperties[key] = value; + } + var generator = new CompilationGenerator { ProjectDirectory = projectDir, @@ -79,6 +97,7 @@ private static async Task Core(string[] args, CancellationToken cancellatio ReferencePath = Sanitize(refs), PreprocessorSymbols = preprocessorSymbols, PluginPaths = Sanitize(plugins), + BuildProperties = Sanitize(buildProperties), IntermediateOutputDirectory = outputDirectory, }; @@ -117,5 +136,10 @@ private static IReadOnlyList Sanitize(IReadOnlyList inputs) { return inputs.Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.Trim()).ToArray(); } + + private static IReadOnlyDictionary Sanitize(IReadOnlyDictionary inputs) + { + return inputs.Where(x => !string.IsNullOrWhiteSpace(x.Value)).ToDictionary(x => x.Key, x => x.Value.Trim()); + } } } \ No newline at end of file diff --git a/src/CodeGeneration.Roslyn.Tool/build/CodeGeneration.Roslyn.Tool.targets b/src/CodeGeneration.Roslyn.Tool/build/CodeGeneration.Roslyn.Tool.targets index c63af0649..85fa5173c 100644 --- a/src/CodeGeneration.Roslyn.Tool/build/CodeGeneration.Roslyn.Tool.targets +++ b/src/CodeGeneration.Roslyn.Tool/build/CodeGeneration.Roslyn.Tool.targets @@ -25,6 +25,10 @@ Include="@(Compile)" Condition=" '%(Compile.Generator)' == 'MSBuild:GenerateCodeFromAttributes' " /> <_CodeGenToolInputs_DefineConstants Include="$(DefineConstants)" /> + <_CodeGenerationRoslynResolvedProperty Include="@(CodeGenerationRoslynRequestedProperty->Distinct())"> + %(Identity) + $(%(Identity)) + @(ReferencePath->'-r;%(Identity)'); @(_CodeGenToolInputs_DefineConstants->'-d;%(Identity)'); + @(_CodeGenerationRoslynResolvedProperty->'%(Key)=%(Value)'->Replace(';', '%3B')->'--buildProperty;%(Identity)'); @(CodeGenerationRoslynPlugin->'--plugin;%(Identity)'); --out; $(IntermediateOutputPath); diff --git a/src/CodeGeneration.Roslyn/TransformationContext.cs b/src/CodeGeneration.Roslyn/TransformationContext.cs index 394c2effb..deffbdf89 100644 --- a/src/CodeGeneration.Roslyn/TransformationContext.cs +++ b/src/CodeGeneration.Roslyn/TransformationContext.cs @@ -22,13 +22,15 @@ public class TransformationContext /// The absolute path of the directory where the project file is located. /// The using directives already queued to be generated. /// The extern aliases already queued to be generated. + /// MSBuild properties requested by plugins using PluginRequestedProperty/CodeGenerationRoslynRequestedProperty items. public TransformationContext( CSharpSyntaxNode processingNode, SemanticModel semanticModel, CSharpCompilation compilation, string projectDirectory, IEnumerable compilationUnitUsings, - IEnumerable compilationUnitExterns) + IEnumerable compilationUnitExterns, + IReadOnlyDictionary buildProperties) { ProcessingNode = processingNode; SemanticModel = semanticModel; @@ -36,6 +38,7 @@ public TransformationContext( ProjectDirectory = projectDirectory; CompilationUnitUsings = compilationUnitUsings; CompilationUnitExterns = compilationUnitExterns; + BuildProperties = buildProperties; } /// Gets the syntax node the generator attribute is found on. @@ -55,5 +58,8 @@ public TransformationContext( /// Gets a collection of extern aliases already queued to be generated. public IEnumerable CompilationUnitExterns { get; } + + /// Gets a dictionary of MSBuild properties requested by plugins using PluginRequestedProperty/CodeGenerationRoslynRequestedProperty items. + public IReadOnlyDictionary BuildProperties { get; } } }