Ejemplo n.º 1
0
    static void Main()
    {
        List <BuildCommand> commands = HostPlatform.IsOSX ? MacCommands() : WindowsCommands();

        commands.AddRange(GetAndroidBuildCommands());

        NativeProgram plugin = new NativeProgram("traceeventprofiler")
        {
            Sources = { "src" }
        };

        plugin.CompilerSettingsForIosOrTvos().Add(s => s.WithEmbedBitcode(true));
        plugin.DynamicLinkerSettingsForWindows().Add(s => s.WithDefinitionFile("src/ProfilerPlugin.def"));

        plugin.OutputName.Set(c => DllNameForPlatform(c.ToolChain.Platform));

        foreach (var command in commands)
        {
            var toolchain = command.ToolChain;

            var config       = new NativeProgramConfiguration(CodeGen.Release, toolchain, false);
            var tempDir      = $"artifacts_preprocess/{toolchain.LegacyPlatformIdentifier}";
            var builtProgram = plugin.SetupSpecificConfiguration(config, toolchain.DynamicLibraryFormat).DeployTo(tempDir).Path;
            if (command.PostProcess != null)
            {
                builtProgram = command.PostProcess(builtProgram, toolchain, command.PluginSubFolder);
            }
            Backend.Current.AddAliasDependency(command.Alias, builtProgram);
        }
    }
Ejemplo n.º 2
0
    private static void SetupCppHelloWorld()
    {
        //setup a platform agnostic native program description
        var np = new NativeProgram("helloworld");

        //add all native sourcefiles in a directory called helloworld_cpp
        np.Sources.Add("hello.c");

        //maybe add a define?
        np.Defines.Add("VERY_IMPORTANT=1");

        //now that we have a nativeprogram, we need to build in into a specific "shape". We need to specify an exact toolchain, architecture,
        //"format", codegen and target directory.  Let's start:

        //let's pick whatever is a good toolchain for the computer we're running on
        var toolchain = ToolChain.Store.Host();

        //Lets create a configuration for this nativeprogram that builds with Debug Codegen, the toolchain we just created, and uses lumping.
        var nativeProgramConfiguration = new NativeProgramConfiguration(CodeGen.Debug, toolchain, lump: true);

        //we want to build the nativeprogram into an executable. each toolchain can provide different ways of "linking" (static, dynamic, executable are default ones)
        var format = toolchain.ExecutableFormat;

        //and here the magic happens, the nativeprogram gets setup for the specific configuration we just made.
        np.SetupSpecificConfiguration(nativeProgramConfiguration, format).DeployTo("build");
    }
Ejemplo n.º 3
0
    protected virtual DotNetAssembly SetupNativeProgram(CSharpProgramConfiguration config, DotNetAssembly result)
    {
        var dotsConfig = (DotsRuntimeCSharpProgramConfiguration)config;

        var npc = dotsConfig.NativeProgramConfiguration;

        if (NativeProgram != null && NativeProgram.Sources.ForAny().Any())
        {
            BuiltNativeProgram setupSpecificConfiguration = NativeProgram.SetupSpecificConfiguration(npc,
                                                                                                     npc.ToolChain.DynamicLibraryFormat ?? npc.ToolChain.StaticLibraryFormat);
            result = result.WithDeployables(setupSpecificConfiguration);
        }

        return(result);
    }
Ejemplo n.º 4
0
    private static void ProcessProgram(NativeProgram plugin, string targetDir, List <BuildCommand> commands)
    {
        foreach (var command in commands)
        {
            var toolchain = command.ToolChain;

            var config       = new NativeProgramConfiguration(CodeGen.Release, toolchain, false);
            var builtProgram = plugin.SetupSpecificConfiguration(config, toolchain.DynamicLibraryFormat);
            var artefact     = builtProgram.Path;
            if (command.PostProcess != null)
            {
                artefact = command.PostProcess(artefact, toolchain, targetDir, command.PluginSubFolder);
            }
            Backend.Current.AddAliasDependency(command.Alias, artefact);
        }
    }
