Circular references between Dlls. firstpass and a generated Dll

EDIT: I ended up using Delegate.Create delegate and hook up my dll dynamically and not statically.

public void ConnectAssembly(string assemblyPath, string serializerTypeName)
{
    if (!File.Exists(assemblyPath))
        throw new IOException("Assembly path doesn't exist: " + assemblyPath);

    var asm = Assembly.LoadFrom(assemblyPath);
    var serializerType = asm.GetType(serializerTypeName);

    if (serializerType == null)
        throw new NullReferenceException("Serializer type was not found: " + serializerTypeName);

    var serializeMethod = serializerType.GetMethod("Serialize", BindingFlags.Static | BindingFlags.Public);
    var deserializeMethod = serializerType.GetMethod("Deserialize", BindingFlags.Static | BindingFlags.Public);
    _serialize = (SerializationDelegate)Delegate.CreateDelegate(typeof(SerializationDelegate), null, serializeMethod);
    _deserialize = (DeserializationDelegate)Delegate.CreateDelegate(typeof(DeserializationDelegate), null, deserializeMethod);
}

EDIT: Here’s a video showing it in action:

In my firstpass dll, I have type X - I have a dynamically generated dll ‘SerTest.dll’ that I emit at in the firstpass, that references ‘X’. So when Unity tries to compile firstpass, it tries to compile the dll ‘SerTest’ which references ‘X’ in firstpass, compilation fails.

-----Compiler Commandline Arguments:

Filename: "C:\Program Files\Unity\Editor\Data\Mono\bin\mono.exe"

Arguments: "C:\Program Files\Unity\Editor\Data\Mono\lib\mono\unity\smcs.exe"  @Temp/UnityTempFile-71d970fa2565f064492790f199c48fd7

MONO_PATH: C:\Program Files\Unity\Editor\Data\Mono\lib\mono\unity

MONO_CFG_DIR: C:\Program Files\Unity\Editor\Data\Mono\etc

index: 68

Responsefile: Temp/UnityTempFile-71d970fa2565f064492790f199c48fd7 Contents: 

-debug
-target:library    
-nowarn:0169    
-out:Temp/Assembly-CSharp-firstpass.dll    
-r:"C:/Program Files/Unity/Editor/Data/Managed/UnityEngine.dll"    
-r:"C:/Program Files/Unity/Editor/Data/UnityExtensions/Unity/GUISystem/UnityEngine.UI.dll"    
-r:Assets/SerTest.dll    <<<<<<<<<< !!!!!
-r:Assets/Dll/FastSerializer.dll    
-r:"C:/Program Files/Unity/Editor/Data/UnityExtensions/Unity/GUISystem/UnityEngine.UI.dll"    
-r:"C:/Program Files/Unity/Editor/Data/Managed/UnityEditor.dll"    
-r:"C:\Program Files\Unity\Editor\Data\PlaybackEngines\iossupport\UnityEditor.iOS.Extensions.Xcode.dll"    
-define:UNITY_5_0_0    
-define:UNITY_5_0    
-define:UNITY_5    
-define:ENABLE_LICENSE_RENAME    
-define:ENABLE_NEW_BUGREPORTER    
-define:ENABLE_2D_PHYSICS    
-define:ENABLE_4_6_FEATURES    
-define:ENABLE_AUDIO    
-define:ENABLE_CACHING   
-define:ENABLE_CLOTH    
-define:ENABLE_DUCK_TYPING    
-define:ENABLE_FRAME_DEBUGGER    
-define:ENABLE_GENERICS    
-define:ENABLE_HOME_SCREEN    
-define:ENABLE_IMAGEEFFECTS    
-define:ENABLE_LIGHT_PROBES_LEGACY    
-define:ENABLE_MICROPHONE    
-define:ENABLE_MULTIPLE_DISPLAYS    
-define:ENABLE_NEW_HIERARCHY    
-define:ENABLE_PHYSICS    
-define:ENABLE_PHYSICS_PHYSX3    
-define:ENABLE_PLUGIN_INSPECTOR    
-define:ENABLE_SHADOWS    
-define:ENABLE_SINGLE_INSTANCE_BUILD_SETTING    
-define:ENABLE_SPRITES    
-define:ENABLE_TERRAIN    
-define:ENABLE_UNITYEVENTS    
-define:ENABLE_WEBCAM    
-define:ENABLE_WWW    
-define:ENABLE_AUDIOMIXER_SUSPEND    
-define:ENABLE_NONPRO    
-define:INCLUDE_DYNAMIC_GI    
-define:INCLUDE_GI    
-define:INCLUDE_IL2CPP    
-define:PLATFORM_SUPPORTS_MONO    
-define:RENDER_SOFTWARE_CURSOR    
-define:UNITY_STANDALONE_WIN    
-define:UNITY_STANDALONE    
-define:ENABLE_SUBSTANCE    
-define:ENABLE_TEXTUREID_MAP    
-define:ENABLE_RUNTIME_GI    
-define:ENABLE_MOVIES    
-define:ENABLE_NETWORK    
-define:ENABLE_MONO    
-define:ENABLE_PROFILER    
-define:UNITY_EDITOR    
-define:UNITY_EDITOR_64    
-define:UNITY_EDITOR_WIN    

