Rosalina icon indicating copy to clipboard operation
Rosalina copied to clipboard

Source generator for Unity 2021

Open trondtactile opened this issue 2 years ago • 13 comments

Unity 2021 supports Source Generators:

  • https://docs.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/source-generators-overview
  • https://docs.unity3d.com/Manual/roslyn-analyzers.html

Instead of having to manually click "generate", this would just generate the code when compiling, meaning your assets would always be in sync with your generated code.

trondtactile avatar Jun 16 '22 14:06 trondtactile

Rosalina uses an AssetProcessor from Unity to detect when a UIDocument has changed to generate the associated binding script. This process is automatic and triggered when the .uxml is saved. https://github.com/Eastrall/Rosalina/blob/main/Editor/Scripts/RosalinaAssetProcessor.cs#L12

I don't know much about source generators, but if it's a compile-time (or build) action, this will not fit into the code generation process since the binding script should be always synchronized with the UIDocument. This is why I used an AssetProcessor to do so.

Of course, you can "force" the binding script generation through the Rosalina > Generate UI bindings but this is only useful if there is a problem with the AssetProcessor. Not mandatory to use it everytime 😉

Eastrall avatar Jun 17 '22 07:06 Eastrall

It's really nice that this is automatic on asset change! Did not notice that.

One upside with Source Generators is that their output is not actual files in the project, but just compiled classes. And when using frameworks with "code behind" generation, the classes are indeed reflected on asset change.

However, as Unity is a special little snowflake, maybe this approach would not work very well, as one does not "build" Unity in order to compile it.

trondtactile avatar Jun 20 '22 13:06 trondtactile

Indeed, if it was a "pure" C# project, this would be a nice solution. Closing this for now, but keeping the idea in the back of my mind. 😉

Eastrall avatar Jun 20 '22 20:06 Eastrall

So I took a stab at running Rosalina as a Source Generator, and... it's really working great. There is no real problem in regards to picking up on changes in the Uxml files, since the changes are picked up as soon as you do anything in your IDE (which you'd want to do anyway if you've added/changed a named VisualElement). It's fantastic to have all bindings up to date in your IDE at all times without the extra recompile! I can supply the code when I've cleaned it up a bit. image

blepmlem avatar Sep 04 '22 15:09 blepmlem

That's great @blepmlem ! Would love to see how you achieve to implement source generators within Unity. Feel free to open a PR and contribute 😄

Reopening the issue for better tracking.

Eastrall avatar Sep 06 '22 07:09 Eastrall

My fork is located at https://github.com/blepmlem/Rosalina/tree/source-generator I've modified it quite a bit for my own purposes, but the general Source Generator work should be pretty universal! (I look for scripts implementing a specific interface, and don't do anything with MonoBehaviours and UIDocuments since I use this tool for both editor and runtime UI 😄)

blepmlem avatar Sep 07 '22 15:09 blepmlem

The gist of it is that I implement an ISyntaxReceiver for finding the relevant scripts to create partial classes for, then pass the information to the RosalinaBindingsGenerator, it does its thing as usual, and the output code is added to the compilation process.

blepmlem avatar Sep 07 '22 15:09 blepmlem

Wow, that is awesome! I'll certainly try to get this capability into my fork (unless it can be added to this repo somehow).

trondtactile avatar Sep 08 '22 11:09 trondtactile

@blepmlem this is a great Proof Of Concept you made here! I am going checkout your code and see how it works, in order to understand properly what's going on within Unity and the source generator. Aditionnaly, this would solve the "Pathing support" described in issue #13 by @siglocpp

Eastrall avatar Sep 10 '22 14:09 Eastrall

So, I've been doing some researches and tests around the source generator feature and it seems that it works great in a "native" C# environment. Let me explain.

