Problems compiling DLLs from MonoDevelop

EDIT: Problem solved. See here for a how-to on setting up for DLL compiling in MonoDevelop using UnityScript.

I’ve puzzled my way through getting MD to compile DLLs for UnityScript, but when I actually use those DLLs in Unity I keep getting errors which I believe are because MonoDevelop is compiling for the .Net 4.0 framework even though I have set the target framework to 2.0.

I have duplicated exactly all the settings in Unity’s own Assembly-UnityScript.unityproj and included the exact same references, but when I check my compiled DLLs with ILSpy, it tells me the runtime is 4.0 whereas the default Unity project DLLs compiled to Library/ScriptAssemblies all show as runtime 2.0 in ILSpy. Expanding the references in ILSpy, mine show mscorlib 4.0.0.0, and Unity’s show 2.0.5.0. I can’t figure out how to force MonoDevelop to compile to the .Net 2.0 framework.

Even stranger, Unity’s Assembly-UnityScript.unityproj says the target framework is .Net 3.5, but the final compiled DLLs show runtime 2.0 in ILSpy. Some kind of behind the scenes magic must be going on when Unity compiles its projects.

By the way, the error is:

MissingMethodException: Method not found: 'System.Type.op_Equality`;

Every site I’ve come across says this is because System.Type.op_Equality was added in .Net 4.0 and is not supported in earlier versions like Unity uses.

Another tidbit of information… After spending hours and hours trying everthing including the kitchen sink, I’ve come to the conclusion that after the intial build in MonoDevelop Unity changes its compiled DLLs in a 2nd pass (synchronization?) or perhaps it just simply recompiles not using MD the second time. The Assembly-UnityScript.dll file built in MonoDevelop goes to the folder /Temp/Debug/bin. This version of the DLL shows runtime 4.0 in ILSpy. Once you click over to Unity though, it again compiles and creates a new version of Assembly-UnityScript.dll in Library/ScriptAssemblies. This version always shows runtime 2.0 in ILSpy.

In other words, no matter what settings I use or references I include in MonoDevelop, it ALWAYS compiles to .Net 4.0.

EDIT: Solved! See this post below.

Given that the options in player setting for api compatibility are for .net 2.0 and .net 2.0 subset it doesn’t seem strange that the runtime is reported as 2.0.

Hmm, that’s true. So that setting there must be the 2nd build target once Unity recompiles the solution outside MonoDevelop.

What seems strange is MonoDevelop seems incapable of outputting a runtime 2.0 DLL… Just did some more testing. I take that back. MonoDevelop can easily output a 2.0 DLL … in C# but NOT in UnityScript. Only Unity’s compiler can output a 2.0 in UnityScript, which means there’s no solution to the bug and I have to just abandon making external libraries from my existing US code. Seems they overlooked actually implementing the Compile Target option in MD with US even though the GUI supports it.

YET AGAIN it’s a dang UnityScript flaw. I can’t tell you how many times I’ve hit a wall in the last 2 years because UnityScript is either missing features or poorly supported as in the case of MD and its cornucopia of UnityScript oversights and bugs. All you people using C#, you have a totally different experience coding for Unity. At least the tools work right for you.

Unity, we are paying a whole lot of money for this thing. Either fully support the language or get rid of it.

This is not implemented in the Mono version of .NET.

Whilst the operator is not implemented, you can perform type comparisons using the following instead:

// This is C#
object.Equals(typeof(A), typeof(A)) == true;

Another solution is to use the following:

//
System.Type TyA = typeof(A);
System.Type TyB = typeof(B);

if (TyA.FullName == TyB.FullName) {
   // Nope! doesn't happen because different type
}

Thanks for those. The object comparison worked fine and now the error is gone, but I still question whether its wise for me to use .Net 4.0 assemblies in Unity. Do you think it would cause any other problems? I imagine it would require that users install the .Net 4.0 runtime to play the game, which in itself isn’t a bad thing, but since Unity doesn’t run on 4.0, would it include the 4.0 runtime installer?

