Assembly Definition Woes

Hi,

Am I using them wrong or are Assembly Definitions kinda useless out of the gate?

I set my projects up in such a way that I have one big Library folder (which contains all my external or reusable code) next to my game specific code.

I get the bright idea to make my Library folder one massive assembly (and why not?), but when it comes to building a standalone, it doesn’t seem to rebuild the assembly to respect Editor folders, ie hundreds of compiler errors because there are editor references it can’t find. Does this mean that I have to create tens or maybe hundreds of assemblies for each Editor folder?

Is there a solution here that doesn’t involve performing acrobatics to separate my editors into multiple folders? Which kind of makes my library folder useless.

Thanks.

Editor special folder do not work with asmref. Think of Editor folders as the old way of doing it and asmref as the new way, we dont intermix them. If you want the Editor folders to be Editor only then you need to either add another asmref to them and make it Editor only or move them out of the asmref.

1 Like

So if you have an asmref inside the domain (domain?) of another asmref it will split it out into another assembly?

Cos I may be able to live with that :wink:

Have you read this? https://docs.unity3d.com/2017.3/Documentation/Manual/ScriptCompilationAssemblyDefinitionFiles.html

1 Like

I haven’t, thanks for the link!

1 Like

[quote]
“Think of Editor folders as the old way of doing it and asmref as the new way, we dont intermix them.”
[/quote] –
Why not combine the best of both worlds and have at least a toggle on Assembly Defintion Files that says “put Editor folders inside this structure into a separate -Editor assembly”?

Right now, using assembly definition files on any serious project that uses a combination of external submodules and asset store packages is impossible. The usual folder structure has Editor folders all over the place, right next to the specific scripts they relate to, which in my opinion is a very good thing that I wouldn’t want to change.

5 Likes

No we want to move away from special folders. The new way will be faster but requires a little time to set up the assembly dependencies, if we support special folders then we don’t know which editor folders need each other and so must reference them all and we lose any benefits. The point is for it to be very tight with assemblies only referencing the ones they actually need, then when a change occurs we can intelligently rebuild. If you want the old behavior then don’t use assembly definitions or write a script to scan for editor folders and set it up for you automatically.

7 Likes

@karl_jones - I agree with the other developers here. Having a simple option to automatically put Editor folders into the old Assembly-CSharp-Editor.dll would be a huge help. What you’re asking requires that we put asmdef files into folders that we shouldn’t be messing with. For example if we are importing Assets from the Asset Store. It’s common practice for Asset Store assets to require that you delete the folders before updating, because as I’m sure you know, the package import process does not remove files that were removed by the asset developer in asset package updates.

Followup:
I just counted and our 2D Game project has 38 Editor folders in it! Having to put asmdef files in all of those just seems like an unreasonable thing to ask.

What might work is if we could specify a path/folder filter in the asmdef file so that we only need to maintain an extra asmdef file in the root folder (e.g. TextMeshPro or VoxelBusters) to exclude things like Editor folders.

Another idea is that if multiple asmdef files have the same assembly name, then they could combine into one assembly. For that to work you’d have to make the asmdef references refer to Assembly names and not asmdef assets.

3 Likes

The issue of assets store products should be solved over time with the Package Manager .

The package manager looks like a nice improvement.

Currently the asmdef files create a separate csproj file for every one. If asset store packages bring in dozens of other asmdef files, hopefully you can figure out a way to keep this more compact, for example place the csproj files into a folder that the .sln references?

@karl_jones Another idea would be to have at least a bit more control about how asmdef files collect scripts. For example, a “include folders by name” string list and a “exclude folders by name” string list. That should keep everything working the same way you intend it to do, but allows people the flexibility to have “special folders” if they want. I always felt it was a great thing that I can create a script, and then after I find I need to have a custom editor for it, just create an Editor folder right next to it with the script’s custom editor in it. What do you think?

Then I could have two asmdefs in a project at top level at least - one with “exclude Editor folder” and one with “include Editor folder”.

I’m not convinced that’s a good idea. Its a fair bit of complexity for something we are trying to move away from.You can still have a script and an editor folder side by side. You just put an editor assembly in the editor folder.

2 Likes

I’m just trying to understand why you want to move away from it, and how the new workflows are supposed to be, both for newcomers and for experts. (“Developer Usability”, a topic that is rarely thought about enough, Unity as a platform is a great exception to that)