According to the Unity documentation (https://docs.unity3d.com/Manual/roslyn-analyzers.html), to create a Roslyn analyzer or a Source Generator, you'll need to create a .NET library targeting netstandard2.0 and then generate your code as you want. (In our case, using the RosalinaGenerator) Then copy/paste the generate dll into a Plugins (or whatever the folder's name) folder and add the RoslynAnalyzer label to this asset and disable both Editor and Standalone platforms on the asset configuration.

In our use case, everytime we make a change to a UXML file, the RosalinaAssetProcessor will have to trigger a dotnet build command of source generator project to generate a dll file with the generate bindings, copy/paste the generated dll into the Plugins folder, set the RoslynAnalyzer label and unselect the Editor and Standalone options manually.

While I believe all of the steps I mentioned above are possible using an Unity script ; but while I was testing I noticed that when I regenerate the DLL with new generated code and import it into Unity again (overriding existing dll), changes were not visible, even if when forcing the reimport process using "Right Click -> "Reimport" on the asset dll. Thus, there is a "risk" that the users using Rosalina experiment some weird behaviors.

What I can suggest for now, let's keep this issue active and see if Unity intend to "natively" support Source Geneartors inside Unity itself using the .NET APIs in the future.

To avoid having the generated files next to the UXML files, in the mean time, we could follow issue #13 and put all generated files into a Assets/Rosalina/AutoGenerated folder to avoid file polution next to the UXML file.

Eastrall avatar Sep 14 '22 17:09 Eastrall

Are you sure this is the intended meaning of the docs page? From what I can read, yes, the source generator needs to be a DLL with a specific label, but it requires no more modification.

In my mind what you need to do, is trigger a recompile (CompilationPipeline.RequestScriptCompilation();) in the asset importer, and then the compilation pipeline will trigger the source generator. And the custom source generator will then simply look up all uxml files and generate code from them.

But perhaps you attempted this, and it does not work this way?

trondtactile avatar Sep 16 '22 06:09 trondtactile

Well I just followed the official documentation and they say:

  1. Build your source generator for release. To do this, go to Build and select the Batch Build option.
  2. In your source generator’s project folder, find the bin/Release/netstandard2.0/ExampleSourceGenerator.dll file.
  3. Copy this file into your Unity project, inside the Assets folder.
  4. Inside the Asset Browser, click on the .dll file to open the Plugin Inspector window.
  5. Go to Select platforms for plugin and disable Any Platform.
  6. Go to Include Platforms and disable Editor and Standalone.
  7. Go to Asset Labels and open the Asset Labels sub-menu.
  8. Create and assign a new label called RoslynAnalyzer. To do this, enter “RoslynAnalyzer” into the text input window in the Asset Labels sub-menu. This label must match exactly and is case sensitive. After you create the label for the first analyzer, The label appears in the Asset Labels sub-menu. You can click on the name of the label in the menu to assign it to other analyzers.
  9. To test the source generator is working, ........

With the current state of Rosalina, we already have "code generation" using Roslyn and when we make changes to a UXML file, the Unity AssetProcessor is triggered and generates the C# files: https://github.com/Eastrall/Rosalina/blob/2d9bffd56736058132e380c7365087ecaf62f03d/Editor/Scripts/RosalinaAssetProcessor.cs#L8-L12 At the end of the process the AssetDatabase is refreshed: https://github.com/Eastrall/Rosalina/blob/2d9bffd56736058132e380c7365087ecaf62f03d/Editor/Scripts/RosalinaAssetProcessor.cs#L44

With this solution, we actually see the generated code into your asset files, but with source generators, the source code is directly "injected" into the final assembly (in the use case described in the Unity official documentation, a netstandard2.0 dll.) So I think this is actually intended by Unity to work this way.

Eastrall avatar Sep 19 '22 18:09 Eastrall

I have been doing more research on source generators and it seems that Unity is working on source generators according to the "Engineering" roadmap: https://unity.com/roadmap/unity-platform/engineering

image

I don't know if it's going to be integrated like a netstandard2.0 project within Visual Studio. I'll suggest we wait and see where Unity is going with this feature. 😉

Eastrall avatar Sep 27 '22 15:09 Eastrall