Search Unity

Bug No AOT code generated for a monobehaviour

Discussion in 'Visual Scripting' started by Peter_Ga, Apr 13, 2022.

  1. Peter_Ga

    Peter_Ga

    Joined:
    Sep 6, 2018
    Posts:
    7
    I'm running into a Bolt issue when running on mobile.
    After running the AOT pre-build step I'm still getting this error on scene start.
    Could this be a bug with how the AOT pre-build generates its AOT code? Or am I doing something wrong?
    I also have a linker file making sure the QuestSceneReferences isn't being stripped

    ΐ--- Time: 4/12/2022 7:19:31 PM Type: Exception ---
    ExecutionEngineException: Attempting to call method 'Ludiq.InstanceFunctionInvoker`3[[AOFL.Multiplayer.Overworld.QuestSceneReferences, OverworldClient, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[UnityEngine.GameObject[], UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]::.ctor' for which no ahead of time (AOT) code was generated.
    System.Reflection.MonoCMethod.InternalInvoke (System.Object obj, System.Object[] parameters) (at <00000000000000000000000000000000>:0)
    System.Reflection.MonoCMethod.DoInvoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at <00000000000000000000000000000000>:0)
    System.Reflection.MonoCMethod.Invoke (System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at <00000000000000000000000000000000>:0)
    System.RuntimeType.CreateInstanceImpl (System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder binder, System.Object[] args, System.Globalization.CultureInfo culture, System.Object[] activationAttributes, System.Threading.StackCrawlMark& stackMark) (at <00000000000000000000000000000000>:0)
    System.Activator.CreateInstance (System.Type type, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder binder, System.Object[] args, System.Globalization.CultureInfo culture, System.Object[] activationAttributes) (at <00000000000000000000000000000000>:0)
    System.Activator.CreateInstance (System.Type type, System.Object[] args) (at <00000000000000000000000000000000>:0)
    Ludiq.OptimizedReflection.GetMethodInvoker (System.Reflection.MethodInfo methodInfo) (at <00000000000000000000000000000000>:0)
    Ludiq.OptimizedReflection.Prewarm (System.Reflection.MethodInfo methodInfo) (at <00000000000000000000000000000000>:0)
    Ludiq.Member.Prewarm () (at <00000000000000000000000000000000>:0)
    Bolt.MemberUnit.Prewarm () (at <00000000000000000000000000000000>:0)
    Ludiq.Graph.Prewarm () (at <00000000000000000000000000000000>:0)
    Ludiq.Machine`2[TGraph,TMacro].Awake () (at <00000000000000000000000000000000>:0)
    Bolt.EventMachine`2[TGraph,TMacro].Awake () (at <00000000000000000000000000000000>:0)
    Rethrow as TargetInvocationException: Exception has been thrown by the target of an invocation.
    System.Reflection.MonoCMethod.InternalInvoke (System.Object obj, System.Object[] parameters) (at <00000000000000000000000000000000>:0)
    System.Reflection.MonoCMethod.DoInvoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at <00000000000000000000000000000000>:0)
    System.Reflection.MonoCMethod.Invoke (System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at <00000000000000000000000000000000>:0)
    System.RuntimeType.CreateInstanceImpl (System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder binder, System.Object[] args, System.Globalization.CultureInfo culture, System.Object[] activationAttributes, System.Threading.StackCrawlMark& stackMark) (at <00000000000000000000000000000000>:0)
    System.Activator.CreateInstance (System.Type type, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder binder, System.Object[] args, System.Globalization.CultureInfo culture, System.Object[] activationAttributes) (at <00000000000000000000000000000000>:0)
    System.Activator.CreateInstance (System.Type type, System.Object[] args) (at <00000000000000000000000000000000>:0)
    Ludiq.OptimizedReflection.GetMethodInvoker (System.Reflection.MethodInfo methodInfo) (at <00000000000000000000000000000000>:0)
    Ludiq.OptimizedReflection.Prewarm (System.Reflection.MethodInfo methodInfo) (at <00000000000000000000000000000000>:0)
    Ludiq.Member.Prewarm () (at <00000000000000000000000000000000>:0)
    Bolt.MemberUnit.Prewarm () (at <00000000000000000000000000000000>:0)
    Ludiq.Graph.Prewarm () (at <00000000000000000000000000000000>:0)
    Ludiq.Machine`2[TGraph,TMacro].Awake () (at <00000000000000000000000000000000>:0)
    Bolt.EventMachine`2[TGraph,TMacro].Awake () (at <00000000000000000000000000000000>:0)
     
  2. marcuslelus

    marcuslelus

    Joined:
    Jun 18, 2018
    Posts:
    67
    OMG I have something. I've just been able to reproduce it and submit a Bug Report. Basically, what I'm saying in the report is:

    If your code is in an Assembly Definition and is referencing some assembly (let's say TextMeshPro) and is using that assembly, like so:

    Code (CSharp):
    1. using TMPro;
    2. using UnityEngine;
    3.  
    4. public class NotIncluded : MonoBehaviour
    5. {
    6.  
    7.     public TextMeshProUGUI TextMeshProUGUI;
    8.  
    9.     public void IncludeMe()
    10.     {
    11.         Debug.Log("Please");
    12.     }
    13. }
    Your whole assembly is considered EditorOnly, which means no AOTStub will be generated for any Members in that assembly (in your case, some function with 2 parameters in QuestSceneReferences). Why TextMeshPro? Well, TextMeshPro is dependant of UnityEngine.UI, which is dependant of UnityEditor.CoreModule assembly. Therefore, UVS thinks your type is using Editor code, which would mean it is an Editor Type.

    TextMeshPro is far from being the only one that triggers this bug, having a reference to Unity.VisualScripting.Core will do the same thing, because Unity.VisualScripting.Core.FrameDelayedCallback is using UnityEditor (even if it's surrounded by #if UNITY_EDITOR, because when this is checked, UNITY_EDITOR is technically defined. Therefore, it thinks the assembly needs UnityEditor).
     
    Last edited: Apr 20, 2022
    agate-pris, Arkade, Hallihax and 4 others like this.
  3. marcuslelus

    marcuslelus

    Joined:
    Jun 18, 2018
    Posts:
    67
    I've made this simple tool when I was testing. It can tell you if your type/assembly is considered an Editor Assembly. The code is the same as the one found in Codebase.cs, but stripped down to the bare minimum. If your type/assembly considered as Editor, it will also show you the dependencies.

    Line 13, you can change the type of myType and run Bug/Check If Type Is Editor.

    The tool also provides a way to generate the AOTStubs.cs without having to do a build. When it's generated, check the file and check if it contains your type (a quick search in VSCode will do).

    Code (CSharp):
    1. #if UNITY_EDITOR
    2. namespace Scripts.Editor
    3. {
    4.     using Unity.VisualScripting;
    5.     using UnityEditor;
    6.     using UnityEngine;
    7.     using System;
    8.     using System.Reflection;
    9.     using System.Collections.Generic;
    10.  
    11.     public static class AOTBuilder
    12.     {
    13.         public static readonly Type MyType = typeof(NotIncluded);
    14.         // Generates the AOTStubs using reflection, but this is the actual process when building.
    15.         [MenuItem("Bug/AOTStubs Builder")]
    16.         public static void Build()
    17.         {
    18.             typeof(AotPreBuilder)
    19.                 .GetMethod("GenerateFromInternalMenu", (BindingFlags) ~0)
    20.                 !.Invoke(null, null);
    21.             var message = @"AOTStubs.cs Generated in Assets\Unity.VisualScripting.Generated\VisualScripting.Core";
    22.             Log(message, Color.cyan);
    23.         }
    24.  
    25.        
    26.         [MenuItem("Bug/Check If Type Is Editor")]
    27.         public static void CheckIfTypeIsEditor()
    28.         {
    29.             var isEditorType = IsEditorType(MyType);
    30.          
    31.             Log($"Is {MyType.Name} an EditorType: {isEditorType}", Color.cyan);
    32.         }
    33.  
    34.         private static bool IsUserAssembly(AssemblyName assemblyName)
    35.         {
    36.             var name = assemblyName.Name;
    37.  
    38.             return
    39.                 name == "Assembly-CSharp" ||
    40.                 name == "Assembly-CSharp-firstpass";
    41.         }
    42.  
    43.         private static bool IsEditorAssembly(Assembly assembly, HashSet<string> visited)
    44.         {
    45.             if (visited.Contains(assembly.GetName().Name))
    46.             {
    47.                 return false;
    48.             }
    49.  
    50.             visited.Add(assembly.GetName().Name);
    51.  
    52.             if (Attribute.IsDefined(assembly, typeof(AssemblyIsEditorAssembly)))
    53.             {
    54.                 Debug.Log(assembly + " Attribute.IsDefined");
    55.                 // _editorAssemblyCache.Add(assembly, true);
    56.                 return true;
    57.             }
    58.  
    59.             if (IsUserAssembly(assembly.GetName()))
    60.             {
    61.                 Debug.Log("IsUserAssembly");
    62.                 // _editorAssemblyCache.Add(assembly, false);
    63.                 return false;
    64.             }
    65.  
    66.             AssemblyName[] listOfAssemblyNames = assembly.GetReferencedAssemblies();
    67.             foreach (var dependencyName in listOfAssemblyNames)
    68.             {
    69.                 try
    70.                 {
    71.                     Assembly dependency = Assembly.Load(dependencyName);
    72.  
    73.                     if (IsEditorAssembly(dependency, visited))
    74.                     {
    75.                         Debug.Log($"{assembly}: {dependency}");
    76.                         // _editorAssemblyCache.Add(assembly, true);
    77.                         return true;
    78.                     }
    79.                 }
    80.                 catch (Exception e)
    81.                 {
    82.                     Debug.LogWarning(e.Message);
    83.                 }
    84.             }
    85.  
    86.             // _editorAssemblyCache.Add(assembly, false);
    87.             return false;
    88.         }
    89.  
    90.         public static bool IsEditorType(Type type)
    91.         {
    92.             return IsEditorAssembly(type.Assembly, new HashSet<string>());
    93.         }
    94.  
    95.         public static void Log(object message, Color color)
    96.         {
    97.             var coloredMessage =
    98.                 $"<color=#{(byte) (color.r * 255f):X2}{(byte) (color.g * 255f):X2}{(byte) (color.b * 255f):X2}>{message}</color>";
    99.             Debug.Log(coloredMessage);
    100.         }
    101.     }
    102. }
    103. #endif
     
  4. CameronDWills

    CameronDWills

    Joined:
    Feb 26, 2021
    Posts:
    91
    I'm having this same issue... it's been driving me mad! My game has worked perfectly for awhile... and now I'm getting this error on iOS (haven't tried android) but it works fine in editor and PC build.

    ExecutionEngineException: Attempting to call method 'Unity.VisualScripting.StaticPropertyAccessor`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]::.ctor' for which no ahead of time (AOT) code was generated.

    System.Reflection.RuntimeConstructorInfo.InternalInvoke (System.Object obj, System.Object[] parameters, System.Boolean wrapExceptions) (at <00000000000000000000000000000000>:0)
    System.RuntimeType.CreateInstanceImpl (System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder binder, System.Object[] args, System.Globalization.CultureInfo culture, System.Object[] activationAttributes, System.Threading.StackCrawlMark& stackMark) (at <00000000000000000000000000000000>:0)
    System.Activator.CreateInstance (System.Type type, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder binder, System.Object[] args, System.Globalization.CultureInfo culture, System.Object[] activationAttributes) (at <00000000000000000000000000000000>:0)
    Unity.VisualScripting.OptimizedReflection.GetPropertyAccessor (System.Reflection.PropertyInfo propertyInfo) (at <00000000000000000000000000000000>:0)
    Unity.VisualScripting.Member.Prewarm () (at <00000000000000000000000000000000>:0)
    Unity.VisualScripting.Graph.Prewarm () (at <00000000000000000000000000000000>:0)
    Unity.VisualScripting.Machine`2[TGraph,TMacro].Awake () (at <00000000000000000000000000000000>:0)
    Unity.VisualScripting.EventMachine`2[TGraph,TMacro].Awake () (at <00000000000000000000000000000000>:0)
    Rethrow as TargetInvocationException: Exception has been thrown by the target of an invocation.
    System.Reflection.RuntimeConstructorInfo.InternalInvoke (System.Object obj, System.Object[] parameters, System.Boolean wrapExceptions) (at <00000000000000000000000000000000>:0)
    System.RuntimeType.CreateInstanceImpl (System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder binder, System.Object[] args, System.Globalization.CultureInfo culture, System.Object[] activationAttributes, System.Threading.StackCrawlMark& stackMark) (at <00000000000000000000000000000000>:0)
    System.Activator.CreateInstance (System.Type type, System.Reflection.BindingFlags bindingAttr, System.Reflection.Binder binder, System.Object[] args, System.Globalization.CultureInfo culture, System.Object[] activationAttributes) (at <00000000000000000000000000000000>:0)
    Unity.VisualScripting.OptimizedReflection.GetPropertyAccessor (System.Reflection.PropertyInfo propertyInfo) (at <00000000000000000000000000000000>:0)
    Unity.VisualScripting.Member.Prewarm () (at <00000000000000000000000000000000>:0)
    Unity.VisualScripting.Graph.Prewarm () (at <00000000000000000000000000000000>:0)
    Unity.VisualScripting.Machine`2[TGraph,TMacro].Awake () (at <00000000000000000000000000000000>:0)
    Unity.VisualScripting.EventMachine`2[TGraph,TMacro].Awake () (at <00000000000000000000000000000000>:0)
     
  5. Peter_Ga

    Peter_Ga

    Joined:
    Sep 6, 2018
    Posts:
    7
    So I found that this instance was actually caused by there being scripts being used that hadn't been added to the Bolt Types; that said I am still seeing this issue on my main project since my asset development is done in a different project.

    Has anybody found a way to force Bolt to make AOT code for all of the methods in a class including the constructor?
     
  6. PanthenEye

    PanthenEye

    Joined:
    Oct 14, 2013
    Posts:
    2,072
    Decorate your classes with [IncludeInSettings(true)] attribute. This will add the class and all its public members to the node library upon regeneration. The user still would have to manually regenerate on their end though.
     
  7. Peter_Ga

    Peter_Ga

    Joined:
    Sep 6, 2018
    Posts:
    7
    Unfortunately this doesn't seem to cause the constructor to be serialized into the aot stubs class
     
  8. PanthenEye

    PanthenEye

    Joined:
    Oct 14, 2013
    Posts:
    2,072
    Are you tied to Bolt? Does this reproduce with UVS as well? Bolt is pretty much an afterthought as far as support goes at this point. It's getting deprecated in a year or so.
     
  9. jeanedouard_unity

    jeanedouard_unity

    Unity Technologies

    Joined:
    May 2, 2019
    Posts:
    49
    This issue has been fixed in 1.7.8 that was released last week.

    Also, we need to provide a mechanism that prevents our users from not being able to compile their graphs. That's why we decided to split runtime only assemblies and assemblies with Editor dependencies. We recognize that it comes with some issues and there are currently conversations about how to fix those problems.

    Do you have a sample of project that reproduces this issue? We really want to get rid of every AotStub issues and we have good ideas on how to fix them. Our problem is we don't have repro cases right now. Please help us by sending us any sample (I mean a really small project) that allows us to test our fixes.
     
  10. Peter_Ga

    Peter_Ga

    Joined:
    Sep 6, 2018
    Posts:
    7
    I can't upgrade my project easily so I'm bound to this version of Bolt, though it looks like this is an issue regardless.
     
  11. Peter_Ga

    Peter_Ga

    Joined:
    Sep 6, 2018
    Posts:
    7
    As far as I can tell the issue is that I author my assets in a different project(s) but I need all of those assets in the same project if the goal is to
    As far as I can tell the issue is that I author my assets in a different project(s) but I need all of those assets in the same project since the AOT Prebuild scan is expecting to find all of those instances in the project and use them to uniquely generate the aot stubs.

    Unfortunately I can't just move the assets to a package as I already tried this and it doesn't seem to notice the bolt flows in those packages.

    Additionally it appears that even if you create a bolt flow that uses all of your defined methods in a class (to ensure the proper aot stub generation) it still doens't serialize the constructor unless you have that as an object scoped variable and have that variable filled (otherwise it drops the type and doesn't generate the constructor stub method)
     
  12. Peter_Ga

    Peter_Ga

    Joined:
    Sep 6, 2018
    Posts:
    7
    I don't think I can share the project, but I'll look into making a sample; in the meantime if you're curious these should be the steps required to reproduce this:
    • Two projects both with bolt in them with Project A consuming Project B's scripts as a package.
    • In Project B create monobehaviour class with some public methods and use it in a bolt flow.
    • Give that bolt flow an object scoped variable of the type of script you just made
    • Put the flow on an object
    • In Project A use this bolt flow in a scene
    • Add the created script to the managed types for Bolt
    • Run the aot prebuild
    • If you check the newly created aotstubs.cs you'll find that your script didn't have its public methods preserved
     
  13. SimonFireproof

    SimonFireproof

    Joined:
    May 20, 2013
    Posts:
    16
    I have been struggling with the same AOT issue since upgrading to 1.7.8. I found that most of our assemblies were being excluded from the AOT stubs because of the dependency code in Codebase.IsEditorAssembly() (as per @marcuslelus post). I have embedded the Visual Scripting package and added a workaround to Codebase.cs to search the project for asmdefs and examine if they're set to be Editor only. FindRuntimeAsmDefNames() is called from the constructor. IsAsmDefRuntimeAssembly() is called from inside IsEditorAssembly

    Code (CSharp):
    1. private static readonly HashSet<string> _runtimeAsmDefs;
    2.  
    3. // The code in IsEditorAssembly() assumes any assembly which depends on an editor assembly
    4. // must be an editor assembly itself. This means our Main assembly (and others) which use editor code
    5. // inside #if UNITY_EDITOR will be excluded from the AOT stubs.
    6. // FindRuntimeAsmDefNames finds all the asmdef files in the project and examines them to see if they
    7. // really are editor assemblies or not.
    8. private static HashSet<string> FindRuntimeAsmDefNames()
    9. {
    10.    // Find all the Assembly Definition files in the project and examine the platform settings
    11.    // to see if it's editor-only. If not, assume it should be included in runtime
    12.  
    13.    HashSet<string> names = new();
    14.  
    15.    try
    16.    {
    17.        string[] guids = UnityEditor.AssetDatabase.FindAssets("t:asmdef", new[] {"Assets"});
    18.        foreach (string guid in guids)
    19.        {
    20.            var asset = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEditorInternal.AssemblyDefinitionAsset>(UnityEditor.AssetDatabase.GUIDToAssetPath(guid));
    21.  
    22.            string wholeFile = asset.text;
    23.            string[] includePlatforms = GetArrayElementsFromText(wholeFile, "includePlatforms");
    24.            string[] excludePlatforms = GetArrayElementsFromText(wholeFile, "excludePlatforms");
    25.  
    26.            bool editorOnlyAssembly = false;
    27.            if (includePlatforms is { Length: 1 } && includePlatforms[0] == "Editor")
    28.            {
    29.                editorOnlyAssembly = true;
    30.            }
    31.            else if (excludePlatforms is { Length: > 0 } && !excludePlatforms.Contains("Editor"))
    32.            {
    33.                editorOnlyAssembly = true;
    34.            }
    35.  
    36.            if (!editorOnlyAssembly)
    37.            {
    38.                // Extract the name from the "name" field in the asmdef asset
    39.                int nameIndex = wholeFile.IndexOf("\"name\":", StringComparison.OrdinalIgnoreCase) + 7;
    40.                if (nameIndex != -1)
    41.                {
    42.                    int startOfNameIndex = nameIndex + wholeFile.Substring(nameIndex).IndexOf("\"", StringComparison.OrdinalIgnoreCase) + 1;
    43.                    int endOfNameIndex = wholeFile.Substring(startOfNameIndex).IndexOf("\"", StringComparison.OrdinalIgnoreCase);
    44.                    string name = wholeFile.Substring(startOfNameIndex, endOfNameIndex);
    45.                    names.Add(name);
    46.                }
    47.            }
    48.        }
    49.    }
    50.    catch (Exception e)
    51.    {
    52.        Debug.Log(e);
    53.    }
    54.  
    55.    return names;
    56. }
    57.  
    58. private static string[] GetArrayElementsFromText(string wholeFile, string arrayName)
    59. {
    60.    // Examine the text file looking for text in the form:
    61.    //  "arrayName": [
    62.    //      "Element_1",
    63.    //      "Element_2",
    64.    //      "Element_3"
    65.    //  ],
    66.  
    67.  
    68.    int arrayNameIndex = wholeFile.IndexOf(arrayName, StringComparison.OrdinalIgnoreCase);
    69.    if (arrayNameIndex > 0)
    70.    {
    71.        int leftBracketSubIndex = wholeFile.Substring(arrayNameIndex).IndexOf("[", StringComparison.Ordinal);
    72.        int rightBracketSubIndex = wholeFile.Substring(arrayNameIndex + leftBracketSubIndex + 1).IndexOf("]", StringComparison.Ordinal);
    73.        if (leftBracketSubIndex > 0 && rightBracketSubIndex > 0)
    74.        {
    75.            string arrayString = wholeFile.Substring(arrayNameIndex + leftBracketSubIndex + 1, rightBracketSubIndex);
    76.            string[] elements = arrayString.Split(',');
    77.            for (int index = 0; index < elements.Length; index++)
    78.            {
    79.                elements[index] = elements[index].Trim().Replace("\"", "");
    80.            }
    81.            return elements;
    82.        }
    83.    }
    84.  
    85.    return null;
    86. }
    87.  
    88. private static bool IsAsmDefRuntimeAssembly(string assemblyName)
    89. {
    90.     return _runtimeAsmDefs.Contains(assemblyName);
    91. }
    Put this at the top of the constructor:
    Code (CSharp):
    1. _runtimeAsmDefs = FindRuntimeAsmDefNames();
    Put this in IsEditorAssembly() just after IsSpecialCaseRuntimeAssembly():
    Code (CSharp):
    1. if (IsAsmDefRuntimeAssembly(name))
    2. {
    3.     _editorAssemblyCache.Add(assembly, false);
    4.     return false;
    5. }
     
    Last edited: Jun 6, 2022
    AdrianoDeRegino likes this.
  14. PanthenEye

    PanthenEye

    Joined:
    Oct 14, 2013
    Posts:
    2,072
    Has anyone successfully reported the bug in proper channels? Have Unity QA confirmed this issue? This is mission critical to the many platforms that don't support Mono scripting backend.
     
    Ignacii likes this.
  15. SimonFireproof

    SimonFireproof

    Joined:
    May 20, 2013
    Posts:
    16
    I’ve not reported it officially. I hope @jeanedouard_unity is on the case though?
     
  16. PanthenEye

    PanthenEye

    Joined:
    Oct 14, 2013
    Posts:
    2,072
    My understanding is that most Unity devs, especially on the UVS team, don't visit the forums and the only valid way of making sure your issue is seen is to report it via the editor.
     
  17. AdrianoDeRegino

    AdrianoDeRegino

    Joined:
    Aug 10, 2019
    Posts:
    87
    That's great @SimonFireproof , would you mind giving a more basic "how to", on implementing this fix?
     
  18. SimonFireproof

    SimonFireproof

    Joined:
    May 20, 2013
    Posts:
    16
    There's not much to it really. You need to embed the visual scripting package, which basically means coping the package from the cache to your project packages directory. Instructions here: https://docs.unity3d.com/Manual/upm-embed.html

    Then open Codebase.cs and add the code from my previous post.

    My code examines the asmdef text file to determine if an assembly is "Editor" only. If any other platform is ticked it will assume the assembly is required for runtime. It doesn't try to compare against the current build platform.
     
    AdrianoDeRegino likes this.
  19. PanthenEye

    PanthenEye

    Joined:
    Oct 14, 2013
    Posts:
    2,072
    Currently, no AOT code is generated for any Cinemachine related nodes as well. They whitelisted TMPro assembly, but the core issue remains for everything that they have not whitelisted. Even if they whitelist everything inside Unity first party ecosystem, the issue will remain in context of 3rd party packages. The logic for automatically detecting editor only assemblies is flawed and needs to be reworked.

    CASE IN-6243 for the Cinemachine issue.
     
    Ignacii likes this.
  20. SimonFireproof

    SimonFireproof

    Joined:
    May 20, 2013
    Posts:
    16
    Agreed. How this ever got released is beyond me.
     
  21. PanthenEye

    PanthenEye

    Joined:
    Oct 14, 2013
    Posts:
    2,072
    The inability to compile their graphs before this change was a user error due to their lack of knowledge, most likely. Now it's the default even for 1st party Unity tools like Cinemachine. The build might be compiling, but it does not actually run on any platform with IL2CPP scripting backend because UVS doesn't generate the required AOT stubs. Even if you whitelist Cinemachine, like you did with TMPro, it doesn't address the core issue.

    It's very common for assemblies to have some editor code that's likely excluded from the final build with #if UNITY_EDITOR conditional compilation, but UVS can't discern between assembly that has a little bit of conditional editor code that does not impact the final build and an assembly that's fully editor only. It throws out all of it.

    I can write code in ways that avoid any editor references, but I can't do that for Cinemachine, WWise, Fmod and assets on the Asset Store, many of which have some editor code in their assemblies that does not impact the final build compilation or AOT compatibility.

    Arguably, the inability to compile a build is far better than building and then having the build fail to execute logic in graphs due to missing AOT stubs UVS failed to generate. It's better because it fails quicker and reports to console and the user can then do something about it - namely remove editor only nodes from graphs.

    Now UVS is deciding that all of Cinemachine is an editor dependent assembly and doesn't generate any AOT stubs for it even for the most benign, runtime only nodes. I shouldn't be expected to change the source of the package to fix this oversight. Nor should I be expected to write redundant wrappers that don't have those editor dependencies.
     
    Last edited: Jun 9, 2022
    sbsmith and vTom like this.
  22. vTom

    vTom

    Joined:
    May 1, 2018
    Posts:
    4
    In the interests of unblocking people who are hitting this issue, I'm sharing my workaround for this problem.

    Basically the underlying issue is, as others have mentioned, that the AOT stub generator will skip code if it has a dependency on UnityEditor. This is very aggressive filtering behaviour which can affect pretty much any class or package, and since there are no editor warnings that code will be skipped, it can be a serious headache.

    My workaround is very brute-force and is primarily aimed at 'whitelisting' code in ASMDefs you have access to, but it can easily be modified to perform whatever checks you like, and does the job without too much fuss.

    At its core, you need to subclass the various AOTStubWriter implementations. They'll be executed during AOT stub generation, and will allow you to determine whether things should be skipped or not.

    Step 1:

    Create a custom attribute:

    Code (CSharp):
    1. using System;
    2.  
    3. namespace AOTAttributes
    4. {
    5.     [AttributeUsage(AttributeTargets.Assembly)]
    6.     public class DoNotSkipAttribute : Attribute
    7.     {
    8.      
    9.     }
    10. }
    Step 2:
    Create an AssemblyInfo.cs file in the root dir of the ASMDef you want to whitelist:

    Code (CSharp):
    1. using AOTAttributes;
    2.  
    3. [assembly:DoNotSkip]
    Step 3:
    Subclass the various AOTStubWriter implementations and check for the existing of the attribute. If it's present, then AOTStubWriter.skip should return false; otherwise fallback to the base implementation:

    Code (CSharp):
    1. using System;
    2. using System.Reflection;
    3. using AOTAttributes;
    4. using Unity.VisualScripting;
    5.  
    6. namespace Editor
    7. {
    8.     static class UnskippableAssemblyChecker
    9.     {
    10.         public static bool IsUnskippable(Type type)
    11.         {
    12.             var rootNamespace = type.RootNamespace();
    13.  
    14.             return HasUnskippableAttribute(type.Assembly);
    15.         }
    16.      
    17.         private static string RootNamespace(this Type type)
    18.         {
    19.             return type.Namespace?.PartBefore('.');
    20.         }
    21.  
    22.         private static bool HasUnskippableAttribute(Assembly assembly)
    23.         {
    24.             return Attribute.IsDefined(assembly, typeof(DoNotSkipAttribute));
    25.         }
    26.     }
    27.  
    28.     [AotStubWriter(typeof(MethodInfo))]
    29.     public class UnskippableMethodInfoStubWriter : MethodInfoStubWriter
    30.     {
    31.         public UnskippableMethodInfoStubWriter(MethodInfo methodInfo) : base(methodInfo)
    32.         {
    33.         }
    34.  
    35.         public override bool skip => ShouldSkip();
    36.  
    37.         private bool ShouldSkip()
    38.         {
    39.             if(UnskippableAssemblyChecker.IsUnskippable(stub.ReflectedType))
    40.             {
    41.                 return false;
    42.             }
    43.             else
    44.             {
    45.                 return base.skip;
    46.             }
    47.         }
    48.  
    49.     }
    50.  
    51.     [AotStubWriter(typeof(FieldInfo))]
    52.     public class UnskippableFieldInfoStubWriter : FieldInfoStubWriter
    53.     {
    54.         public UnskippableFieldInfoStubWriter(FieldInfo fieldInfo) : base(fieldInfo)
    55.         {
    56.         }
    57.      
    58.         public override bool skip => ShouldSkip();
    59.  
    60.         private bool ShouldSkip()
    61.         {
    62.             if(UnskippableAssemblyChecker.IsUnskippable(stub.ReflectedType))
    63.             {
    64.                 return false;
    65.             }
    66.             else
    67.             {
    68.                 return base.skip;
    69.             }
    70.         }
    71.     }
    72.  
    73.     [AotStubWriter(typeof(PropertyInfo))]
    74.     public class UnskippablePropertyInfoStubWriter : PropertyInfoStubWriter
    75.     {
    76.         public UnskippablePropertyInfoStubWriter(PropertyInfo propertyInfo) : base(propertyInfo)
    77.         {
    78.         }
    79.      
    80.         public override bool skip => ShouldSkip();
    81.  
    82.         private bool ShouldSkip()
    83.         {
    84.             if(UnskippableAssemblyChecker.IsUnskippable(stub.ReflectedType))
    85.             {
    86.                 return false;
    87.             }
    88.             else
    89.             {
    90.                 return base.skip;
    91.             }
    92.         }
    93.  
    94.     }
    95.  
    96.     [AotStubWriter(typeof(ConstructorInfo))]
    97.     public class UnskippableConstructorInfoStubWriter : ConstructorInfoStubWriter
    98.     {
    99.         public UnskippableConstructorInfoStubWriter(ConstructorInfo constructorInfo) : base(constructorInfo)
    100.         {
    101.         }
    102.      
    103.         public override bool skip => ShouldSkip();
    104.  
    105.         private bool ShouldSkip()
    106.         {
    107.             if(UnskippableAssemblyChecker.IsUnskippable(stub.ReflectedType))
    108.             {
    109.                 return false;
    110.             }
    111.             else
    112.             {
    113.                 return base.skip;
    114.             }
    115.         }
    116.  
    117.     }
    118. }

    This workaround should be fairly easy to modify to support whatever checks you like - allowing you to explicitly whitelist anything that hits one of the stubwriter implementations.

    I don't know if this will be supported in future versions of VS, or whether there's an issue with doing things this way - but it sorted my AOT stub generation problem after upgrading from Bolt. Hopefully it'll help others too.
     
    SimonFireproof and PanthenEye like this.
  23. SimonFireproof

    SimonFireproof

    Joined:
    May 20, 2013
    Posts:
    16
    Another consequence of the Codebase.IsEditorAssembly() change is that it changes the behavior of "Regenerate Nodes". Any class in an assembly that's considered "Editor" won't be included in the node database unless it's been explicitly added to Type Options.

    After making my change, I found the Regenerate Nodes button returned to the previous behaviour. @vTom I assume your change doesn't change that?
     
    Last edited: Jun 9, 2022
  24. vTom

    vTom

    Joined:
    May 1, 2018
    Posts:
    4
    Unfortunately not - you'll still need to explicitly add any types you need - my workaround is explicitly only triggered during the AOT generation process which occurs during build.
     
    SimonFireproof likes this.
  25. PanthenEye

    PanthenEye

    Joined:
    Oct 14, 2013
    Posts:
    2,072
    Now that I think about it more. Why is this managed at AOT stubs generation level at all? If editor only nodes is the problem, filter them out in the fuzzy finder or even type options. AOT Safety Mode does precisely that, or at least it used to in the Bolt days.

    Seems like they had a task for builds to compile in all use cases, even ones with user error. But now builds don't work and a designer can't do anything to fix the issue. Task accomplished...
     
  26. sbsmith

    sbsmith

    Joined:
    Feb 7, 2013
    Posts:
    126
    Thanks @marcuslelus ! Your code helped me identify why my assembly's types aren't showing up in Visual Scripting. My code references Unity.InputSystem which depends on UnityEditor.CoreModule @jeanedouard_unity. This is the newest Visual Scripting and newest Input System. This has been frustrating because we've been forced to manually specify types and there is some odd thing happening where the types list is getting reset in Plastic. I'm hoping that Unity will fix this issue because right now my options are to either force the designers to deal with bugs and a process they're already complaining about, or re-structure the project to work around the forced editor dependency.
     
    marcuslelus likes this.
  27. sbsmith

    sbsmith

    Joined:
    Feb 7, 2013
    Posts:
    126
    I made a version to check all the dependencies and I'm looking for clarification from @jeanedouard_unity that using any of the listed assemblies will block the auto-import of your nodes because of these dependencies and that this is intended functionality. This is causing a real headache for the team. I can also confirm that UnityEngine.UI still has UnityEditor.CoreModule as a dependency.

    I'm using Unity 2022.1.4 and all plugins are on their latest versions as of June 17, 2022.

    upload_2022-6-17_14-40-7.png
     
    SimonFireproof likes this.
  28. sbsmith

    sbsmith

    Joined:
    Feb 7, 2013
    Posts:
    126
    Oh, and since @marcuslelus was kind enough to provide the original code, here is my modification. I'm a bit selfconscious because it's a quick job, but it does the job.


    Code (CSharp):
    1. namespace SomeNameSpace
    2. {
    3.     using System;
    4.     using System.Collections.Generic;
    5.     using System.Reflection;
    6.     using System.Text;
    7.     using UnityEditor;
    8.     using UnityEngine;
    9.  
    10.     public static class AssemblyValidationTools
    11.     {
    12.         /// <summary>
    13.         /// We are using types that we know to be in our runtime assemblies to get references
    14.         /// to our runtime assemblies. If these types are ever moved to a different assembly, we will get
    15.         /// misleading results
    16.         /// </summary>
    17.         [MenuItem( "AssemblyTools/Check runtime assemblies for editor dependencies" )]
    18.         private static void CheckSelectedAssembly()
    19.         {
    20.             CheckAssembly( typeof( ATypeFromRuntimeAssemblyA ).Assembly );
    21.  
    22.             //common.Runtime
    23.             CheckAssembly( typeof( ATypeFromRuntimeAssemblyB ).Assembly );
    24.         }
    25.  
    26.         private static void CheckAssembly( Assembly assembly )
    27.         {
    28.             if( IsEditorAssembly( assembly ) )
    29.             {
    30.                 Debug.Log( $"{assembly.GetName().Name} is an editor assembly" );
    31.                 return;
    32.             }
    33.  
    34.             HashSet<string> allVisited = new HashSet<string>();
    35.  
    36.             Stack<string> currentDependencyPath = new Stack<string>();
    37.  
    38.             List<Stack<string>> editorDependencyChains = new List<Stack<string>>();
    39.  
    40.             DependsOnEditorAssemblies( assembly, currentDependencyPath, editorDependencyChains, allVisited );
    41.  
    42.             if( 0 < editorDependencyChains.Count )
    43.             {
    44.                 foreach( Stack<string> dependencyChain in editorDependencyChains )
    45.                 {
    46.                     PrintEditorDependent( dependencyChain );
    47.                 }
    48.             }
    49.             else
    50.             {
    51.                 PrintNoEditorDependencies( assembly.GetName().Name );
    52.             }
    53.         }
    54.  
    55.         private static void PrintNoEditorDependencies( string name )
    56.         {
    57.             Debug.Log( $"{ColouredLogString( name, Color.green )} has no editor dependencies" );
    58.         }
    59.  
    60.         private static void PrintEditorDependent( Stack<string> dependencyChain )
    61.         {
    62.             string[] stackContents = dependencyChain.ToArray();
    63.             Array.Reverse( stackContents );
    64.  
    65.             int numElements = stackContents.Length;
    66.  
    67.             int iElement = 0;
    68.  
    69.             StringBuilder stringBuilder = new StringBuilder();
    70.  
    71.             stringBuilder.Append( "Editor dependency found: " );
    72.  
    73.             if( iElement < numElements )
    74.             {
    75.                 stringBuilder.Append( ColouredLogString( stackContents[ iElement ], Color.red ) );
    76.                 iElement += 1;
    77.             }
    78.  
    79.             for( ; iElement < numElements - 1; ++iElement )
    80.             {
    81.                 stringBuilder.Append( $" > {stackContents[ iElement ]}" );
    82.             }
    83.  
    84.             if( iElement < numElements )
    85.             {
    86.                 stringBuilder.Append( " > " );
    87.                 stringBuilder.Append( ColouredLogString( stackContents[ iElement ], Color.red ) );
    88.             }
    89.  
    90.             Debug.LogWarning( stringBuilder.ToString() );
    91.         }
    92.  
    93.         private static void DependsOnEditorAssemblies(
    94.             Assembly assembly,
    95.             Stack<string> currentDependencyPath,
    96.             List<Stack<string>> editorDependencyChains,
    97.             HashSet<string> visitedNonEditorAssemblies )
    98.         {
    99.             if( visitedNonEditorAssemblies.Contains( assembly.GetName().Name ) )
    100.             {
    101.                 return;
    102.             }
    103.  
    104.             if( IsEditorAssembly( assembly ) )
    105.             {
    106.                 editorDependencyChains.Add( DuplicateDependencyPathAndAddAssembly( currentDependencyPath, assembly.GetName().Name ) );
    107.                 return;
    108.             }
    109.  
    110.             // we don't mark editor assemblies as visited because we want to find all the user assemblies that depend on them
    111.             visitedNonEditorAssemblies.Add( assembly.GetName().Name );
    112.  
    113.             if( IsIgnoredUserAssembly( assembly.GetName() ) )
    114.             {
    115.                 return;
    116.             }
    117.  
    118.             currentDependencyPath.Push( assembly.GetName().Name );
    119.  
    120.             AssemblyName[] listOfAssemblyNames = assembly.GetReferencedAssemblies();
    121.             foreach( var dependencyName in listOfAssemblyNames )
    122.             {
    123.                 try
    124.                 {
    125.                     Assembly dependency = Assembly.Load( dependencyName );
    126.  
    127.                     DependsOnEditorAssemblies( dependency, currentDependencyPath, editorDependencyChains, visitedNonEditorAssemblies );
    128.                 }
    129.                 catch( Exception e )
    130.                 {
    131.                     Debug.LogWarning( e.Message );
    132.                 }
    133.             }
    134.  
    135.             currentDependencyPath.Pop();
    136.         }
    137.  
    138.         private static Stack<string> DuplicateDependencyPathAndAddAssembly( Stack<string> dependencyPath, string lastAssemblyName )
    139.         {
    140.             string[] stackContents = dependencyPath.ToArray();
    141.             Array.Reverse( stackContents );
    142.             Stack<string> newDependencyChain = new Stack<string>( stackContents );
    143.             newDependencyChain.Push( lastAssemblyName );
    144.  
    145.             return newDependencyChain;
    146.         }
    147.  
    148.         private static bool IsEditorAssembly( Assembly assembly )
    149.         {
    150.             if( Attribute.IsDefined( assembly, typeof( AssemblyIsEditorAssembly ) ) )
    151.             {
    152.                 return true;
    153.             }
    154.             else
    155.             {
    156.                 return false;
    157.             }
    158.         }
    159.  
    160.         private static bool IsIgnoredUserAssembly( AssemblyName assemblyName )
    161.         {
    162.             var name = assemblyName.Name;
    163.  
    164.             return
    165.                 name == "Assembly-CSharp" ||
    166.                 name == "Assembly-CSharp-firstpass";
    167.         }
    168.  
    169.         private static string ColouredLogString( object message, Color color )
    170.         {
    171.             var colouredLogString =
    172.                 $"<color=#{(byte)( color.r * 255f ):X2}{(byte)( color.g * 255f ):X2}{(byte)( color.b * 255f ):X2}>{message}</color>";
    173.  
    174.             return colouredLogString;
    175.         }
    176.     }
    177. }
    178.  
     
    SimonFireproof likes this.
  29. thedrhax14

    thedrhax14

    Joined:
    Aug 7, 2014
    Posts:
    38
    This helped to find dependencies and the solution regarding the Bolt or Visual Scripting plugin issue/feature. In general, the plugin forces developers now to avoid making their code dependent on any assembly that depends on UnityEditor, which code by marcuslelus helps to find in case such dependency was created by accident.

    Best solution will be revising the structure of your code and classes. In our case we had to move one dependent class to a different name space and under different assembly and made sure that the class is not dependent on anything that maybe dependent on Editor, while other classes that Bolt wasn't interacting with were left as it is.

    Now, is this a bug? Not really, because the plugin as I said forces developers to follow specific code and assembly structure, which seems to be by design. Otherwise, don't use Bolt:)
     
  30. PanthenEye

    PanthenEye

    Joined:
    Oct 14, 2013
    Posts:
    2,072
    You would sorta have a point if this change didn't break first party packages like Cinemachine and common middleware packages like FMOD and Wwise. Not to mention hundreds of assets on the asset store that extend the editor but still have valid runtime API that worked before UVS 1.7.7.

    We can adhere to the new requirements in our own code, but that's not the problem here. Feels like this change was made and approved by someone coming from enterprise who has no clue about how Unity is used.
     
    Last edited: Jun 30, 2022
  31. sbsmith

    sbsmith

    Joined:
    Feb 7, 2013
    Posts:
    126
    I filed a bug report and they acknowledged the problem that many of their own runtime assemblies still have editor dependencies that block automatic node importing. They said it will be fixed in Visual Scripting 1.8.0-pre.1. I don't know if this affects the code generation issue that this thread was originally about.
     
  32. sbsmith

    sbsmith

    Joined:
    Feb 7, 2013
    Posts:
    126
    1.8.0-pre.1 has not been verified against anything before 2023 yet, but people can still try it. (I will get to it later in the week since it is a lower priority right now). These are the installation instructions Customer QA gave me in response to my bug report (IN-6257).

     
    Last edited: Jul 20, 2022
  33. PanthenEye

    PanthenEye

    Joined:
    Oct 14, 2013
    Posts:
    2,072
    1.8 has been cancelled, it was a preview of the new high performance interpreter runtime, which should not be available publically anymore, according to UVS team members. They've refocused on the next major version of UVS instead. No ETA has been given. Could be Unity 2023, could also be Unity 2024.

    EDIT: Source #1

    and from official Discord:

     
    sbsmith likes this.
  34. sbsmith

    sbsmith

    Joined:
    Feb 7, 2013
    Posts:
    126
    Uggggg, yeah. I looked at the docs and was shocked to see that 1.8.0-pre.1 was almost a year old.
     
  35. PanthenEye

    PanthenEye

    Joined:
    Oct 14, 2013
    Posts:
    2,072
    Has anyone received a response to their bug submission? Mine is being ignored.
     
  36. PanthenEye

    PanthenEye

    Joined:
    Oct 14, 2013
    Posts:
    2,072
  37. ericb_unity

    ericb_unity

    Unity Technologies

    Joined:
    Nov 24, 2017
    Posts:
    167
  38. PanthenEye

    PanthenEye

    Joined:
    Oct 14, 2013
    Posts:
    2,072
  39. Trypants

    Trypants

    Joined:
    Dec 20, 2016
    Posts:
    2
    Finally!
     
  40. AdrianoDeRegino

    AdrianoDeRegino

    Joined:
    Aug 10, 2019
    Posts:
    87
    One year for bugfixes, still feeling this is sort of abandoned.
     
  41. PanthenEye

    PanthenEye

    Joined:
    Oct 14, 2013
    Posts:
    2,072
    It is and it's not. This version of the tool is in maintenance mode if you can call it that with this update frequecy, but they're working on what is essentially a full rewrite by the looks of it. Another two to three year wait for that one per recent stickied Unity posts.
     
  42. agate-pris

    agate-pris

    Joined:
    Sep 19, 2018
    Posts:
    5
    It says this bug was fixed in 1.8.0, but it doesn't look fixed to me.
     
  43. PanthenEye

    PanthenEye

    Joined:
    Oct 14, 2013
    Posts:
    2,072
    Need more info, are you still getting ExecutionEngineException errors in build with 1.8.0?