static AsmDefConfigFile() { Json = JObject.Parse(new NPath("asmdefs.json").MakeAbsolute().ReadAllText()); UnityProjectPath = Json["UnityProjectPath"].Value <string>(); ProjectName = Json["ProjectName"].Value <string>(); UnityCompilationPipelineAssemblyPath = Json["CompilationPipelineAssemblyPath"].Value <string>(); BuildSettingsFileVersion = Json["BuildSettingsFileVersion"].Value <int>(); // asmrefs have to be created first, because they're used during construction of the asmdef description foreach (var asmref in Json["asmrefs"].Values <JObject>()) { var path = asmref["FullPath"].Value <string>().ToNPath(); var desc = new AsmRefDescription(path, asmref["PackageSource"].Value <string>()); _pathsToAsmRefDescription[path] = desc; } // then the Guid mapping has to be set up foreach (var asmdef in Json["asmdefs"].Values <JObject>()) { var name = asmdef["AsmdefName"].Value <string>(); GuidsToAsmDefNames[asmdef["Guid"].Value <string>()] = name; } // finally we can create the AsmDefDescriptions foreach (var asmdef in Json["asmdefs"].Values <JObject>()) { var name = asmdef["AsmdefName"].Value <string>(); var desc = new AsmDefDescription(asmdef["FullPath"].Value <string>(), asmdef["PackageSource"].Value <string>()); _namesToAsmDefDescription[name] = desc; } }
private static DotsRuntimeCSharpProgram SetupTest(AsmDefDescription test) { var testProgram = GetOrMakeDotsRuntimeCSharpProgramFor(test); if (!IsTestProgramDotsRuntimeCompatible(testProgram)) { return(null); } var config = DotsConfigs.HostDotnet; var builtTest = testProgram.SetupSpecificConfiguration(config); builtTest = TypeRegistrationTool.SetupInvocation(builtTest, config); NPath deployDirectory = $"build/{test.Name}/{test.Name}-{config.Identifier}"; var deployed = builtTest.DeployTo(deployDirectory); testProgram.ProjectFile.OutputPath.Add(c => c == config, deployDirectory); testProgram.ProjectFile.BuildCommand.Add(c => c == config, new BeeBuildCommand(deployed.Path.ToString(), false, false).ToExecuteArgs()); Backend.Current.AddAliasDependency(test.Name.Replace(".", "-").ToLower(), deployed.Path); Backend.Current.AddAliasDependency("tests", deployed.Path); return(testProgram); }
public AsmDefBasedDotsRuntimeCSharpProgram(AsmDefDescription asmDefDescription) : base(asmDefDescription.Directory, deferConstruction: true ) { AsmDefDescription = asmDefDescription; ReferencedPrograms = AsmDefDescription.References.Select(BuildProgram.GetOrMakeDotsRuntimeCSharpProgramFor).ToArray(); var referencesEntryPoint = ReferencedPrograms.Any(r => r.FileName.EndsWith(".exe")); var isExe = asmDefDescription.DefineConstraints.Contains("UNITY_DOTS_ENTRYPOINT") || (asmDefDescription.Path.Parent.Files("*.project").Any() && !referencesEntryPoint) || asmDefDescription.OptionalUnityReferences.Contains("TestAssemblies"); Construct(asmDefDescription.Name, isExe); ProjectFile.AdditionalFiles.Add(asmDefDescription.Path); IncludePlatforms = AsmDefDescription.IncludePlatforms; ExcludePlatforms = AsmDefDescription.ExcludePlatforms; Unsafe = AsmDefDescription.Unsafe; References.Add(config => { if (config is DotsRuntimeCSharpProgramConfiguration dotsConfig) { return(ReferencedPrograms.Where(rp => rp.IsSupportedOn(dotsConfig.NativeProgramConfiguration.ToolChain.Platform))); } //this codepath will be hit for the bindgem invocation return(ReferencedPrograms); }); if (BuildProgram.ZeroJobs != null) { References.Add(BuildProgram.ZeroJobs); } if (BuildProgram.UnityLowLevel != null) { References.Add(BuildProgram.UnityLowLevel); } if (IsTestAssembly) { References.Add(BuildProgram.NUnitFramework); var nunitLiteMain = BuildProgram.BeeRoot.Combine("CSharpSupport/NUnitLiteMain.cs"); Sources.Add(nunitLiteMain); ProjectFile.AddCustomLinkRoot(nunitLiteMain.Parent, "TestRunner"); References.Add(BuildProgram.NUnitLite); References.Add(BuildProgram.GetOrMakeDotsRuntimeCSharpProgramFor(BuildProgramConfigFile.AsmDefDescriptionFor("Unity.Entities"))); } BindGem.ConfigureNativeProgramFor(this); }
private static NPath GetBurstExecutablePath(AsmDefDescription burstAsmDef) { var burstDebugVariable = Environment.GetEnvironmentVariable("UNITY_BURST_RUNTIME_PATH"); if (!string.IsNullOrEmpty(burstDebugVariable)) { var bclPath = burstDebugVariable.ToNPath().Combine("bcl.exe"); if (bclPath.FileExists()) { return(bclPath); } } return(burstAsmDef.Path.Parent.Parent.Combine(".Runtime/bcl.exe")); }
private static DotsRuntimeCSharpProgram SetupTest(AsmDefDescription test) { var testProgram = GetOrMakeDotsRuntimeCSharpProgramFor(test); if (!IsTestProgramDotsRuntimeCompatible(testProgram)) { return(null); } var name = test.Name; SetupTestForConfig(name, testProgram, DotsConfigs.HostDotnet); // Turn back on when multi-threaded working: https://unity3d.atlassian.net/browse/DOTSR-75 //SetupTestForConfig(name, testProgram, DotsConfigs.MultithreadedJobsTestConfig); return(testProgram); }
public static AsmDefDescription AsmDefDescriptionFor(string asmdefname) { if (_namesToAsmDefDescription.TryGetValue(asmdefname, out var result)) { return(result); } var jobject = Json["asmdefs"].Values <JObject>().FirstOrDefault(o => o["AsmdefName"].Value <string>() == asmdefname); if (jobject == null) { return(null); } result = new AsmDefDescription(jobject["FullPath"].Value <string>(), jobject["PackageSource"].Value <string>()); _namesToAsmDefDescription[asmdefname] = result; return(result); }
public static AsmDefCSharpProgram GetOrMakeDotsRuntimeCSharpProgramFor( AsmDefDescription asmDefDescription) { return(_cache.GetOrMake(asmDefDescription, () => { if (BuildStack.Contains(asmDefDescription)) { Console.WriteLine($"Fatal Error: recursive asmdef or build program dependency detected!"); foreach (var bs in BuildStack) { Console.WriteLine($" {bs.Name}"); } Console.WriteLine($"-> {asmDefDescription.Name}"); throw new InvalidProgramException("Recursive asmdef dependencies"); } BuildStack.Add(asmDefDescription); var prog = new AsmDefCSharpProgram(asmDefDescription); BuildStack.RemoveAt(BuildStack.Count - 1); return prog; })); }
public static void InjectAsmDef(NPath path, string packageSource = "BuiltIn") { var asmdef = new AsmDefDescription(path, packageSource); _namesToAsmDefDescription[asmdef.Name] = asmdef; }
public virtual DotsRuntimeCSharpProgram TryCreateProgramForAsmDef(AsmDefDescription asmDef) { return(null); }
public static DotsRuntimeCSharpProgram RunAllCustomizersTryCreateProgramForAsmDef(AsmDefDescription asmDef) { DotsRuntimeCSharpProgram result = null; Type resultCustomizer = null; foreach (var customizer in All) { var r = customizer.TryCreateProgramForAsmDef(asmDef); if (r == null) { continue; } if (result != null) { throw new InvalidOperationException( $"Both {customizer.GetType()} and {resultCustomizer} created a custom CSharpProgram for {asmDef.Name}"); } result = r; resultCustomizer = customizer.GetType(); } return(result); }
public static AsmDefCSharpProgram GetOrMakeDotsRuntimeCSharpProgramFor(AsmDefDescription asmDefDescription) => _cache.GetOrMake(asmDefDescription, () => new AsmDefCSharpProgram(asmDefDescription));
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); }
public AsmDefCSharpProgram(AsmDefDescription asmDefDescription) : base(asmDefDescription.Directory, asmDefDescription.IncludedAsmRefs.Select(asmref => asmref.Path.Parent), deferConstruction: true) { AsmDefDescription = asmDefDescription; var asmDefReferences = AsmDefDescription.References.Select(asmDefDescription1 => BuildProgram.GetOrMakeDotsRuntimeCSharpProgramFor(asmDefDescription1)).ToList(); var isExe = asmDefDescription.DefineConstraints.Contains("UNITY_DOTS_ENTRYPOINT") || asmDefDescription.Name.EndsWith(".Tests"); Construct(asmDefDescription.Name, isExe); ProjectFile.AdditionalFiles.Add(asmDefDescription.Path); IncludePlatforms = AsmDefDescription.IncludePlatforms; ExcludePlatforms = AsmDefDescription.ExcludePlatforms; Unsafe = AsmDefDescription.AllowUnsafeCode; References.Add(config => { if (config is DotsRuntimeCSharpProgramConfiguration dotsConfig) { if (dotsConfig.TargetFramework == TargetFramework.Tiny) { return(asmDefReferences.Where(rp => rp.IsSupportedFor(dotsConfig) && !IncompatibleTinyBCLAsmDefs.Contains(rp.FileName))); } else { return(asmDefReferences.Where(rp => rp.IsSupportedFor(dotsConfig))); } } //this codepath will be hit for the bindgem invocation return(asmDefReferences); }); if (AsmDefDescription.IsTinyRoot || isExe) { AsmDefCSharpProgramCustomizer.RunAllAddPlatformImplementationReferences(this); } if (BuildProgram.UnityTinyBurst != null) { References.Add(BuildProgram.UnityTinyBurst); } if (BuildProgram.ZeroJobs != null) { References.Add(BuildProgram.ZeroJobs); } if (BuildProgram.UnityLowLevel != null) { References.Add(BuildProgram.UnityLowLevel); } if (BuildProgram.TinyIO != null) { References.Add(BuildProgram.TinyIO); } // Add in any precompiled references found in the asmdef directory or sub-directory foreach (var pcr in asmDefDescription.PrecompiledReferences) { var files = asmDefDescription.Path.Parent.Files(pcr, true); if (files.Any()) { References.Add(files); } } if (IsTestAssembly) { var nunitLiteMain = BuildProgram.BeeRoot.Combine("CSharpSupport/NUnitLiteMain.cs"); Sources.Add(nunitLiteMain); // Setup for IL2CPP var tinyTestFramework = BuildProgram.BeeRoot.Parent.Combine("TinyTestFramework"); Sources.Add(c => ((DotsRuntimeCSharpProgramConfiguration)c).ScriptingBackend == ScriptingBackend.TinyIl2cpp || ((DotsRuntimeCSharpProgramConfiguration)c).TargetFramework == TargetFramework.Tiny, tinyTestFramework); Defines.Add(c => ((DotsRuntimeCSharpProgramConfiguration)c).ScriptingBackend == ScriptingBackend.TinyIl2cpp || ((DotsRuntimeCSharpProgramConfiguration)c).TargetFramework == TargetFramework.Tiny, "UNITY_PORTABLE_TEST_RUNNER"); // Setup for dotnet References.Add(c => ((DotsRuntimeCSharpProgramConfiguration)c).ScriptingBackend == ScriptingBackend.Dotnet && ((DotsRuntimeCSharpProgramConfiguration)c).TargetFramework != TargetFramework.Tiny, BuildProgram.NUnitFramework); ProjectFile.AddCustomLinkRoot(nunitLiteMain.Parent, "TestRunner"); References.Add(c => ((DotsRuntimeCSharpProgramConfiguration)c).ScriptingBackend == ScriptingBackend.Dotnet && ((DotsRuntimeCSharpProgramConfiguration)c).TargetFramework != TargetFramework.Tiny, BuildProgram.NUnitLite); // General setup References.Add(BuildProgram.GetOrMakeDotsRuntimeCSharpProgramFor(AsmDefConfigFile.AsmDefDescriptionFor("Unity.Entities"))); References.Add(BuildProgram.GetOrMakeDotsRuntimeCSharpProgramFor(AsmDefConfigFile.AsmDefDescriptionFor("Unity.Tiny.Core"))); References.Add(BuildProgram.GetOrMakeDotsRuntimeCSharpProgramFor(AsmDefConfigFile.AsmDefDescriptionFor("Unity.Tiny.UnityInstance"))); References.Add(BuildProgram.GetOrMakeDotsRuntimeCSharpProgramFor(AsmDefConfigFile.AsmDefDescriptionFor("Unity.Collections"))); } else if (IsILPostProcessorAssembly) { References.Add(BuildProgram.UnityCompilationPipeline); References.Add(MonoCecil.Paths); References.Add(Il2Cpp.Distribution.Path.Combine("build/deploy/net471/Unity.Cecil.Awesome.dll")); } }
private static DotsRuntimeCSharpProgram SetupGame(AsmDefDescription game) { var gameProgram = GetOrMakeDotsRuntimeCSharpProgramFor(game); var configToSetupGame = new Dictionary <DotsRuntimeCSharpProgramConfiguration, DotNetAssembly>(); if (!PerConfigBuildSettings.ContainsKey(game.Name)) { return(null); } var configsToUse = PerConfigBuildSettings[game.Name].Where(config => !CanSkipSetupOf(game.Name, config)); foreach (var config in configsToUse) { var withoutExt = new NPath(new NPath(gameProgram.FileName).FileNameWithoutExtension).Combine(config.Identifier); NPath exportManifest = withoutExt.Combine("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( c => c.Equals(config), new DeployableFile(dataFile, GetDeployPathFromExportPath(dataFile))); } } 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()); } foreach (var config in configsToUse) { DotNetAssembly setupGame = gameProgram.SetupSpecificConfiguration(config); if (config.TargetFramework == TargetFramework.Tiny) { var tinyStandard = new DotNetAssembly(Il2Cpp.Distribution.Path.Combine("build/profiles/Tiny/Facades/netstandard.dll"), Framework.FrameworkNone); setupGame = setupGame.WithDeployables(tinyStandard); } 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); var configToSetupGameBursted = new Dictionary <DotsRuntimeCSharpProgramConfiguration, DotNetAssembly>(); foreach (var kvp in configToSetupGame) { var config = kvp.Key; var setupGame = kvp.Value; if (config.UseBurst) { BurstCompiler burstCompiler = null; if (config.Platform is WindowsPlatform) { burstCompiler = new BurstCompilerForWindows64(); burstCompiler.Link = false; } else if (config.Platform is MacOSXPlatform) { burstCompiler = new BurstCompilerForMac(); burstCompiler.Link = false; } else if (config.Platform is IosPlatform) { burstCompiler = new BurstCompilerForiOS(); burstCompiler.EnableStaticLinkage = true; burstCompiler.ObjectFileExtension = "a"; } else if (config.Platform is LinuxPlatform) { burstCompiler = new BurstCompilerForLinuxWaitingForBurstRelease(); } else if (config.Platform is AndroidPlatform) { burstCompiler = new BurstCompilerForAndroid(); burstCompiler.EnableStaticLinkage = false; burstCompiler.Link = false; burstCompiler.EnableDirectExternalLinking = true; if (config.NativeProgramConfiguration.ToolChain.Architecture is Arm64Architecture) { burstCompiler.TargetArchitecture = "ARMV8A_AARCH64"; } } else if (config.Platform is WebGLPlatform) { burstCompiler = new BurstCompilerForEmscripten(); burstCompiler.EnableStaticLinkage = true; burstCompiler.DisableVectors = false; } // Only generate marshaling info for platforms that require marshalling (e.g. Windows DotNet) // but also if collection checks are enabled (as that is why we need marshalling) burstCompiler.EnableJobMarshalling &= config.EnableUnityCollectionsChecks; burstCompiler.SafetyChecks = config.EnableUnityCollectionsChecks; burstCompiler.DisableWarnings = "BC1370"; // Suppress warning for burst function throwing an exception var outputDir = $"artifacts/{game.Name}/{config.Identifier}_bursted"; var burstLibName = "lib_burst_generated"; DotNetAssembly burstedGame = setupGame; var burstlib = BurstCompiler.SetupBurstCompilationForAssemblies( burstCompiler, setupGame, new NPath(outputDir).Combine("bclobj"), outputDir, burstLibName, out burstedGame); if ((config.Platform is IosPlatform || config.Platform is AndroidPlatform) && config.NativeProgramConfiguration.ToolChain.DynamicLibraryFormat.Extension == "a") // static lib based toolchain { il2CppOutputProgram.Libraries.Add(c => c.Equals(config.NativeProgramConfiguration), burstlib); il2CppOutputProgram.Defines.Add( c => c.Equals(config.NativeProgramConfiguration), $"FORCE_PINVOKE_{burstLibName}_INTERNAL"); } else if (config.Platform is WebGLPlatform) { il2CppOutputProgram.Libraries.Add(c => c.Equals(config.NativeProgramConfiguration), burstlib); } else { var burstDynamicLib = new NativeProgram(burstLibName); burstDynamicLib.Libraries.Add(c => c.Equals(config.NativeProgramConfiguration), burstlib); burstDynamicLib.Libraries.Add( c => c.Equals(config.NativeProgramConfiguration), gameProgram.TransitiveReferencesFor(config) .Where( p => p is DotsRuntimeCSharpProgram && ((DotsRuntimeCSharpProgram)p).NativeProgram != null) .Select( p => new NativeProgramAsLibrary(((DotsRuntimeCSharpProgram)p).NativeProgram) { BuildMode = NativeProgramLibraryBuildMode.Dynamic })); if (config.Platform is IosPlatform || config.Platform is AndroidPlatform) { NativeJobsPrebuiltLibrary.AddToNativeProgram(burstDynamicLib); } DotsRuntimeCSharpProgram.SetupDotsRuntimeNativeProgram(burstLibName, burstDynamicLib); var builtBurstLib = burstDynamicLib.SetupSpecificConfiguration( config.NativeProgramConfiguration, config.NativeProgramConfiguration.ToolChain.DynamicLibraryFormat); burstedGame = burstedGame.WithDeployables(builtBurstLib); } 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 tinyShellFileName = "tiny_shell.html"; NPath tinyShellPath = new NPath(new NPath(gameProgram.FileName).FileNameWithoutExtension).Combine(config.Identifier, "WebTemplate", tinyShellFileName); il2CppOutputProgram.DynamicLinkerSettingsForEmscripten().Add(c => c.WithShellFile(tinyShellPath)); 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.DotsConfiguration); } if (config.PlatformBuildConfig is WebBuildConfig webBuildConfig) { if (webBuildConfig.SingleFile) { deployedGame = new DeployableFile(GameDeployBinaryFor(gameProgram, config)); CopyTool.Instance().Setup(deployedGame.Path, (builtNativeProgram as EmscriptenExecutable).Path); } else { deployedGame = builtNativeProgram.DeployTo(deployPath); } var webTemplateFolder = webBuildConfig.WebTemplateFolder; if (String.IsNullOrEmpty(webTemplateFolder)) { webTemplateFolder = LowLevelRoot.Combine("WebSupport", "WebTemplates", "Default").ToString(); } if (new NPath(webTemplateFolder).IsRelative) { webTemplateFolder = new NPath("../..").Combine(webTemplateFolder).MakeAbsolute().ToString(); } if (!new NPath(webTemplateFolder).Combine(tinyShellFileName).FileExists()) { throw new InvalidProgramException($"Web template folder \"{webTemplateFolder}\" doesn't contain \"{tinyShellFileName}\" file."); } foreach (var templateFilePath in new NPath(webTemplateFolder).Files(recurse:true)) { string fileRelativePath = templateFilePath.ToString().Substring(webTemplateFolder.Length + 1); if (fileRelativePath == tinyShellFileName) { NPath shellPackager = LowLevelRoot.Combine("WebSupport", "package_shell_file.js"); NPath tinyShellJS = LowLevelRoot.Combine("WebSupport", "tiny_shell.js"); var inputs = new List <NPath> { TinyEmscripten.NodeExe, shellPackager, templateFilePath, tinyShellJS }; var commandLineArguments = new List <string> { shellPackager.ToString(), "--outputHtml", tinyShellPath.ToString(), "--inputShellHtml", templateFilePath.ToString(), "--inputShellJs", tinyShellJS.ToString() }; NPath exportManifest = new NPath(new NPath(gameProgram.FileName).FileNameWithoutExtension).Combine(config.Identifier, "export.manifest"); if (webBuildConfig.SingleFile && exportManifest.FileExists()) { inputs.Add(exportManifest.MakeAbsolute().ReadAllLines().Select(d => new NPath(d))); NPath assetRootDirectory = new NPath(new NPath(gameProgram.FileName).FileNameWithoutExtension).Combine(config.Identifier); commandLineArguments.AddRange(new List <string> { "--assetRootDirectory", assetRootDirectory.ToString(), "--assetManifest", exportManifest.ToString() }); } Backend.Current.AddAction( actionName: "Package Shell File", targetFiles: new NPath[] { tinyShellPath }, inputs: inputs.ToArray(), executableStringFor: TinyEmscripten.NodeExe.InQuotes(), commandLineArguments: commandLineArguments.Select(d => d.InQuotes()).ToArray() ); Backend.Current.AddDependency(deployedGame.Path, tinyShellPath); } else if (!templateFilePath.HasExtension("meta")) { var targetPath = deployPath.Combine(fileRelativePath); CopyTool.Instance().Setup(targetPath, templateFilePath); Backend.Current.AddDependency(deployedGame.Path, targetPath); } } } else { deployedGame = builtNativeProgram.DeployTo(deployPath); } entryPointExecutable = deployedGame.Path; if (config.EnableManagedDebugging && !(builtNativeProgram is IPackagedAppExtension)) { Backend.Current.AddDependency(deployedGame.Path, Il2Cpp.CopyIL2CPPMetadataFile(deployPath, setupGame)); } // make sure http-server gets fetched from stevedore. this should probably go elsewhere, but this is // a convenient quick hack place. if (config.PlatformBuildConfig is WebBuildConfig) { var httpserver = new StevedoreArtifact("http-server"); httpserver.GenerateUnusualPath(); var httpserverpath = httpserver.GetUnusualPath().Combine("bin", "http-server"); Backend.Current.AddDependency(deployedGame.Path, httpserverpath); } } 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); // Do an explicit check for the entrypoint.exe as a program may refer to other exes as assembly references var from = dotNetAssembly.RecursiveRuntimeDependenciesIncludingSelf.SingleOrDefault(a => a.Path.FileName == "Unity.Runtime.EntryPoint.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); //Identifier with slash means that this is complementary target and we should skip steps which are main target specific. //See comment in DotsConfigs.cs DotsConfigs.MakeConfigs() method for details. if (config.Identifier.IndexOf('/') != -1) { continue; } 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); }
private static DotsRuntimeCSharpProgram SetupGame(AsmDefDescription game) { DotsRuntimeCSharpProgram 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 = DotsConfigs.Configs.ToDictionary(config => config, config => { DotNetAssembly setupGame = gameProgram.SetupSpecificConfiguration(config); return(TypeRegistrationTool.SetupInvocation(setupGame, config)); }); var il2CppOutputProgram = new Il2Cpp.Il2CppOutputProgram(gameProgram.FileName + "_il2cpp"); foreach (var kvp in configToSetupGame) { 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); } } foreach (var kvp in configToSetupGame) { var config = kvp.Key; var setupGame = kvp.Value; NPath deployPath = $"build/{game.Name}/{game.Name}-{config.Identifier}"; IDeployable deployedGame; 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()); deployedGame = builtNativeProgram.DeployTo(deployPath); } 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.Single(a => a.Path.HasExtension("exe")).Path; Backend.Current.AddDependency(deployedGame.Path, CopyTool.Instance().Setup(to, from)); } } NPath deployedGamePath = deployedGame.Path; gameProgram.ProjectFile.StartInfo.Add(c => c == config, StartInfoFor(config, deployedGame)); gameProgram.ProjectFile.BuildCommand.Add(c => c == config, new BeeBuildCommand(deployedGamePath.ToString(), false, false).ToExecuteArgs()); Backend.Current.AddAliasDependency($"{game.Name.ToLower()}-{config.Identifier}", deployedGamePath); Backend.Current.AddAliasDependency($"{game.Name.ToLower()}-all", deployedGamePath); } return(gameProgram); }
public AsmDefCSharpProgram(AsmDefDescription asmDefDescription) : base(asmDefDescription.Directory, asmDefDescription.IncludedAsmRefs.Select(asmref => asmref.Path.Parent), deferConstruction: true) { AsmDefDescription = asmDefDescription; var asmDefReferences = AsmDefDescription.References.Select(BuildProgram.GetOrMakeDotsRuntimeCSharpProgramFor).ToList(); ReferencedPrograms = asmDefReferences.Where(r => !IncompatibleDotRuntimeAsmDefs.Contains(r.AsmDefDescription.Name)).ToArray(); var isTinyRoot = AsmDefDescription.NamedReferences.Contains("Unity.Tiny.Main") || asmDefDescription.Path.Parent.Files("*.project").Any(); var isExe = asmDefDescription.DefineConstraints.Contains("UNITY_DOTS_ENTRYPOINT") || asmDefDescription.Name.EndsWith(".Tests"); Construct(asmDefDescription.Name, isExe); ProjectFile.AdditionalFiles.Add(asmDefDescription.Path); IncludePlatforms = AsmDefDescription.IncludePlatforms; ExcludePlatforms = AsmDefDescription.ExcludePlatforms; Unsafe = AsmDefDescription.Unsafe; References.Add(config => { if (config is DotsRuntimeCSharpProgramConfiguration dotsConfig) { return(ReferencedPrograms.Where(rp => rp.IsSupportedFor(dotsConfig))); } //this codepath will be hit for the bindgem invocation return(ReferencedPrograms); }); if (isTinyRoot || isExe) { AsmDefCSharpProgramCustomizer.RunAllAddPlatformImplementationReferences(this); } if (BuildProgram.ZeroJobs != null) { References.Add(BuildProgram.ZeroJobs); } if (BuildProgram.UnityLowLevel != null) { References.Add(BuildProgram.UnityLowLevel); } if (IsTestAssembly) { References.Add(BuildProgram.NUnitFramework); var nunitLiteMain = BuildProgram.BeeRoot.Combine("CSharpSupport/NUnitLiteMain.cs"); Sources.Add(nunitLiteMain); ProjectFile.AddCustomLinkRoot(nunitLiteMain.Parent, "TestRunner"); References.Add(BuildProgram.NUnitLite); References.Add(BuildProgram.GetOrMakeDotsRuntimeCSharpProgramFor(AsmDefConfigFile.AsmDefDescriptionFor("Unity.Entities"))); } else if (IsILPostProcessorAssembly) { References.Add(BuildProgram.UnityCompilationPipeline); References.Add(StevedoreUnityCecil.Paths); } }