Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Hooking into a Windows IL2CPP build process

Discussion in '2018.1 Beta' started by pkbis, Feb 8, 2018.

  1. pkbis

    pkbis

    Joined:
    Aug 1, 2017
    Posts:
    18
    Hello,

    we have a build pipeline, which runs a weaving post-process using Mono.Cecil on our assemblies, to prevent any JIT'd code during runtime; the pipeline works great for Mono builds, because after BuildPipeline.BuildPlayer finishes, the assemblies are located in the Managed folder.

    However, weaving into assemblies after BuildPlayer finishes is not possible for IL2CPP builds, since the IL2CPP conversion seems to happen during the method call, and there seems to be no way to "hook" into this process.

    I know CompilationPipeline.assemblyCompilationFinished exists, but this event only gives a path to a single assembly at the time of firing the event.

    Having a event that gets fired after all of the managed assemblies are compiled and before IL2CPP is actually executed (with the IL2CPP execution waiting for this event subscribers to finish), which passes either the path to a staing area folder, or a list of paths of the compiled assemblies to be able to modify them ourselves, before actually running IL2CPP on them would be a really helpful.

    EDIT: Also, the CompilationPipeline.assemblyCompilationFinished event is fired mutliple times - which is something that can make the compilation times several minutes longer, in case of many huge assemblies and modifications. Having a single event after all compilations finish, for purposes of final post-processing would be much better, and would also be useful for mono build pipeline (however, for mono, it's easy to just modify the files in the folder after buildplayer finishes).
     
    Last edited: Feb 8, 2018
  2. Claytonious

    Claytonious

    Joined:
    Feb 16, 2009
    Posts:
    881
    We also do weaving via Mono.cecil. Try using the AssemblyReloadEvents.beforeAssemblyReload event.

    (Combine this with setting an assembly attribute on woven assemblies to make weaving idempotent, so you never to wonder whether you've already woven or not).
     
  3. pkbis

    pkbis

    Joined:
    Aug 1, 2017
    Posts:
    18
    We use the AssemblyReloadEvent for in-editor weaving; however, for builds, the event is not fired when BuildPipeline.isBuildingPlayer is true.

    I think if someone from Unity (e.g, @lukaszunity since he gave a brief explanation last time I reported issues with the assemblyCompilationFinished event) could chime in with description of the exact build process and (re)compilations during it (and all the possible "inject points"), it would be really helpful to see if there is currently any way to do this, or a new API would be needed. This would also be a great addition to the documentation, which seems to be lacking in this regard.
     
  4. pkbis

    pkbis

    Joined:
    Aug 1, 2017
    Posts:
    18
    Bump. Still stuck on this to be honest, preventing me from testing the IL2CPP alltogether. :/

    Tested it with CompilationPipeline.assemblyCompilationFinished, and it seems there is no way to get it to be executed only once at the end of the managed assemblies compilation, before the IL2CPP build is started.
     
    Last edited: Feb 12, 2018
  5. Claytonious

    Claytonious

    Joined:
    Feb 16, 2009
    Posts:
    881
    Like I said above, if you set an attribute on the assembly once you've woven it, you can make your weaving idempotent, so now it's safe to attempt weaving "more than once", because your own code will stop weaving if it has already been done on a given assembly. Your own code will check for the attribute and not weave again if the attribute is present, as in:

    Code (CSharp):
    1. if (moduleDefinition.CustomAttributes.Any(x => x.AttributeType.FullName.Contains("YourCompany.WovenAttribute")))
    2. {
    3.     return;
    4. }
    5. // Do weaving here...
     
  6. pkbis

    pkbis

    Joined:
    Aug 1, 2017
    Posts:
    18
    Flagging if the assembly is already weaved won't solve this, because with every event call, the assembly is "clean", and the code would run again, wouldn't it?

    Since the custom build step takes a minute or two, listening to this event during compilation multiple times is definitely bad due to the increase in build times; also, you cannot de-register the callback after the first call, because that would make the additional compilations override the assembly with the clean version again.

    Or am I missing something?
     
  7. pkbis

    pkbis

    Joined:
    Aug 1, 2017
    Posts:
    18
    Spent more time or this, and there are several issues - basically, to better explain it, the build pipeline looks (in a really simplified way) like this:

    - Prepare assets (excludes something from build, etc.)
    - Build player (executes BuildPipeline.BuildPlayer)
    - Post-build (weaves, generates AssetBundles, ...)

    The issue is with the post-build phases, since for mono, we have the managed assemblies ready in a folder, and the build has ended by that time - so we can do anything we want at that moment.

    For some of the weaving itself, the CompilationPipeline.assemblyCompilationFinished would be enough. However, for the Asset Bundles, this is much harder - because it includes not only weaving (to replace some definitions to the assets in the code, etc.), but also calls BuildPipeline.BuildAssetBundles - which triggers the compilation again, triggering the event listener, creating an infinite loop (yes, this can be solved with a simple bool flag, but the event to suggested in the OP would be a better solution anyways).

    The most annoying thing are the errors and the crash (seems to be consitent, I'll submit the crash log later), which will get emmited during the BuildAssetBundles call:

    Only one BuildReport can be registered for log messages at a time.

    and

    Could not load symbol mono_unity_lock_dynamic_function_access_tables64 : The specified procedure could not be found.

    I guess the crash has something to do with the execution of a asset bundle build while currently building a player?

    Basically, this therefore comes down to this:

    - Is the inability to build asset bundles during BuildPipeline.BuildPlayer phase a bug?
    - Even if this is "won't fix" thing and we will need to rewrite the build steps, could you at least add the event proposed in the OP? (I honestly believe it has legitimate use-cases, especially for IL2CPP builds.)
     
  8. joncham

    joncham

    Unity Technologies

    Joined:
    Dec 1, 2011
    Posts:
    276
    Regarding the errors, I am not sure about the BuildReport one. The `mono_unity_lock_dynamic_function_access_tables64` one is the crash handler looking up an optional symbol. I will fix this.

    As for the build events we can look into adding better events before the managed linker and AOT process is run on output assemblies during a player build.
     
    LeonhardP, pkbis and Peter77 like this.
  9. pkbis

    pkbis

    Joined:
    Aug 1, 2017
    Posts:
    18
    That would be great!

    I believe the BuildReport error can be caused with a code like this, but I'm not able to make the repro case and submit it right now (will try to do so over the weekend though).

    Code (CSharp):
    1.  
    2. public class Test
    3. {
    4.   private static bool buildingAB;
    5.  
    6.   public static void Build()
    7.   {
    8.     CompilationPipeline.assemblyCompilationFinished += Foo;
    9.     BuildPipeline.BuildPlayer();
    10.   }
    11.  
    12.   public static void Foo(string assembly, string[] messages)
    13.   {
    14.     if (!buildingAB && BuildPipeline.isBuildingPlayer)
    15.     {
    16.       buildingAB = true;
    17.       BuildPipeline.BuildAssetBundles();
    18.       buildingAB = false;
    19.     }
    20.   }
    21. }
    22.  
    As for the events - they would really help! I personally do not care if the parameters would contain the folder with the assemblies or an array of the assemblies that are going to be linked/il2cpp'd (both are fine for me). Is it possible they could make it into 2018.1, or is the API for 2018.1 already "finalized"?
     
  10. pkbis

    pkbis

    Joined:
    Aug 1, 2017
    Posts:
    18
    Hello,

    just want to ask if the API will be present at least present in 2018.2 since I got no response about .1 and the beta is comming to end really soon?