Assets/Plugins/DemoBehaviour.cs

-r:"C:\Program Files\Unity\Editor\Data\Mono\lib\mono\unity\System.Runtime.Serialization.dll"

-r:"C:\Program Files\Unity\Editor\Data\Mono\lib\mono\unity\System.Xml.Linq.dll"

-----CompilerOutput:-stdout--exitcode: 1--compilationhadfailure: True--outfile: Temp/Assembly-CSharp-firstpass.dll

Internal compiler error at Assets/Plugins/DemoBehaviour.cs(111,10):: exception caught while emitting MethodBuilder [DemoBehaviour::Demo]

The following assembly referenced from C:\Users\vexe\Desktop\FastSerializer\Assets\SerTest.dll could not be loaded:

     Assembly:   Assembly-CSharp-firstpass    (assemblyref_index=1)

     Version:    0.0.0.0

     Public Key: (none)

The assembly was not found in the Global Assembly Cache, a path listed in the MONO_PATH environment variable, or in the location of the executing assembly (C:\Users\vexe\Desktop\FastSerializer\Assets\).



Could not load file or assembly 'Assembly-CSharp-firstpass, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies.

-----CompilerOutput:-stderr----------



Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly 'Assembly-CSharp-firstpass, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies.

    
File name: 'Assembly-CSharp-firstpass, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'

To put it in a simplified way, here’s what I have:

public class DemoBehaviour : MonoBehaviour
{
    void Demo()
    {
        // access code in SerTest.dll
    }

    public class TestClass
    {
        // some fields....
    }
}

That goes into the firstpass.

Then I emit a dll that references TestClass and does stuff with ‘some fields’. The dll is called ‘SerTest.dll’ and is also located in the firstpass.

I really need to put that dll in the firstpass because I generate serialization code for user types, which could be in any pass, firstpass, plugins etc.

NOTE: I only get the errors when I try to do something with my generated Dll. So say there’s a type called “SERIALIZER” in it, in the ‘Demo’ method, if I do SERIALIZER.Serialize (access the dll statically) I get those errors. I looked around and found this answer - apparently, if I reference the dll dynamically via reflection, it works! so instead of SERIALIZER.Serialize, I do typeof(SERIALIZER).GetMethod(“Serialize”).Invoke(…) which is really weird…

I really appreciate any help.

I’m not sure if i fully understood your setup ^^. What seems a bit strange is that your SerText.dll accesses a class / method in a script file (which of course has to be compiled first).

The solution is: Don’t reference TestClass from your SerTest.dll.
Now it depends on when and where you need / want to access that TestClass from the SerTest dll. The usual way, the way most native plugins solve “callbacks”, is to provide an “Init” method in your SerTest dll that takes an parameter of an interface which your script can implement. So at runtime the first thing you do is to pass an implementation of that interface to your DLL and it’s going to store that in a static var. This initialization could even be done from a constructor / static constructor. Of course don’t use classes derived from a Unity type for the interface implementation as this will force you to wait for Awake.

If you just need a few simple callbacks, you can also use static delegates in your DLL which will be set from your script classes.

Another way is, like you already mentioned in your question, use reflection from the DLL to access the TestClass (not the other way round).

So it’s perfectly fine to statically link / access your “SERIALIZER” from the TestClass as the SerTest dll is already compiled. It makes no sense to introduce a static dependency in your SerTest dll to “Assembly-CSharp-firstpass” as the latter actually isn’t compiled yet.

edit

Ok, i just saw your video ^^. This is a very special case. Can’t you just “generate” your serializer code as text into script files? :slight_smile: That way it would be compiled along with the first pass.

If you can’t “generate” your code as text, i would suggest that you use reflection to create a delegate to your serialize method at runtime. Or, as said above, use an interface which your serializer implements. That way you just need to call a static method in your SerTest dll via reflection to create an instance and pass back the interface reference.