Also, I’m wondering, if the Type.Equality operator wasn’t included until 4.0, why do direct type comparisons work in Unity’s default assemblies? Am I to assume when Unity does its 2.0 compile pass it does some auto substitution of things like that?

Unity does not use .NET 4.0, it uses Mono (which is not the same thing). The version of Mono includes some of the 4.0 features, but certainly not all of them.

I have been targeting 4 when building but using the minimal amount of features possible. The only reason that I am targeting 4.0 and not 3.5 is that I like to use default function arguments (whilst this works with .NET 3.5 it requires Mono 4). This is certainly confusing, just make sure that you test thoroughly before release!

I always build using the MonoDevelop IDE because whilst building with Visual Studio does work, I have encountered a number of serious issues that have led to corruption of prefabs (when DLL is updated). This does not stop me from using Visual Studio for coding because it is a better IDE which doesn’t crash (unlike MonoDevelop which always crashes and causes me to recode the same thing), but I never build using Visual Studio.

I am always interested in hearing the views of other developers, so please let me know how you get on and any other issues that you encounter. This particular issue is not a Unity one though but rather one with Mono. There are issues with compiling DLLs for Unity, so please be careful!

Both of those are horrible…

if(ReferenceEquals(typeof(A), typeof(B))) { ... }

Unity does not support the new CLR thats used by .NET 4 assemblies.
In consequence when you drop a .NET 4 assembly in, you will get an error which prevents you from running and compiling the game till the assembly is removed again.

That might change somewhen in the future, but that future will definitely not be this year anymore as it is at earliest going to happen with Unity 4.1 if not later, as 4.0 is the same mono version as used in 3.x

True. So I guess I don’t have to worry about the .NET 4.0 installer then. I’m just worried about hitting other incompatibilities between .NET 4 and Mono. But the compiled DLLs do include references to mscorlib.dll v4.0.0.0 (ones I compile) and v2.0.5.0 (ones Unity compiles). These are references to Microsoft .NET dlls as far as I’m aware.

If only they’d make the compiling process they use internally available it would solve all these issues. It seems really silly to code blindly in .Net 4.0 only to have to go into Unity and test everything for incompatibilities. The type equality operator works in Unity assemblies compiled by the engine.

That’s interesting. I figured Visual Studio would totally replace MD. I take it you haven’t had the prefab corruption problem with DLLs export from MD?
MonoDevelop doesn’t ever crash on me which is good I guess, but that’s about the only good thing it does do. You don’t even get underlining in US and code completion 1) does not work right 95% of the time and 2) drags your system to a crawl in a class longer than a couple thousand lines or so.

Thanks for your help! :slight_smile: If I can get this thing divided up between DLLs and scripts it will save a lot of time compiling when testing.
Actually I think this is a Unity issue because they are the ones who are supposed to support the UnityScript language and they’re providing the MonoDevelop build with addins for UnityScript. The code to set a build target in US is broken and nobody but they can fix it. Plus I think it’s really weird they do that double-compliation where Unity compiles differently internally than the tool they give you to code in. Being able to use things like the type equality operator in Unity assemblies and not your own makes it almost like they’ve got 2 versions of UnityScript, one with more features than the other. It’s just weird.

Thanks!

Interesting. Do you mean when you do a release build? Because it is letting me run the game in the editor with the .NET 4 assembly I compiled in MD, albeit with errors unless I implement workarounds.

I was hoping to use Visual Studio as a replacement too. When I use Visual Studio prefabs acquire additional broken components when replacing the DLL with the newer compiled version. The issue is intermittent but occurs frequently when compiling with VS, the issue has never occurred for me when my DLLs compiled using MonoDevelop.

Here is a related forum thread that I started:
http://forum.unity3d.com/threads/144601-Unusual-issue-with-editor-script

I have MonoDevelop open for around 8 hours at a time and find that during that time it will crash 2 or 3 times. Whilst I am not certain on the cause, the problem seems to be caused by the compile warning/error overlays. During development I have a lot of warning overlays due to missing XML comments (I write documentation once testing has been completed).

But now I am just literally hitting the build button in MonoDevelop with no source files open, it doesn’t crash at all.