Also, I second @tessellation in that current projects contain easily dozens of Editor folders (I counted – 29 for a current project which uses a combination of Package Manager, AssetStore packages and git Submodules).
“Solving over time” only works if there is a gradual path to that shiny future – currently, the documentation says “use asmdefs for everything or not at all”, which, given that not all submodules/assetstore packages use them, means “not at all” unfortunately. We’re actively looking into it (thus my involvement here) trying to figure out how to get there asap without breaking all external components we’re using.

What would be the new recommended workflow for having CustomEditors for scripts?
Put them “somewhere you think it might fit”?
Put them “in your single Editor folder where all your editor scripts are”?
Put them “in an Editor folder right next to the script but don’t forget to create a new asmdef for that single editor script”?

Hi again.
To not be the “guy who just complains”, I today tried to do everything the “new way” and upgrade a whole existing project to using assembly definition files.

Here’s my writedown:

Original Project Statistics

  • 121 own scripts

  • 4 AssetStore packages

  • 6 unitypackages from github

  • 6 git submodules (with some more recursive submodules)

  • 1 new UnityPackageManager packages (ProBuilder)

  • 27 editor folders scattered around, 5 in own scripts

  • 8 projects in Visual Studio (csproj files)

  • ProBuilder.AddOns.Editor

  • PackageManagerUI.Editor

  • PostProcessing.Runtime

  • PostProcessing.Editor

  • MyProject

  • MyProject.Editor

  • MyProject.Editor.Plugins

  • MyProject.Plugins

  • compilation time when changing a script ~9s

  • time for Visual Studio to react when double-clicking a script: ~6s

  • cold start for Visual Studio (not open before): ~19s

Process
All in all, it took me and a coworker ~3hrs to go through the project.
That was split up in ~1.5hrs adding asmdefs and connecting them for all folders with our code – project was compiling at that point again; and ~1.5hrs making the project build again (adding asmdefs to all editor folders).
In the end, we mostly made our code asmdef ready, and only touched some of the external references, mostly because we didn’t want to break upgrade compatibility for those.

Pain Points

  • When creating asmdefs for Editor folders, the process is very very repetetive:

  • create asmdef

  • unselect “”

  • click on “Unselect All”

  • click on “Editor”

  • Apply.

  • drag other asmdef (that’s not from this editor) into the references

  • Apply again

  • 90% of the time, when clicking somewhere in the Project Window have to “Apply” again (msg window pops up)

  • It takes a lot of thinking to figure out what goes where – I consider myself an advanced Unity/C# developer, and it felt like going back in time to having to AddReference tons of things in a larger project with multiple subcomponents. I think this is essientially because that’s what asmdefs do, add references internally.
    I don’t think that Unity beginners or even intermediary users will have any benefit from doing this.

  • I had to break the update path for the external components we decided to asmdef’y (because they were directly referenced from our own code), since now if I upgrade them, I need to re-create the asmdef files
    (most more complex AssetStore packages ask you to delete the folder and then import the upgrade because they contain DLLs etc.)

  • Visual Studio now doesn’t fly, it just crawls on the floor screaming for help. Every time I cold start it, it takes ~30s, every time I double-click on a script in Unity after a recompile (with Visual Studio open) it takes ~138s (I used a stopwatch to measure that!)
    I have no idea whether that’s a bug with visual studio or the Unity integration or Unity, but it happens on all machines I tested on.
    There are now 32 projects (csproj) that are loaded/unloaded every time.

  • When creating a new script from inside Unity and double-clicking on it, Visual Studio

  • unloads/reloads all csproj files – this takes 138s

  • then tells me “something has changed externally, should I reload?” which takes another 138s

  • bringing the total time to 276s (more than 4 minutes)

  • Renaming the Assembly Name inside the asmdef file breaks all references to that asmdef file, contrary to how Unity usually handles asset references it seems these are not serialized with references of asmdef files to each other but instead with string references.
    I consider this an “annoying but not crucial bug”.

  • There were some unsolvable dependency issues in the process that I’ll try to describe here:

  • we have a submodule with some code that references SomeExternalComponent (it creates nodes for a node-based system).

  • that code lives in an asmdef

  • these nodes use #if SOME_EXTERNAL_COMPONENT to make sure they compile even if SomeExternalComponent is not present (a common practice).

  • the submodule in itself is fine (no SomeExternalComponent in there)

  • when adding the submodule to a project where SomeExternalComponent is contained, it complains of not finding it.

  • SomeExternalComponent does not use asmdef, so had to add it manually

  • referencing the SomeExternalComponent asmdef to the asmdef of the submodule that creates nodes for it works

  • now pushing those changes back the submodule

  • now the submodule doesn’t compile in itself anymore, since it complains of not finding that SomeExternalComponent asmdef.

  • Ended up removing the node-generation code and not using asmdefs for it and SomeExternalComponent. Not sure what the right way would be here.

