Ejemplo n.º 1
0
    private static DotsRuntimeCSharpProgram SetupGame(AsmDefDescription game)
    {
        var gameProgram = GetOrMakeDotsRuntimeCSharpProgramFor(game);

        var   withoutExt     = new NPath(gameProgram.FileName).FileNameWithoutExtension;
        NPath exportManifest = new NPath(withoutExt + "/export.manifest");

        Backend.Current.RegisterFileInfluencingGraph(exportManifest);
        if (exportManifest.FileExists())
        {
            var dataFiles = exportManifest.MakeAbsolute().ReadAllLines();
            foreach (var dataFile in dataFiles.Select(d => new NPath(d)))
            {
                gameProgram.SupportFiles.Add(new DeployableFile(dataFile, "Data/" + dataFile.FileName));
            }
        }

        var configToSetupGame = new Dictionary <DotsRuntimeCSharpProgramConfiguration, DotNetAssembly>();

        if (!PerConfigBuildSettings.ContainsKey(game.Name))
        {
            return(null);
        }

        foreach (var config in PerConfigBuildSettings[game.Name].Where(config => !CanSkipSetupOf(game.Name, config)))
        {
            gameProgram.ProjectFile.StartInfo.Add(c => c == config,
                                                  StartInfoFor(config, EntryPointExecutableFor(gameProgram, config)));
            gameProgram.ProjectFile.BuildCommand.Add(c => c == config,
                                                     new BeeBuildCommand(GameDeployBinaryFor(gameProgram, config).ToString(), false, false).ToExecuteArgs());


            DotNetAssembly setupGame = gameProgram.SetupSpecificConfiguration(config).WithDeployables(new DotNetAssembly(
                                                                                                          Il2Cpp.Distribution.Path.Combine("build/profiles/Tiny/Facades/netstandard.dll"),
                                                                                                          Framework.FrameworkNone));

            var postILProcessedGame = ILPostProcessorTool.SetupInvocation(setupGame, config, gameProgram.Defines.For(config).ToArray());
            var postTypeRegGenGame  = TypeRegistrationTool.SetupInvocation(postILProcessedGame, config);
            configToSetupGame[config] = postTypeRegGenGame;
        }
        ;

        var il2CppOutputProgram = new Il2Cpp.Il2CppOutputProgram(gameProgram.AsmDefDescription.Name.Replace(".", "-") + ".il2cpp");

        var configToSetupGameBursted = new Dictionary <DotsRuntimeCSharpProgramConfiguration, DotNetAssembly>();

        BurstCompiler hostBurstCompiler = null;

        if (HostPlatform.IsWindows)
        {
            hostBurstCompiler      = new BurstCompilerForWindows64();
            hostBurstCompiler.Link = true;
            hostBurstCompiler.OnlyStaticMethods = true;
        }
        else if (HostPlatform.IsOSX)
        {
            hostBurstCompiler      = new BurstCompilerForMac();
            hostBurstCompiler.Link = true;
            hostBurstCompiler.OnlyStaticMethods = true;
        }
        else
        {
            Console.WriteLine("Tiny burst not yet supported on linux.");
        }
        var webBurstCompiler = new BurstCompilerForEmscripten();

        foreach (var kvp in configToSetupGame)
        {
            var config    = kvp.Key;
            var setupGame = kvp.Value;

            if (config.UseBurst)
            {
                var outputDir = $"artifacts/{game.Name}/{config.Identifier}_bursted";
                var isWebGL   = config.Platform is WebGLPlatform;
                var extension = config.NativeProgramConfiguration.ToolChain.DynamicLibraryFormat.Extension;

                //burst generates a .bundle on os x.
                if (config.Platform is MacOSXPlatform)
                {
                    extension = "bundle";
                }

                var burstlib = BurstCompiler.SetupBurstCompilationAndLinkForAssemblies(
                    isWebGL
                        ? webBurstCompiler
                        : hostBurstCompiler,
                    setupGame,
                    new NPath(outputDir).Combine(
                        $"lib_burst_generated{("."+extension) ?? ""}"),
                    outputDir,
                    out var burstedGame);

                if (isWebGL)
                {
                    il2CppOutputProgram.Libraries.Add(c => c.Equals(config.NativeProgramConfiguration), burstlib);
                }
                else
                {
                    burstedGame = burstedGame.WithDeployables(burstlib);
                }
                configToSetupGameBursted[config] = burstedGame;
            }
            else
            {
                configToSetupGameBursted[config] = setupGame;
            }
        }

        var configToSetupGameStripped = new Dictionary <DotsRuntimeCSharpProgramConfiguration, DotNetAssembly>();

        foreach (var kvp in configToSetupGameBursted)
        {
            var config    = kvp.Key;
            var setupGame = kvp.Value;

            if (config.ScriptingBackend == ScriptingBackend.TinyIl2cpp)
            {
                setupGame = Il2Cpp.UnityLinker.SetupInvocation(setupGame, $"artifacts/{game.Name}/{config.Identifier}_stripped", config.NativeProgramConfiguration);
                il2CppOutputProgram.SetupConditionalSourcesAndLibrariesForConfig(config, setupGame);
                configToSetupGameStripped[kvp.Key] = setupGame;
            }
            else
            {
                configToSetupGameStripped[kvp.Key] = kvp.Value;
            }
        }

        foreach (var kvp in configToSetupGameStripped)
        {
            var   config     = kvp.Key;
            var   setupGame  = kvp.Value;
            NPath deployPath = GameDeployDirectoryFor(gameProgram, config);

            IDeployable deployedGame;
            NPath       entryPointExecutable = null;

            if (config.ScriptingBackend == ScriptingBackend.TinyIl2cpp)
            {
                var builtNativeProgram = il2CppOutputProgram.SetupSpecificConfiguration(
                    config.NativeProgramConfiguration,
                    config.NativeProgramConfiguration.ExecutableFormat
                    )
                                         .WithDeployables(setupGame.RecursiveRuntimeDependenciesIncludingSelf.SelectMany(a => a.Deployables.Where(d => !(d is DotNetAssembly) && !(d is StaticLibrary)))
                                                          .ToArray());

                if (builtNativeProgram is IPackagedAppExtension)
                {
                    (builtNativeProgram as IPackagedAppExtension).SetAppPackagingParameters(gameProgram.AsmDefDescription.Name, config.NativeProgramConfiguration.CodeGen, gameProgram.SupportFiles.For(config));
                }
                deployedGame         = builtNativeProgram.DeployTo(deployPath);
                entryPointExecutable = deployedGame.Path;
                if (config.EnableManagedDebugging)
                {
                    Backend.Current.AddDependency(deployedGame.Path, Il2Cpp.CopyIL2CPPMetadataFile(deployPath, setupGame));
                }
            }
            else
            {
                deployedGame = setupGame.DeployTo(deployPath);

                var dotNetAssembly = (DotNetAssembly)deployedGame;

                //Usually a dotnet runtime game does not have a static void main(), and instead references another "entrypoint asmdef" that provides it.
                //This is convenient, but what makes it weird is that you have to start YourEntryPoint.exe  instead of YourGame.exe.   Until we have a better
                //solution for this, we're going to copy YourEntryPoint.exe to YourGame.exe, so that it's easier to find, and so that when it runs and you look
                //at the process name you understand what it is.
                if (deployedGame.Path.HasExtension("dll"))
                {
                    var to   = deployPath.Combine(deployedGame.Path.ChangeExtension("exe").FileName);
                    var from = dotNetAssembly.RecursiveRuntimeDependenciesIncludingSelf.SingleOrDefault(a => a.Path.HasExtension("exe"))?.Path;
                    if (from == null)
                    {
                        throw new InvalidProgramException($"Program {dotNetAssembly.Path} is an executable-like thing, but doesn't reference anything with Main");
                    }
                    Backend.Current.AddDependency(deployedGame.Path, CopyTool.Instance().Setup(to, from));
                    entryPointExecutable = to;
                }
                else
                {
                    entryPointExecutable = deployedGame.Path;
                }
            }

            //Because we use multidag, and try to not run all the setupcode when we just want to create projectfiles, we have a bit of a challenge.
            //Projectfiles require exact start and build commands. So we need to have a cheap way to calculate those. However, it's important that they
            //exactly match the actual place where the buildprogram is going to place our files. If these don't match things break down. The checks
            //in this block, they compare the "quick way to determine where the binary will be placed, and what the start executable is",  with the
            //actual return values returned from .DeployTo(), when we do run the actual buildcode.
            NPath deployedGamePath = GameDeployBinaryFor(gameProgram, config);
            if (deployedGame.Path != deployedGamePath)
            {
                throw new InvalidProgramException($"We expected deployPath to be {deployedGamePath}, but in reality it was {deployedGame.Path}");
            }
            var expectedEntryPointExecutable = EntryPointExecutableFor(gameProgram, config);
            if (entryPointExecutable != expectedEntryPointExecutable)
            {
                throw new InvalidProgramException($"We expected entryPointExecutable to be {expectedEntryPointExecutable}, but in reality it was {entryPointExecutable}");
            }

            Backend.Current.AddAliasDependency(config.Identifier, deployedGamePath);
        }

        return(gameProgram);
    }