Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Source generator for Unity 2021 #15

Open
trondtactile opened this issue Jun 16, 2022 · 13 comments
Open

Source generator for Unity 2021 #15

trondtactile opened this issue Jun 16, 2022 · 13 comments
Labels
area: code-generator The issue is related to the code generator engine discussion planned

Comments

@trondtactile
Copy link
Contributor

Unity 2021 supports Source Generators:

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.

@Eastrall
Copy link
Owner

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 😉

@trondtactile
Copy link
Contributor Author

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.

@Eastrall
Copy link
Owner

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 Eastrall added area: code-generator The issue is related to the code generator engine discussion labels Jun 20, 2022
@blepmlem
Copy link
Contributor

blepmlem commented Sep 4, 2022

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

@Eastrall
Copy link
Owner

Eastrall commented Sep 6, 2022

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 Eastrall reopened this Sep 6, 2022
@blepmlem
Copy link
Contributor

blepmlem commented Sep 7, 2022

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
Copy link
Contributor

blepmlem commented Sep 7, 2022

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.

@trondtactile
Copy link
Contributor Author

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

@Eastrall
Copy link
Owner

@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
Copy link
Owner

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.

@trondtactile
Copy link
Contributor Author

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?

@Eastrall
Copy link
Owner

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:

public class RosalinaAssetProcessor : AssetPostprocessor
{
private const string UIDocumentExtension = ".uxml";
private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromPath)

At the end of the process the AssetDatabase is refreshed:
AssetDatabase.Refresh();

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
Copy link
Owner

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. 😉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: code-generator The issue is related to the code generator engine discussion planned
Projects
Development

No branches or pull requests

3 participants