• Stars
    star
    129
  • Rank 279,262 (Top 6 %)
  • Language
    C#
  • License
    Other
  • Created almost 7 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A Roslyn based C# source generation framework

Uno SourceGenerator

The Uno source generator is an API compatible source generator inspired by Roslyn v2.0 source generation feature, and an msbuild task which executes the SourceGenerators.

It provides a way to generate C# source code based on a project being built, using all of its syntactic and semantic model information.

The Uno Source Generator also supports the Roslyn 3.8 (C# 9.0) Source Generator APIs, see below for more details. Note that as of Roslyn 3.8, generators do not support multi-pass generation where generators can depend on each others. In order to benefit from this feature, a generator must run using Uno.SourceGenerationTasks.

Using this generator allows for a set of generators to share the same Roslyn compilation context, which is particularly expensive to create, and run all the generators in parallel.

The Uno.SourceGeneratorTasks support updating generators on the fly, making iterative development easier as visual studio or MSBuild will not lock the generator's assemblies.

The Uno.SourceGeneratorTasks support any target framework for code generation, though there are limitations when using a mixed targetframeworks graph, such as generating code in a net47 project that references a netstandard2.0 project. In such cases, prefer adding a net47 target instead of targeting netstandard2.0.

Visual Studio 2017 15.3+ for Windows, macOS and Linux builds are supported. Building for .NET Core requires .NET 3.0.100 or later.

Build status

Target Branch Status
development master Build Status

Nuget Packages

Package nuget.org usage
Uno.SourceGeneration NuGet Use this package to create a generator
Uno.SourceGenerationTasks NuGet Use this package to use a generator

Experimental packages are available through this NuGet feed: https://pkgs.dev.azure.com/uno-platform/Uno%20Platform/_packaging/unoplatformdev/nuget/v3/index.json

Creating a Source Generator

  1. In Visual Studio 2017, create a .NET Standard Class Library project named MyGenerator

  2. In the csproj file

    1. Change the TargetFramework to net46
    2. Add a package reference to Uno.SourceGeneration (take the latest version)
    <ItemGroup>
        <PackageReference Include="Uno.SourceGeneration" Version="1.5.0" />
    </ItemGroup>
  3. Add a new source file containing this code :

    using System;
    using Uno.SourceGeneration;
    
    namespace MyGenerator
    {
        public class MyCustomSourceGenerator : SourceGenerator
        {
            public override void Execute(SourceGeneratorContext context)
            {
                var project = context.GetProjectInstance();
    
                context.AddCompilationUnit("Test", "namespace MyGeneratedCode { class TestGeneration { } }");
            }
        }
    }

    Note that the GetProjectInstance is a helper method that provides access to the msbuild project currently being built. It provides access to the msbuild properties and item groups of the project, allowing for fine configuration of the source generator.

  4. Create a file named MyGenerator.props (should be the name of your project + .props) in a folder named build and set its Build Action to Content. Put the following content:

    <Project>
        <ItemGroup>
            <SourceGenerator Include="$(MSBuildThisFileDirectory)..\bin\$(Configuration)\net46\MyGenerator.dll"
                    Condition="Exists('$(MSBuildThisFileDirectory)..\bin')" />
            <SourceGenerator Include="$(MSBuildThisFileDirectory)..\tools\MyGenerator.dll"
                    Condition="Exists('$(MSBuildThisFileDirectory)..\tools')" />
        </ItemGroup>
    </Project>

Using the generator inside the same solution (another project)

  1. In Visual Studio 2017, create a .NET Standard Class Library project named MyLibrary. This is the project where your generator will do its generation.
  2. In the .csproj file:
    1. Change the TargetFramework to net46 (.Net Framework v4.6)
    2. Add a package reference to Uno.SourceGenerationTasks
    <ItemGroup>
        <PackageReference Include="Uno.SourceGenerationTasks" Version="1.5.0" />
    </ItemGroup>

    *You can also use the Nuget Package Manager to add this package reference. The version can differ, please use the same than the generator project.

    1. Import the source generator by placing the following line at the end :
    <Import Project="..\MyGenerator\build\MyGenerator.props" />
  3. Add some C# code that uses the MyGeneratedCode.TestGeneration class that the generator creates.
  4. Compile... it should works.
  5. You can sneak at the generated code by clicking the Show All Files button in the Solution Explorer. The code will be in the folder obj\<config>\<platform>\g\<generator name>\.

Packaging the source generator in NuGet

Packaging the generator in nuget requires to :

  1. In the csproj file containing your generator:

    1. Add this group to your csproj:
    <ItemGroup>
        <Content Include="build/**/*.*">
        <Pack>true</Pack>
        <PackagePath>build</PackagePath>
        </Content>
    </ItemGroup>
    

    Note that the name of this file must match the package name to be taken into account by nuget.

    1. Update the package references as follows

      <ItemGroup>
          <PackageReference Include="Uno.SourceGeneration" Version="1.19.0-dev.316" PrivateAssets="All" />
          <PackageReference Include="Uno.SourceGenerationTasks" Version="1.19.0-dev.316" PrivateAssets="None" />
      </ItemGroup>

      This ensure that the source generator tasks will be included in any project referencing your new generator, and that the source generation interfaces are not included.

      *You can also use the Nuget Package Manager to add this package reference. The version can differ, please take the latest stable one.

    2. Add the following property:

      <PropertyGroup>
          <IsTool>true</IsTool>
      </PropertyGroup>

      This will allow for the generator package to be installed on any target framework.

Creating a C# 9.0 compatible generator

Based on C# 9.0 generators the bootstrapper defines a set of APIs that are compatible with Roslyn.

Here's a roslyn compatible generator:

[Generator]
public class CustomGenerator : ISourceGenerator
{
    public void Initialize(GeneratorInitializationContext context) {}

    public void Execute(GeneratorExecutionContext context)
    {
        context.AddSource("myGeneratedFile.cs", @"
namespace GeneratedNamespace
{
    public class GeneratedClass
    {
        public static void GeneratedMethod()
        {
            // generated code
        }
    }
}");
    }
}

Uno also provides a set of methods giving access to the MSBuild properties and items, compatible Uno's source generation tasks:

public void Execute(GeneratorExecutionContext context)
{
	var myProperty = context.GetMSBuildPropertyValue("MyTestProperty");

    var myItems = context.GetMSBuildPropertyValue("GetMSBuildItems").Select(i => i.Identity);
}

Note that the a generator running under Uno.SourceGenerationTasks does not need to define in MSBuild which properties need to be used, whereas C# 9.0 requires it.

Debugging a generator

In your generator, add the following in the SourceGenerator.Execute override :

Debugger.Launch();

This will open another visual studio instance, and allow for stepping through the generator's code.

General guidelines for creating generators

  • 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:

    [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. If you don't declare those dependencies, when a project is loaded to be analyzed, all generated files 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.

  • You can also define a generator which must be executed after yours. To do this, you need to declare a dependent generator:

    [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:

    taskkill /fi "imagename eq msbuild.exe" /f /t

Logging to build output

You can write to build output using the following code:

    public override void Execute(SourceGeneratorContext context)
    {
        var logger = context.GetLogger(); // this is an extension method on SourceGeneratorContext

        // Log something to build output when the mode is "detailed".
        logger.Debug($"The current count is {count}");

        // Log something to build output when the mode is "normal".
        logger.Info($"A total of {filesCount} has been generated.");

        // Log something as warning in build output.
        logger.Warn($"No code generated because the mode is {currentMode}.");

        // Log something as error in build output.
        logger.Error($"Unable to open file {filename}. No code generated.");
    }

Available Properties

The source generation task provides set of properties that can alter its behavior based on your project.

UnoSourceGeneratorAdditionalProperty

The UnoSourceGeneratorAdditionalProperty item group provides the ability for a project to enable the propagation of specific properties to the generators. This may be required if properties are injected dynamically, or provided as global variables.

A good example of this is the JavaSdkDirectory that is generally injected as a global parameter through the msbuild command line.

In such as case, add the following in your project file:

<ItemGroup>
    <UnoSourceGeneratorAdditionalProperty Include="JavaSdkDirectory" />
</ItemGroup>

In this case, the JavaSdkDirectory value will be captured in the original build environment, and provided to the generators' build environment.

Troubleshooting

Error: Failed to analyze project file ..., the targets ... failed to execute.

This is issue is caused by a open Roslyn issue for which all projects of the solution must have all the possible "head" projects configuration.

For instance, if you are building a UWP application, all the projects in the solution must have the 10.0.xxx target framework defined, even if netstandard2.0 would have been enough.

Generation output

The source generator provides additional details when building, when running the _UnoSourceGenerator msbuild target.

To view this information either place visual studio in details verbosity (Options, Projects and Solutions, Build and Run then MSBuild project build output verbosity) or by using the excellent MSBuild Binary and Structured Log Viewer from Kirill Osenkov.

The source generation target can also optionally produces binlog file in the obj folder, used to troubleshoot issues with the msbuild instance created inside the application domain used to generate the code. The path for those files can be altered using the UnoSourceGeneratorBinLogOutputPath msbuild property. In the context of a VSTS build, setting it to $(build.artifactstagingdirectory) allows for an improved diagnostics experience. Set the UnoSourceGeneratorUnsecureBinLogEnabled property to true to enabled binary logging.

Important: The binary logger may leak secret environment variables, it is a best practice to never enable this feature as part of normal build.

My build ends with error code 3

By default, in some cases, the source generation host will run into an internal error, and will exit without providing details about the generation error.

To enable the logging of these errors, add the following property to your project:

<UnoSourceGeneratorCaptureGenerationHostOutput>true</UnoSourceGeneratorCaptureGenerationHostOutput>

The errors will the be visible when the build logging output is set to detailed, or by using the binary logger.

Have questions? Feature requests? Issues?

Make sure to visit our StackOverflow, create an issue or visit our gitter.

Upgrade notes

earlier versions to 1.29

A breaking change has been introduced to support proper UWP head projects, and when upgrading to Uno.SourceGenerationTasks 1.29 or later you will have to search for projects that contain the uap10.0 target framework and do the following:

  • Update to the MSBuild.Sdk.Extras 1.6.65 or later
  • Choose an UWP sdk version, and use the appropriate target framework (e.g. uap10.0 to uap10.0.16299)

More Repositories

1

uno

Open-source platform for building cross-platform native Mobile, Web, Desktop and Embedded apps quickly. Create rich, C#/XAML, single-codebase apps from any IDE. Hot Reload included! 90m+ NuGet Downloads!!
C#
8,817
star
2

Uno.Wasm.Bootstrap

A simple nuget package to run C# code in a WASM-compatible browser
WebAssembly
370
star
3

Uno.Samples

A collection of code samples for the Uno Platform
C#
214
star
4

Uno.Playground

Source code for the Uno Gallery apps and Uno Playground (made in Wasm)
C#
214
star
5

Uno.QuickStart

An Uno "Hello world!" project using Windows UWP, Linux, iOS, Android and WebAssembly
C#
173
star
6

Uno.Themes

This library is designed to help you use the Material, Fluent or Cupertino design system with the Uno Platform
C#
168
star
7

Uno.Gallery

The Uno Platform Gallery application
C#
152
star
8

workshops

workshops, study guides and learning materials for the Uno Platform
C#
97
star
9

Uno.CodeGen

A set of source generators for equality, immutability, ...
C#
90
star
10

uno.toolkit.ui

A set of custom controls for the WinUI and the Uno Platform not offered out of the box by WinUI, such as Card, TabBar, NavigationBar, etc.
C#
82
star
11

uado

Universal Azure DevOps Organizer - Uno Reference Implementation project
C#
81
star
12

Uno.Ch9

Ch9 - Uno Reference Implementation project
C#
72
star
13

uno.extensions

Libraries to ease common developer tasks associated with building multi-platform mobile, desktop and web applications using Uno Platform or WinAppSDK.
C#
72
star
14

Uno.GettingStartedTutorial

A getting started with the Uno Platform Tutorial
C#
50
star
15

uno.todo

Uno Platform "Uno To Do" reference app
C#
47
star
16

Uno.Core

Uno.Core is a set of helpers and extension methods used to accelerate development.
C#
39
star
17

Uno.SQLitePCLRaw.Wasm

An SQLiteRaw WebAssembly provider for SQLitePCLRaw.core
C#
35
star
18

uno.check

CLI tool to setup your environment to build Uno Platform apps
C#
30
star
19

Elmish.Uno

Static UWP views for elmish programs running with the Uno Platform
F#
26
star
20

uno.fabulous

F#
22
star
21

uno.templates

Uno Platform project templates
C#
16
star
22

uno.resizetizer

The home for Uno.Resizetizer, an image resizting tooling for Uno Platform apps
C#
15
star
23

Uno.UITest

Unified UI Testing Framework for Uno Platform based applications
C#
12
star
24

RpnCalculator

The Xamarin.Forms RPN (Reverse Polish Notation) calculator running in WebAssembly
C#
11
star
25

Uno.Roslyn

A set of roslyn extensions and helpers packaged as content files to avoid extertnal dependencies in analyzers and source generators
C#
11
star
26

uno.blazor.native.todoapp

A Todo app using Uno WebAssembly Renderers for Blazor Native
C#
11
star
27

uno.fonts

Uno Platform Fonts
PowerShell
10
star
28

Uno.PackageDiff

A command line tool that compares two versions of a NuGet package and provides public API differences
C#
10
star
29

talks

conference, meetup and hackathon presentation materials for the Uno Platform
C#
10
star
30

Uno.Wasm.WebSockets

An Uno Platform implementation of .NET System.Net.WebSocket for WebAssembly
C#
10
star
31

Uno.DotnetRuntime.WebAssembly

template for brand new github repositories
PowerShell
7
star
32

uno.ui.runtimetests.engine

In-App MS Tests Runner for Uno Platform and WinAppSDK
C#
7
star
33

Uno.LottieSample

C#
7
star
34

uno.xamlmerge.task

C#
7
star
35

cybertruck

Cybertruck demo in XAML with the Uno Platform
C#
6
star
36

styleguide

style guide and brand assets for the Uno Platform
5
star
37

uno.dopesbench

C#
5
star
38

Uno.UITests.Helpers

Xamarin.UITests Helpers for the Uno Platforms for iOS and Android
C#
4
star
39

template

template for brand new github repositories
4
star
40

Uno.Diagnostics.Eventing

A set of packages and tools that enable the use of the Windows Performance Analyzer tooling with iOS, Android and WebAssembly.
C#
3
star
41

winui3-convert

A command line tool to convert UWP project libraries to WinUI 3 for .NET 5
C#
3
star
42

dotnet-azdo-artifacts-multidownload

template for brand new github repositories
C#
2
star
43

Uno.MonoAnalyzers

A set of Roslyn C# analyzers for Xamarin and Mono-based code bases
C#
2
star
44

Uno.MSAL.Graph.Demo

Uno Demo app for using MS Graph API with Uno version of MSAL
C#
2
star
45

performance

This repo contains benchmarks used for testing the performance of the Uno Platform
C#
2
star
46

Uno.UI.Toolkit.SL

The Silverlight Toolkit ported to the Uno Platform
C#
2
star
47

uno.extensions.logging

Set of loggers compatible with Microsoft.Extensions.Logging. Provides an OSLog logger, and a browser console WebAssembly logger
C#
2
star
48

Uno.Microsoft.Identity.Client

Fork of https://github.com/AzureAD/microsoft-authentication-library-for-dotnet to add support for Uno-WASM
C#
1
star
49

docker

template for brand new github repositories
Dockerfile
1
star