// Copyright (c) 2012-2023 Flax Engine. All rights reserved. #define USE_STD using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Text; using Flax.Build; using Flax.Build.Platforms; namespace Flax.Deploy { partial class Deployment { private static void CodeSign(string file) { if (string.IsNullOrEmpty(Configuration.DeployCert)) return; Log.Info("Code signing file: " + file); switch (Platform.BuildTargetPlatform) { case TargetPlatform.Windows: VCEnvironment.CodeSign(file, Configuration.DeployCert, Configuration.DeployCertPass); break; case TargetPlatform.Mac: MacPlatform.CodeSign(file, Configuration.DeployCert); break; } } public class Editor { private static string RootPath; private static string OutputPath; public static void Package() { // Prepare RootPath = Globals.EngineRoot; OutputPath = Path.Combine(Deployer.PackageOutputPath, "Editor"); Utilities.DirectoryDelete(OutputPath); Directory.CreateDirectory(OutputPath); Log.Info(string.Empty); Log.Info("Deploy editor files"); Log.Info(string.Empty); // Deploy binaries foreach (var configuration in Deployer.Configurations) DeployEditorBinaries(configuration); { var binariesSubDir = "Binaries/Tools"; var src = Path.Combine(RootPath, binariesSubDir); var dst = Path.Combine(OutputPath, binariesSubDir); var buildToolExe = Platform.BuildTargetPlatform == TargetPlatform.Windows ? "Flax.Build.exe" : "Flax.Build"; DeployFile(src, dst, buildToolExe); CodeSign(Path.Combine(dst, buildToolExe)); var buildToolDll = "Flax.Build.dll"; DeployFile(src, dst,buildToolDll); CodeSign(Path.Combine(dst, buildToolDll)); DeployFile(src, dst, "Flax.Build.xml", true); DeployFile(src, dst, "Flax.Build.pdb"); DeployFile(src, dst, "Flax.Build.deps.json"); DeployFile(src, dst, "Flax.Build.runtimeconfig.json"); DeployFile(src, dst, "Ionic.Zip.Reduced.dll"); DeployFile(src, dst, "Mono.Cecil.dll"); DeployFile(src, dst, "Microsoft.CodeAnalysis.dll"); DeployFile(src, dst, "Microsoft.CodeAnalysis.CSharp.dll"); DeployFile(src, dst, "Microsoft.VisualStudio.Setup.Configuration.Interop.dll"); } // Deploy content DeployFolder(RootPath, OutputPath, "Content"); // Deploy DotNet deps { var subDir = "Source/Platforms/DotNet"; DeployFile(RootPath, OutputPath, subDir, "Newtonsoft.Json.dll"); DeployFile(RootPath, OutputPath, subDir, "Newtonsoft.Json.xml"); DeployFile(RootPath, OutputPath, Path.Combine(subDir, "AOT"), "Newtonsoft.Json.dll"); } // Deploy sources { // Modules public files var rules = Builder.GenerateRulesAssembly(); var files = new List(); foreach (var module in rules.Modules) { if (!module.Deploy) continue; module.GetFilesToDeploy(files); files.Add(module.FilePath); foreach (var file in files) { var src = Path.GetDirectoryName(file); var dst = Path.Combine(OutputPath, Utilities.MakePathRelativeTo(src, RootPath)); var filename = Path.GetFileName(file); DeployFile(src, dst, filename); } files.Clear(); } // Shader includes var shaders = Directory.GetFiles(Path.Combine(RootPath, "Source/Shaders"), "*.hlsl", SearchOption.AllDirectories); foreach (var shader in shaders) { var localPath = Utilities.MakePathRelativeTo(shader, RootPath); DeployFile(shader, Path.Combine(OutputPath, localPath)); } // Custom engine files DeployFile(RootPath, OutputPath, "Source/ThirdParty", "concurrentqueue.h"); DeployFile(RootPath, OutputPath, "Source", ".editorconfig"); DeployFile(RootPath, OutputPath, "Source", "flax.natvis"); DeployFile(RootPath, OutputPath, "Source", "FlaxEditor.Build.cs"); DeployFile(RootPath, OutputPath, "Source", "FlaxEngine.Gen.h"); DeployFile(RootPath, OutputPath, "Source", "FlaxGame.Build.cs"); // Mark deployed sources as already prebuilt Utilities.ReplaceInFile(Path.Combine(OutputPath, "Source/FlaxEditor.Build.cs"), "IsPreBuilt = false;", "IsPreBuilt = true;"); Utilities.ReplaceInFile(Path.Combine(OutputPath, "Source/FlaxGame.Build.cs"), "IsPreBuilt = false;", "IsPreBuilt = true;"); } // Deploy project DeployFile(RootPath, OutputPath, "Flax.flaxproj"); // Package Editor into macOS app if (Platform.BuildTargetPlatform == TargetPlatform.Mac) { var arch = Platform.BuildTargetArchitecture; Log.Info(string.Empty); Log.Info("Creating macOS app..."); var appPath = Path.Combine(Deployer.PackageOutputPath, "FlaxEditor.app"); var appContentsPath = Path.Combine(appPath, "Contents"); var appBinariesPath = Path.Combine(appContentsPath, "MacOS"); Utilities.DirectoryDelete(appPath); Directory.CreateDirectory(appPath); Directory.CreateDirectory(appContentsPath); Directory.CreateDirectory(appBinariesPath); // Copy icons set Directory.CreateDirectory(Path.Combine(appContentsPath, "Resources")); Utilities.FileCopy(Path.Combine(Globals.EngineRoot, "Source/Platforms/Mac/Default.icns"), Path.Combine(appContentsPath, "Resources/icon.icns")); // Create PkgInfo file File.WriteAllText(Path.Combine(appContentsPath, "PkgInfo"), "APPL???", Encoding.ASCII); // Create Info.plist var plist = File.ReadAllText(Path.Combine(Globals.EngineRoot, "Source/Platforms/Mac/Default.plist")); var flaxProject = EngineTarget.EngineProject; plist = plist.Replace("{Version}", flaxProject.Version.ToString()); plist = plist.Replace("{Copyright}", flaxProject.Copyright); plist = plist.Replace("{Executable}", "FlaxEditor"); plist = plist.Replace("{Arch}", arch == TargetArchitecture.ARM64 ? "arm64" : "x86_64"); File.WriteAllText(Path.Combine(appContentsPath, "Info.plist"), plist, Encoding.ASCII); // Copy output editor files Utilities.DirectoryCopy(OutputPath, appContentsPath); // Copy native binaries for app execution var defaultEditorConfig = "Development"; var ediotrBinariesPath = Path.Combine(appContentsPath, "Binaries/Editor/Mac", defaultEditorConfig); Utilities.DirectoryCopy(ediotrBinariesPath, appBinariesPath, true, true); } // Compress if (Configuration.DontCompress) return; Log.Info(string.Empty); Log.Info("Compressing editor files..."); string editorPackageZipPath; var unix = Platform.BuildTargetPlatform == TargetPlatform.Linux || Platform.BuildTargetPlatform == TargetPlatform.Mac; if (unix) { var zipEofPath = UnixPlatform.Which("zip"); unix = File.Exists(zipEofPath); if (!unix) Log.Verbose("Using .NET compressing"); } if (unix) { // Use system tool (preserves executable file attributes and link files) editorPackageZipPath = Path.Combine(Deployer.PackageOutputPath, $"FlaxEditor{Enum.GetName(typeof(TargetPlatform), Platform.BuildTargetPlatform)}.zip"); Utilities.FileDelete(editorPackageZipPath); Utilities.Run("zip", "Editor.zip -r .", null, OutputPath, Utilities.RunOptions.ThrowExceptionOnError); File.Move(Path.Combine(OutputPath, "Editor.zip"), editorPackageZipPath); } else { editorPackageZipPath = Path.Combine(Deployer.PackageOutputPath, "Editor.zip"); Utilities.FileDelete(editorPackageZipPath); #if USE_STD System.IO.Compression.ZipFile.CreateFromDirectory(OutputPath, editorPackageZipPath, System.IO.Compression.CompressionLevel.Optimal, false); #else using (var zip = new Ionic.Zip.ZipFile()) { zip.AddDirectory(OutputPath); zip.CompressionLevel = Ionic.Zlib.CompressionLevel.BestCompression; zip.Comment = string.Format("Flax Editor {0}.{1}.{2}\nDate: {3}", Deployer.VersionMajor, Deployer.VersionMinor, Deployer.VersionBuild, DateTime.UtcNow); zip.Save(editorPackageZipPath); } #endif } Log.Info("Compressed editor package size: " + Utilities.GetFileSize(editorPackageZipPath)); if (Platform.BuildTargetPlatform == TargetPlatform.Windows) { Log.Info("Compressing editor debug symbols files..."); editorPackageZipPath = Path.Combine(Deployer.PackageOutputPath, "EditorDebugSymbols.zip"); Utilities.FileDelete(editorPackageZipPath); #if USE_STD System.IO.Compression.ZipFile.CreateFromDirectory(Path.Combine(Deployer.PackageOutputPath, "EditorDebugSymbols"), editorPackageZipPath, System.IO.Compression.CompressionLevel.Optimal, false); #else using (var zip = new Ionic.Zip.ZipFile()) { zip.AddDirectory(Path.Combine(Deployer.PackageOutputPath, "EditorDebugSymbols")); zip.CompressionLevel = Ionic.Zlib.CompressionLevel.BestCompression; zip.Comment = string.Format("Flax Editor {0}.{1}.{2}\nDate: {3}", Deployer.VersionMajor, Deployer.VersionMinor, Deployer.VersionBuild, DateTime.UtcNow); zip.Save(editorPackageZipPath); } #endif Log.Info("Compressed editor debug symbols package size: " + Utilities.GetFileSize(editorPackageZipPath)); } // Cleanup Utilities.DirectoryDelete(OutputPath); Utilities.DirectoryDelete(Path.Combine(Deployer.PackageOutputPath, "EditorDebugSymbols")); } private static void DeployEditorBinaries(TargetConfiguration configuration) { var binariesSubDir = Path.Combine(Platform.GetEditorBinaryDirectory(), configuration.ToString()); var src = Path.Combine(RootPath, binariesSubDir); var dst = Path.Combine(OutputPath, binariesSubDir); Directory.CreateDirectory(dst); DeployFile(src, dst, "FlaxEditor.Build.json"); DeployFile(src, dst, "FlaxEngine.CSharp.runtimeconfig.json"); DeployFile(src, dst, "FlaxEngine.CSharp.pdb"); DeployFile(src, dst, "FlaxEngine.CSharp.xml"); DeployFile(src, dst, "Newtonsoft.Json.pdb"); if (Platform.BuildTargetPlatform == TargetPlatform.Windows) { var dstDebug = Path.Combine(Deployer.PackageOutputPath, "EditorDebugSymbols/Win64/" + configuration); Directory.CreateDirectory(dstDebug); // Validate that build editor app has a valid version number var editorExeName = "FlaxEditor.exe"; var version = FileVersionInfo.GetVersionInfo(Path.Combine(src, editorExeName)); if (version.FileMajorPart != Deployer.VersionMajor || version.FileMinorPart != Deployer.VersionMinor || version.FileBuildPart != Deployer.VersionBuild) { throw new InvalidDataException("Invalid engine build number. Output " + editorExeName + " has not matching version number."); } // Deploy binaries DeployFile(src, dst, editorExeName); CodeSign(Path.Combine(dst, editorExeName)); DeployFile(src, dst, "FlaxEditor.lib"); DeployFiles(src, dst, "*.dll"); CodeSign(Path.Combine(dst, "FlaxEngine.CSharp.dll")); // Deploy debug symbols files DeployFiles(src, dstDebug, "*.pdb"); File.Delete(Path.Combine(dstDebug, "FlaxEngine.CSharp.pdb")); File.Delete(Path.Combine(dstDebug, "Newtonsoft.Json.pdb")); if (configuration == TargetConfiguration.Development) { // Deploy Editor executable debug file for Development configuration to improve crash reporting DeployFile(src, dst, "FlaxEditor.pdb"); } } else if (Platform.BuildTargetPlatform == TargetPlatform.Linux) { // Deploy binaries DeployFile(src, dst, "FlaxEditor"); DeployFiles(src, dst, "*.dll"); DeployFiles(src, dst, "*.so"); DeployFile(src, dst, "Logo.png"); // Optimize package size Utilities.Run("strip", "FlaxEditor", null, dst, Utilities.RunOptions.None); Utilities.Run("strip", "libFlaxEditor.so", null, dst, Utilities.RunOptions.None); } else if (Platform.BuildTargetPlatform == TargetPlatform.Mac) { // Deploy binaries DeployFile(src, dst, "FlaxEditor"); DeployFile(src, dst, "MoltenVK_icd.json"); DeployFiles(src, dst, "*.dll"); DeployFiles(src, dst, "*.dylib"); // Optimize package size Utilities.Run("strip", "FlaxEditor", null, dst, Utilities.RunOptions.None); Utilities.Run("strip", "FlaxEditor.dylib", null, dst, Utilities.RunOptions.None); Utilities.Run("strip", "libMoltenVK.dylib", null, dst, Utilities.RunOptions.None); CodeSign(Path.Combine(dst, "FlaxEditor")); CodeSign(Path.Combine(dst, "FlaxEditor.dylib")); CodeSign(Path.Combine(dst, "libMoltenVK.dylib")); } } } } }