According to Dantus you do have access to another compiler provided by Unity which must be run from the command line:

With the exception of the missing operators for comparing System.Type I have not experienced any other issues with targeting .NET 4 (except of course with the prefab issue with Visual Studio). Once I have finished the project that I am currently working on I am going to spend some time comparing the three compilers.

Oh, and the following is quite interesting if you haven’t already read it:

Framework versions 3.0 and 3.5 were purely updates to the libraries - adding WCF/WPF/WF, LINQ, and so on; they didn’t change the actual CLR, and so still use runtime 2.0. Framework 4.0 changed the CLR which is why its assemblies are incompatible.

Anyway, I’m not sure why you’re having trouble; I’ve got at least one externally-compiled UnityScript DLL, and Unity (3.4.2) picks it up with no problems.

Which versions of Unity and Monodevelop are you using?

How did you go about setting the target framework in MD?

@superpig I have set the target framework for my DLL project using the project options box in MonoDevelop to “Mono / .NET 4.0” which seems to be working perfectly for me at least. The only reason I am targeting this (as opposed to Mono / .NET 3.5) is that I require default function arguments which do not appear to be otherwise available.

Would you mind shedding a little light on that for me?

With Visual Studio it is possible to compile a DLL to target .NET 2.0 runtime whilst using the C# 4.0 compiler. I am wondering if this is possible with MonoDevelop…

Okay after many more hours of fiddling and fuddling around, I finally found a solution!

I tracked down Unity’s internal UnityScript compiler. It’s at C:\Program Files (x86)\Unity\Editor\Data\Mono\lib\mono\unity\us.exe. Figuring out how to use it was a lot of head bashing, but then I decided to see if the Unity log was any help and lo and behold everything I needed was in there. When Unity imports, it logs everything its doing and told me just about all the command line arguments I needed to compile exactly like the Editor does.

Here’s a batch file you can place within your external solution project folder. It will compile all the JS files within that folder (and subfolders I believe) into a .NET 2.0 DLL which it will put in bin\Debug. Edit: It now copies the .DLL and .MDB to a folder you choose in your UnityProject/Assets folder. It also includes instructions on integrating with MonoDevelop so you can do your final compile directly to your game folder.