Ejemplo n.º 5
0
    private static void BuildStarterKitPlugin()
    {
        var np = new NativeProgram("MARSXRSubsystem");

        np.Sources.Add("Source");
        np.IncludeDirectories.Add("External/Unity");

        var toolchains = new ToolChain[]
        {
            new WindowsToolchain(new Lazy <WindowsSdk>(() => WindowsSdk.Locatorx64.UserDefaultOrLatest).Value),
            new MacToolchain(new Lazy <MacSdk>(() => MacSdk.Locatorx64.UserDefaultOrLatest).Value),
            //new AndroidNdkToolchain(new Lazy<AndroidNdk>(() => (AndroidNdk) ToolChain.Store.Android().r16b().Arm64().Sdk).Value),
            //new AndroidNdkToolchain(new Lazy<AndroidNdk>(() => (AndroidNdk) ToolChain.Store.Android().r16b().Armv7().Sdk).Value),
            //new IOSToolchain(new Lazy<IOSSdk>(() => IOSSdk.LocatorArm64.UserDefaultOrLatest).Value),
        };

        np.OutputName.Add(s => s.Platform is AndroidPlatform, $"lib{np.Name}");

        foreach (var toolchain in toolchains)
        {
            var name = toolchain.Platform.Name.ToLower();
            if (!toolchain.CanBuild)
            {
                Console.WriteLine($"Can't build {toolchain.ToString()}");
                continue;
            }

            var nativeProgramConfiguration = new NativeProgramConfiguration(CodeGen.Debug, toolchain, lump: false);

            //we want to build the nativeprogram into an executable. each toolchain can provide different ways of "linking" (static, dynamic, executable are default ones)
            var format = toolchain is IOSToolchain ? toolchain.StaticLibraryFormat : toolchain.DynamicLibraryFormat;

            //and here the magic happens, the nativeprogram gets setup for the specific configuration we just made.
            var folder       = name;
            var deployFolder = new NPath("build").Combine(folder).Combine(toolchain.Architecture.DisplayName);
            var output       = np.SetupSpecificConfiguration(nativeProgramConfiguration, format).DeployTo(deployFolder);

            var metaFolder       = new NPath("Meta/").Combine(folder).Combine(toolchain.Architecture.DisplayName);
            var metaFile         = metaFolder.Combine("plugin." + output.Path.Extension + ".meta");
            var deployedMetaFile = deployFolder.Combine(output.Path.FileName + ".meta");
            CopyMetaFileWithNewGUID(metaFile, deployedMetaFile);

            var alias = name;
            Backend.Current.AddAliasDependency(alias, output.Path);
            Backend.Current.AddAliasDependency(alias, deployedMetaFile);
        }
    }
Ejemplo n.º 6
0
    //Run ./bee build-lib-webp to compile the webp library located in Unity.Tiny.Image2D.Native.cpp~/libwebp and copy it to Unity.Tiny.Image2D.Authoring/libwebp
    public override void Customize()
    {
        var           asmdefRoot      = AsmDefConfigFile.AsmDefDescriptionFor("Unity.Tiny.Image2D.Native").Directory;
        var           outputDirectory = AsmDefConfigFile.AsmDefDescriptionFor("Unity.Tiny.Image2D.Authoring").Directory;
        NativeProgram WebPLib         = new NativeProgram("libwebp")
        {
            Sources =
            {
                asmdefRoot.Combine("cpp~/libwebp"),
            },
            IncludeDirectories = { asmdefRoot.Combine("cpp~/libwebp") },
            OutputDirectory    = { outputDirectory.Combine("libwebp") }
        };

        DotsRuntimeNativeProgramConfiguration config = DotsConfigs.HostDotnet.NativeProgramConfiguration;
        var builtWebPLib = WebPLib.SetupSpecificConfiguration(config, config.ToolChain.DynamicLibraryFormat);

        Backend.Current.AddAliasDependency("build-lib-webp", builtWebPLib.Path);
    }
Ejemplo n.º 7
0
    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);
    }