Result Project Statistics

  • 26 asmdef files, with up to 5 references
  • 12 of those for Editor folders

Compilation time is down from ~9s to ~5s for scripts that are in an ideal place (have their own asmdef not referencing anything). To my understanding this still takes that amount of time because all those external packages that we didn’t dare to touch/add asmdefs to will be recompiled everytime such a poor little script is changed.

Compilation time is up from ~9s to ~13s for scripts that are at the end of the reference chain.

Visual Studio opening time is up from ~6s to ~138s since Visual studio unloads/reloads all those projects every time a script has changed (basically every time I save a script, go to Unity, let it compile, then double-click on a script to open it).

Postmortem
Fortunately all these changes happened in a separate branch and are now packed away until some or all of these issues are resolved. We will not be using asmdef files until then. Maybe the unloading/loading is just me missing a configuration detail, but that’s just using a fresh install of 2018.1beta10, so the experience will be the same for most users.

For working on core scripts/modules, if compilation times actually increase, it might make sense to allow users to globally enable/disable the use of these asmdef files altogether, to make the overhead of working on sth that most of everything else refers to less of a burden. For a backend developer, the introduction of asmdefs made compilation times worse.

15 Likes

@fherbst I had a somewhat similar experience.

What I ended up doing was actually editing the asset store packages I use and make them compatible with custom assemblies. So I ended up with a total of 8 projects:

myGame
Devel (for fast development, depends on everything, no dependencies)
Vendor
Tests
and their -Editor counterparts.

What would really improve this workflow is to have partial asmdef files, where the same assembly could be split into multiple folders.

Check out this common pattern

Assets\SomeModule
Assets\SomeModule\Editor

Assets\AnotherModule
Assets\AnotherModule\Editor

With partial asmdef files, we would stick one MyGame-Editor.asmdef file in each of those Editor folders, and they would all be part of the MyGame-Editor assembly.
Without it, we need to change the structure to this

Assets\SomeModule
Assets\AnotherModule
Assets\Editor\SomeModule
Assets\Editor\AnotherModule

Now we need to maintain the names of those folders in two separate locations.
Even as a sole dev, this will most certainly get out of sync with time. It is also much worse to navigate when using an IDE with a dedicated “Unity” solution explorer (like Rider or Visual Studio).

This is also very bad for asset store packages, since it makes maintaining the packages harder and most packages use the “canonical” folder structure of having the Editor folder inside whatever it is the editor for.