@echo off
REM by guavaman (http://forum.unity3d.com/members/12523-guavaman) 8/25/2012

REM This batch file should be placed in project directory with the .js files
REM Uses Unity's internal compiler to compile a DLL and MDB from a directory of .js files
REM and copies them to a target directory.

REM NOTE: Make sure your source directory path (the current directory) has no spaces in it because quotes will not work to contain it.

REM Workflow:
REM You can add this to MonoDevelop as a menu item in your project so you can compile
REM normally in MD for error checking, then once you're done and you want a final DLL,
REM run the menu item Project -> Compile to Unity.
REM To install in MonoDevelop as a menu item, go to MonoDevelop, project options, custom commands, click "(Select a project operation)" and change it to "Custom Command".
  REM Name: Compile to Unity
  REM Command: compile.bat
  REM Working Directory: ${ProjectDir}
  REM Check "Run on External Console"

REM ---- YOU MUST EDIT THESE VARIABLES -----

REM User defined variables - Set these variables based on your installation
  REM TARGET_DIR = the directory to which the final .dll and .mdb file should be copied
  REM Target directory should be somewhere within REM your Unity project\Assets directory.
set UNITY_INSTALL_DIR=C:\Program Files (x86)\Unity
set TARGET_DIR=C:\PATH_TO_YOUR_UNITY_PROJECT\Assets\Assemblies

REM Add additional DLL references if necessary
REM Example:
    REM set ADDITIONAL_DDLS=-r:"FULL_PATH_TO_DLL\Assembly.dll" -r:"FULL_PATH_TO_DLL\Assembly.dll" -r:"FULL_PATH_TO_DLL\Assembly.dll"
set ADDITIONAL_DDL_REFS=

REM ---- DO NOT CHANGE ANYTHING BELOW THIS LINE ----

REM Get the directory name
::setlocal enableextensions,enabledelayedexpansion
SET CDIR=%~p0
SET CDIR=%CDIR:~1,-1%
SET CDIR=%CDIR:\=,%
SET CDIR=%CDIR: =_%
FOR %%a IN (%CDIR%) DO SET "PROJECT_NAME=%%a"
SET PROJECT_NAME=%PROJECT_NAME:_= %

REM Set up other varabiles
set SOURCE_DIR=%~dp0
set OUTPUT_DIR=%SOURCE_DIR%bin\Debug
set UNITY_DLL_DIR=%UNITY_INSTALL_DIR%\Editor\Data\Managed
set MONO_PREFIX=%UNITY_INSTALL_DIR%\Editor\Data\Mono
set MONO=%MONO_PREFIX%\bin\mono.exe
set MONO_PATH=%MONO_PREFIX%\lib\mono\unity
set MONO_CFG_DIR=%MONO_PREFIX%\etc

REM Compile
echo Compiling...
echo Compiling... > compile_log.txt
"%MONO%" %MONO_OPTIONS% --runtime=moonlight --security=temporary-smcs-hack "%MONO_PATH%\us.exe" -debug -target:library -i:UnityEngine -i:System.Collections -base:UnityEngine.MonoBehaviour -nowarn:BCW0016 -nowarn:BCW0003 -method:Main -out:"%OUTPUT_DIR%\%PROJECT_NAME%.dll" -x-type-inference-rule-attribute:UnityEngineInternal.TypeInferenceRuleAttribute -define:UNITY_3_5_5 -define:UNITY_3_5 -define:UNITY_EDITOR -define:ENABLE_PROFILER -define:UNITY_STANDALONE_WIN -define:ENABLE_GENERICS -define:ENABLE_DUCK_TYPING -define:ENABLE_TERRAIN -define:ENABLE_MOVIES -define:ENABLE_WEBCAM -define:ENABLE_MICROPHONE -define:ENABLE_NETWORK -define:ENABLE_CLOTH -define:ENABLE_WWW -define:ENABLE_SUBSTANCE -r:"%UNITY_DLL_DIR%\UnityEngine.dll" -r:"%UNITY_DLL_DIR%\UnityEditor.dll" %ADDITIONAL_DDL_REFS% -i:UnityEditor -srcdir:%SOURCE_DIR% >> compile_log.txt

REM Check if compile was completed successfully
IF errorlevel 1 (
  echo Compile failed! See %SOURCE_DIR%\compile_log.txt for details!
  echo Compile failed! >> compile_log.txt
  GOTO END
) ELSE (
  echo Compile completed!
  echo Compile completed! >> compile_log.txt
)

REM Copy the files to target directory
echo Copying files to target folder...
echo. >> compile_log.txt
echo Copying files to target folder... >> compile_log.txt
echo xcopy "%OUTPUT_DIR%\%PROJECT_NAME%.*" "%TARGET_DIR%\" /Y >> compile_log.txt
xcopy "%OUTPUT_DIR%\%PROJECT_NAME%.*" "%TARGET_DIR%\" /Y >> compile_log.txt
IF errorlevel 1 (
  echo File copy failed! See %SOURCE_DIR%\compile_log.txt for details!
  echo File copy failed! >> compile_log.txt
 MonoDevelop.
) ELSE (
  echo .DLL and .MDB copied to %TARGET_DIR%!
  echo .DLL and .MDB copied to %TARGET_DIR%! >> compile_log.txt
)

:END

This has nothing to do with the .unityproj options so don’t expect any settings from MonoDevelop to carry over.

I can’t believe it was this tough to get a proper DLL compiled from UnityScript that won’t choke on things like .NET 4.0 incompatibilities. Maybe I should make a small guide.

Oh yeah, one EXCELLENT side effect of compiling like this is that you get a .MDB file automatically with no need to use PDB2MDB, which breaks on anything with an IEnumerable in it including MonoBehaviour.

Glad to hear. Still, backup, backup, backup! :slight_smile:

Yes, I assumed it would be there somewhere and I’ve been digging all day long. Finally found out how to make it work! Now I’ll have to see if I can get a little more integration with MD.

It works for me too in C#. It only fails in UnityScript.

It is picking it up fine, but depending on what you do in the code you may or may not get errors. For example, the issue with the missing type comparison operator. There are also some issues like having to explicitly type System.Type whereas just Type would work before.

Unity latest. 3.5.5f3, MonoDev 2.8.2, came with Unity.

Through the GUI, project options, General. I’ve also fiddled with everything possible inside the .unityproj files.

I have been experimenting and have found some points of interest (at least to me):

Point 1 MonoDevelop uses the Microsoft .NET compiler when used on Windows from the framework associated with the target platform.

When target is “Mono / .NET 3.5” it uses “C:\Windows\Microsoft.NET\Framework\v3.5\csc.exe”
When target is “Mono / .NET 4” it uses “C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe”

Point 2 Visual Studio always uses the compiler “C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe” regardless of the target framework.

Point 3 Despite using the latest C# compiler, Visual Studio links to the targeted framework libraries (as expected). This means that new language features of C# can be used in conjunction with older versions of the .NET framework.

To me it seems that MonoDevelop should be following suite by always using the newer compiler, whilst using the specified target framework.

I have been comparing the build command line used by Visual Studio and MonoDevelop and am confused as to why one causes me issues with Unity, whilst the other doesn’t (when targeting .NET 4 from both IDEs). For some reason the MonoDevelop version works and the Visual Studio one causes issues with prefabs.

Note: I have re-ordered command arguments and placed onto separate lines for easier comparison.

MonoDevelop : Target = Mono / .NET 4

C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe
/noconfig
/warn:4
"/define:TRACE"
/optimize+
"/out:C:\TestLibraries\TestEditor\bin\Release\TestEditor.dll"
/t:library

/nologo

 "/r:C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.dll"
 "/r:C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.Core.dll"
 "/r:C:\Program Files (x86)\Unity\Editor\Data\Managed\UnityEditor.dll"
 "/r:C:\Program Files (x86)\Unity\Editor\Data\Managed\UnityEngine.dll"

 /fullpaths
 /utf8output

MonoDevelop : Target = Mono / .NET 3.5

C:\Windows\Microsoft.NET\Framework\v3.5\csc.exe
/warn:4
"/define:TRACE"
/optimize+
"/out:C:\TestLibraries\TestEditor\bin\Release\TestEditor.dll"
/t:library

/nologo
-nostdlib

 "/r:C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.dll"
 "/r:C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.5\System.Core.dll"
 "/r:C:\Program Files (x86)\Unity\Editor\Data\Managed\UnityEditor.dll"
 "/r:C:\Program Files (x86)\Unity\Editor\Data\Managed\UnityEngine.dll"

 /fullpaths
 /utf8output

Visual Studio : Target = .NET 4

C:\Windows\Microsoft.NET\Framework\v4.0.30319\Csc.exe
/noconfig
/nowarn:1701,1702
/warn:4
/define:TRACE
/optimize+
/out:obj\Release\TestEditor.dll
/target:library

/nostdlib+ /errorreport:prompt

/reference:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\mscorlib.dll"
/reference:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.Core.dll"
/reference:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.dll"
/reference:"C:\Program Files (x86)\Unity\Editor\Data\Managed\UnityEngine.dll"

/debug:pdbonly
/filealign:512

Visual Studio : Target = .NET 3.5

C:\Windows\Microsoft.NET\Framework\v4.0.30319\Csc.exe
/noconfig
/nowarn:1701,1702
/warn:4
/define:TRACE
/optimize+
/out:obj\Release\TestEditor.dll
/target:library

/nostdlib+ /errorreport:prompt

/reference:C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorlib.dll
/reference:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.5\System.Core.dll"
/reference:C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.dll
/reference:"C:\Program Files (x86)\Unity\Editor\Data\Managed\UnityEngine.dll"

/debug:pdbonly
/filealign:512

Point 4 According to the MonoDevelop settings, Unity will use MonoDevelop to build project when debugging. See “Tools → Options → Unity → Debugger”

These are more notes than anything, but perhaps there is something hidden in here somewhere :stuck_out_tongue: