Skip to content

sator-imaging/Unity-AltSourceGenerator

Repository files navigation

Alternative Source Generator for Unity

Alternative Source Generator is built on the Unity native functions.

  • ✅ Unity Package Manager Support
  • ✅ No Complicated IDE Environment Setup
  • ✅ No Additional Package Installation

As you already know, Roslyn's source generator is too sophisticated. This framework provides more simple, ease of use and good enough functions for source code generation.

日本語 / JA

超簡単に使える Unity 向けソースジェネレーターです。

Table of Contents

Sample Code

Minimal implementation codes of source generator.

日本語 / JA

最小限のソースジェネレーターの構成はこちら。StringBuilder が渡されるので書き込んで true を返せば context.OutputPath に内容を書き込みます。false を返せば書き込みを中止できます。

Method Generator

This example will add Pacic() method to target class.

日本語 / JA

ターゲットのクラスに Panic() メソッドを追加するサンプル。

public class PanicMethodGenerator { static string OutputFileName() => "PanicMethod.cs"; // -> PanicMethod.<TargetClass>.<GeneratorClass>.g.cs static bool Emit(USGContext context, StringBuilder sb) { if (!context.TargetClass.IsClass || context.TargetClass.IsAbstract) return false; // return false to tell USG doesn't write file. // code generation sb.Append($@" namespace {context.TargetClass.Namespace} {{  internal partial class {context.TargetClass.Name}  {{  public void Panic() => throw new System.Exception();  }} }} "); return true; } }

How to Use

using SatorImaging.UnitySourceGenerator; namespace Sample { // Add attribute to target class to use method generator. // Note that class must be defined as partial class. [UnitySourceGenerator(typeof(PanicMethodGenerator), OverwriteIfFileExists = false)] internal partial class MethodGeneratorSample { } }

Result

Generated code looks like this.

// <auto-generated>PanicMethodGenerator</auto-generated> namespace Sample { internal partial class MethodGeneratorSample { public void Panic() => throw new System.Exception(); } }

Generic Generator

Here is target-less generator example.

It is useful to generate static database that cannot be generated on Unity runtime. For example, build asset GUIDs database using UnityEditor.AssetDatabase, resource integrity tables, etc.

using System.Text; using SatorImaging.UnitySourceGenerator; [UnitySourceGenerator(OverwriteIfFileExists = false)] class MinimalGenerator { static string OutputFileName() => "Test.cs"; // -> Test.<ClassName>.g.cs static bool Emit(USGContext context, StringBuilder sb) { // write content into passed StringBuilder. sb.AppendLine($"Asset Path: {context.AssetPath}"); sb.AppendLine($"Hello World from {typeof(MinimalGenerator)}"); // you can modify output path. initial file name is that USG updated. // NOTE: USG doesn't care the modified path is valid or not. context.OutputPath += "_MyFirstTest.txt"; // return true to tell USG to write content into OutputPath. false to do nothing. return true; } }

Result

// <auto-generated>MinimalGenerator</auto-generated> Asset Path: Assets/Scripts/MinimalGenerator.cs Hello World from Sample.MinimalGenerator

Output Directory and File Name

Source Generator creates USG.g folder next to target script and append class names to file name.

Resulting file path will be:

  • Assets/Scripts/USG.g/Test.MinimalGenerator.g.cs
  • Assets/Scripts/USG.g/PanicMethod.MethodGeneratorSample.PanicMethodGenerator.g.cs

NOTE: In above example, output path is modified so that resulting file name is Test.MinimalGenerator.g.cs_MyFirstTest.txt

日本語 / JA

書き出し先は上記の通り。フォルダーとターゲット・ジェネレータークラス名が付与されます。

Utility Functions for Build

There are utility functions to perform source code generation on build event.

日本語 / JA

IPostprocessBuildWithReport も実装しようかと思ったものの、ビルドイベントに勝手に処理追加するのはなんか訳わからんが動かんの原因だし、BuildReport として渡される全てのファイル名を走査する処理は効率も良くない。ということで。

// search by class name if you don't know where it is. var assetPath = USGUtility.GetAssetPathByName(nameof(MinimalGenerator)); // perform code generation. USGEngine.IgnoreOverwriteSettingByAttribute = true; // force overwrite USGEngine.ProcessFile(assetPath);

Technical Notes

As of C# 9.0, it doesn't allow to define abstract static methods in interface, USG reports error when source generator class doesn't implement required static methods.

日本語 / JA

理想はアトリビュートとインターフェイスによるフィルタリングですが、Unity 2021 は C# 9.0 で abstract static を含んだインターフェイスが使えません。

しょうがないのでメソッドのシグネチャを確認して存在しなければエラーをコンソールに出します。

Naming Convention

  • Generator class name and filename must be matched.
  • Class name must be unique in whole project.
  • Classes are ignored if defined in assembly which name starts with:
    • Unity (no trailing dot)
    • System.
    • Mono.

日本語 / JA
  • ジェネレータークラスの名前はファイル名と一致
  • ジェネレータクラスの名前はプロジェクト内で一意
  • クラスが以下で始まる名前のアセンブリで宣言されている場合は対象としない
    • Unity (末尾ドット無し)
    • System.
    • Mono.

<auto-generated/> Tag

USG automatically adds document tag at the beginning of generated file. You can remove this document tag by sb.Clear() in Emit() method.

日本語 / JA

渡される StringBuilder の冒頭にはドキュメントタグが入ってます。不要なら sb.Clear() してください。

Installation

Use the following git URL in Unity Package Manager (UPM).

Editor Extensions

日本語 / JA

手動でソースコード生成イベントの発火も可能です。「ジェネレーターのスクリプトファイル」か「生成されたファイル」を選択して、Project ウインドウで ReimportUnity Source Generator 以下のメニューを実行します。

ジェネレーターとして参照されているファイルを Reimport した場合は、関連するクラスすべてが再生成されます。Force Generate... はクラスアトリビュートの設定に関わらず強制的に上書き生成します。

There is an ability to invoke source code generation by hand. With generator script file or generated file selected in Project window:

  • Reimport

    • This command respects OverwriteIfFileExists setting by generator class attribute.
    • Classes referencing selected generator will also be re-generated.
  • Unity Source Generator > Force Generate

    • This command will force re-generate source code even if overwrite setting is disabled.

Copyright

Copyright © 2023 Sator Imaging, all rights reserved.

License

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
MIT License Copyright (c) 2023 Sator Imaging Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 

 
 

Devnote

TODO

  • Add new attribute option UseCustomWriter to use it's own file writer instead of builtin writer. For the "non-allocation" addicted developers.
    • USGEngine.ProcessingFile() doesn't care what happens in custom writer. just returns true in this situation.
    • Option is for generator class. Referenced generator class doesn't have UnitySourceGenerator attribute so that need to retrieve it from target classes. (how handle conflicts?)
    • USGContext.UseCustomWriter can be used to prevent writing file but StringBuilder is built prior to Emit() method.

Memo

Unity doesn't invoke import event if Visual Studio is not launch by current session of Unity...?

Sponsor this project

 

Packages

No packages published

Languages