We talked a bit more about this partial asmdef files in [this thread]( Example Project - Assembly Definition Files page-6#post-3321822)

All in all, I really hope this partial asmdef file idea lands at some point. This, along with a easy way to create packages to use with the Package Manager (and have a way to have their resources available in the project) will make Unity development much faster and modular.

2 Likes

Putting an “editor assembly” in each folder with editor stuff will add up real quick. We have lots of very modular stuff, each module having its own Editor folder. If I were to follow this pattern, I would end up having well over a hundred projects, each containing just a few scripts (editors are usually only 1 or 2 script files). This overhead alone would make asmdef files lose their appeal.

I re-structured the project to have all editor stuff in a separate folder, and asmdef files are cool in this configuration, but it is a lot of work and duplication. I gave some more context in the previous post .

3 Likes

To solve the Editor folder script issue - I made a editor script that would turn all the assembly definition files on/off. So while developing we’d have them on (for quick compile times / code modularity) then turn them on when actually building the game / building asset bundles. I thought it worked great for a while - then I noticed all the Unity UI events I had set up would then be ‘missing’ after switching (since they reference using the assembly name).

So since that didn’t work, I decided to make a script that would generate all the assembly definition files for the Editor folders. I didn’t want to have 70+ projects created - so this is still something I only turn on when I am building the game. Generates them based on their parent’s assembly definition file.

    class AssemblyDefinitionType
    {
        public string name;
        public List<string> references;
        public List<string> includePlatforms;
        public List<string> excludePlatforms;
    }

    public class AssemblyDefinitionSwitch
    {
        [MenuItem("Tools/Create EditorAssemblyDefFiles")]
        public static void TurnOnAssembly()
        {
            CreateEditorFiles(new DirectoryInfo(Application.dataPath), null, false);
            AssetDatabase.Refresh();
        }

        [MenuItem("Tools/Delete EditorAssemblyDefFiles")]
        public static void TurnOffAssembly()
        {
            CreateEditorFiles(new DirectoryInfo(Application.dataPath), null, true);
            AssetDatabase.Refresh();
        }

        static List<string> EditorAssemblySibilings;
        static int duplicateNum = 0;

        static void CreateEditorFiles(DirectoryInfo Dir, FileInfo ParentAssemb, bool Remove)
        {
            FileInfo[] files = Dir.GetFiles();
            foreach (FileInfo file in files)
            {
                if (file.Extension == ".asmdef")
                {
                    ParentAssemb = file;
                    EditorAssemblySibilings = new List<string>();
                    duplicateNum = 1;
                }
            }

            if (ParentAssemb != null)
            {
                DirectoryInfo[] dirs = Dir.GetDirectories();
                foreach (DirectoryInfo subdir in dirs)
                {
                    if (subdir.Name == "Editor")
                    {
                        FileInfo[] editorfiles = subdir.GetFiles();
                        foreach (FileInfo file in editorfiles)
                        {
                            if (file.Extension == ".asmdef")
                            {
                                if (Remove)
                                    File.Delete(file.FullName);
                            }
                        }
                        if (!Remove)
                        {
                            // create a assemb file based on the Parent one, only with includePlatforms changed to Editor
                            // assembly name is the parent assembly name + directory that contains the editor folder + "Editor"

                            AssemblyDefinitionType EditorAssemb = JsonUtility.FromJson<AssemblyDefinitionType>(File.ReadAllText(ParentAssemb.FullName));
                            EditorAssemb.references.Add(EditorAssemb.name); // add parent assembly as a reference
                            EditorAssemb.references.AddRange(EditorAssemblySibilings); // for subeditor folders to reference the above editor assemblies

                            EditorAssemb.name += Dir.Name + "Editor";
                            if (EditorAssemblySibilings.Contains(EditorAssemb.name))
                            {
                                EditorAssemb.name += duplicateNum;
                                duplicateNum += 1;
                            }
                            EditorAssemblySibilings.Add(EditorAssemb.name);
                            EditorAssemb.includePlatforms = new List<string>();
                            EditorAssemb.includePlatforms.Add("Editor");
                            File.WriteAllText(Path.Combine(subdir.FullName, EditorAssemb.name + ".asmdef"), JsonUtility.ToJson(EditorAssemb));
                        }
                    }
                }
            }

            DirectoryInfo[] dirs2 = Dir.GetDirectories();
            foreach (DirectoryInfo subdir in dirs2)
            {
                if (subdir.Name != "Editor")
                    CreateEditorFiles(subdir, ParentAssemb, Remove);
            }
        }
}
3 Likes

@LVermeulen That’s neat!

This could be a real alternative to modifying the project structure, and behaves similar similar to how Unity handles the UnityEditor namespace: you can use anything from UnityEditor during development (available to all), but it will not be available during a build (so you get errors pointing to where you incorrectly used it).

Thanks for sharing your idea (and code)!

Hi there,
just to support what has already been said…
I was really glad when I first saw the assembly definitions feature, I thought this would really improve the compile time of my big project. I went through the same process that @fherbst described, adding asmdefs everywhere, including ones for Editor folders, and trying to figure out how to asmdef’y Asset Store assets and even the Standard Assets folder (spoiler: it’s very hard)…

And now I’m thinking of removing them altogether, because moving or adding any script makes Visual Studio completely reload everything, or when it doesn’t, the file does not see any reference nor can be seen by.

Btw, apart from creating asmdefs for each Editor folder being horribly tedious, I see how it makes for a nice and unified system, and still I’m not sure that the Editor should be considered a platform… it somehow feels conceptually weird.

1 Like

A good idea would probably be to allow multiple asmdefs with the same assembly name (maybe with an additional toggle) to combine into one, so separate folders from different script modules could be treated as one (like “Editor” for example).

2 Likes