private static void ExportWebP(string path, string name, Texture2D texture, int quality, ICollection <FileInfo> output) { var hasAlpha = ReallyHasAlpha(texture); var outputTexture = CopyTexture(texture, hasAlpha ? TextureFormat.RGBA32 : TextureFormat.RGB24); var tempPngInputPath = Path.Combine(Application.temporaryCachePath, "webp-input.png"); File.WriteAllBytes(tempPngInputPath, outputTexture.EncodeToPNG()); quality = Math.Max(0, Math.Min(100, quality)); var dstFile = new FileInfo(Path.Combine(path, name + ".webp")) .FullName; UTinyBuildUtilities.RunInShell( $"cwebp -quiet -q {quality} \"{tempPngInputPath}\" -o \"{dstFile}\"", new ShellProcessArgs() { ExtraPaths = ImageUtilsPath().AsEnumerable() }); File.Delete(tempPngInputPath); output.Add(new FileInfo(dstFile)); }
private void CleanUp() { // We will keep the bug-report zip file, but clean up everything-else. if (Directory.Exists(BugProjectPath)) { UTinyBuildUtilities.PurgeDirectory(new DirectoryInfo(BugProjectPath)); } }
private static void InstallRuntime(bool force, bool silent) { try { var installLocation = new DirectoryInfo("UTiny"); var versionFile = new FileInfo(Path.Combine(installLocation.FullName, "lastUpdate.txt")); var sourcePackage = new FileInfo(UTinyConstants.PackagePath + "tiny-runtime-dist.zip"); var shouldUpdate = sourcePackage.Exists && (!versionFile.Exists || versionFile.LastWriteTimeUtc < sourcePackage.LastWriteTimeUtc); if (!force && !shouldUpdate) { if (!silent) { Debug.Log("Tiny: Runtime is already up to date"); } return; } if (!sourcePackage.Exists) { if (!silent) { Debug.LogError($"Tiny: could not find {sourcePackage.FullName}"); } return; } if (installLocation.Exists) { EditorUtility.DisplayProgressBar($"{UTinyConstants.ApplicationName} Runtime", "Removing old runtime...", 0.0f); UTinyBuildUtilities.PurgeDirectory(installLocation); } EditorUtility.DisplayProgressBar($"{UTinyConstants.ApplicationName} Runtime", "Installing new runtime...", 0.5f); UTinyBuildUtilities.UnzipFile(sourcePackage.FullName, installLocation.Parent); File.WriteAllText(versionFile.FullName, $"{sourcePackage.FullName} install time: {DateTime.UtcNow.ToString()}"); #if UNITY_EDITOR_OSX // TODO: figure out why UnzipFile does not preserve executable bits in some cases // chmod +x any native executables here UTinyBuildUtilities.RunInShell("chmod +x cwebp moz-cjpeg pngcrush", new ShellProcessArgs() { WorkingDirectory = new DirectoryInfo(GetToolDirectory("images/osx")), ExtraPaths = "/bin".AsEnumerable(), // adding this folder just in case, but should be already in $PATH ThrowOnError = false }); #endif Debug.Log($"Installed {UTinyConstants.ApplicationName} runtime at: {installLocation.FullName}"); } finally { EditorUtility.ClearProgressBar(); } }
private bool HostContent(string contentDir) { StopHostingContent(); // Setup a signal event with handlers string stdout = "", stderr = ""; ManualResetEvent isRunning = new ManualResetEvent(false); DataReceivedEventHandler outputReceived = (sender, e) => { stdout += e.Data; isRunning.Set(); }; DataReceivedEventHandler errorReceived = (sender, e) => { stderr += e.Data; isRunning.Set(); }; // Start new local http server var ppid = Process.GetCurrentProcess().Id; var port = UTinyEditorApplication.Project.Settings.LocalHTTPServerPort; var httpServerDir = new DirectoryInfo(UTinyBuildPipeline.GetToolDirectory("httpserver")); var unityVersion = InternalEditorUtility.GetUnityVersion(); var profilerVersion = unityVersion.Major > 2018 || (unityVersion.Major == 2018 && unityVersion.Minor > 1) ? 0x20180123 : 0x20170327; m_LocalServerProcess = UTinyBuildUtilities.RunNodeNoWait(httpServerDir, "index.js", $"--pid {ppid} --port {port} --dir \"{contentDir}\" --profiler " + profilerVersion, outputReceived, errorReceived); if (m_LocalServerProcess == null) { throw new Exception("Failed to create local http server process."); } // Wait for the process to write something in either stdout or stderr isRunning.WaitOne(3000); // Check if process state is valid if (m_LocalServerProcess.HasExited || stderr.Length > 0) { var errorMsg = "Failed to start local http server."; if (stderr.Length > 0) { errorMsg += $"\n{stderr}"; } else if (stdout.Length > 0) { errorMsg += $"\n{stdout}"; } UnityEngine.Debug.LogError(errorMsg); return(false); } return(true); }
private bool PackagingDependencies() { IEnumerable <string> packagableFiles = k_EmptyPackageableFiles; IEnumerable <string> nonPackagableFiles = k_EmptyNonPackageableFiles; foreach (var group in m_Paths.GroupBy(p => p.StartsWith(DataPath))) { var inAssetFolder = group.Key; if (inAssetFolder) { packagableFiles = group.Select(RelativeToProjectFolder); } else { nonPackagableFiles = group; } } if (nonPackagableFiles.Any()) { string zipFilename = $"{DataPath}/external-files.zip"; UTinyBuildUtilities.ZipPaths(nonPackagableFiles.ToArray(), zipFilename); var relativeZipPath = RelativeToProjectFolder(zipFilename); AssetDatabase.ImportAsset(relativeZipPath, ImportAssetOptions.ForceSynchronousImport); File.Move(zipFilename, $"{BugProjectPath}/{relativeZipPath}"); File.Move($"{zipFilename}.meta", $"{BugProjectPath}/{relativeZipPath}.meta"); } if (packagableFiles.Any()) { var toPackage = packagableFiles.ToArray(); var targetPackagePath = $"{BugProjectPath}/{k_BugPackagePath}"; AssetDatabase.ExportPackage(toPackage, targetPackagePath, ExportPackageOptions.IncludeDependencies | ExportPackageOptions.Recurse); } return(true); }
private void Send(MouseUpEvent evt) { if (Directory.Exists(BugReportPath)) { UTinyBuildUtilities.PurgeDirectory(new DirectoryInfo(BugReportPath)); } EditorUtility.DisplayProgressBar($"{UTinyConstants.ApplicationName} Bug Report", "Validating Fields", 0.0f); m_PathsOnSend.AddRange(m_Paths); try { if (!RunUntilFailure(m_SendMethods, true)) { EditorUtility.ClearProgressBar(); // Failed to send the bug report to Jira, but successfully created the zip file. if (File.Exists(ZipFilePath)) { Debug.Log($"{UTinyConstants.ApplicationName}: Could not send the bug report. you can send it manually by emailing the '{RelativeToProjectFolder(ZipFilePath)}' file to '*****@*****.**'" + "\nNo need to provide additional description, it is all packed in the zip file itself."); EditorUtility.RevealInFinder(ZipFilePath); Close(); } } else { Debug.Log($"{UTinyConstants.ApplicationName}: bug sent successfully - thanks!"); Close(); } } finally { CleanUp(); m_PathsOnSend.Clear(); EditorUtility.ClearProgressBar(); } }
private static void ExportJpgOptimized(string path, string name, Texture2D texture, int quality, ICollection <FileInfo> output) { var colorFilePath = $"{Path.Combine(path, name)}.jpg"; var outputTexture = CopyTexture(texture, TextureFormat.RGB24); //var tempPngInputPath = Path.Combine(Application.temporaryCachePath, "webp-input.png"); var tempPngInputPath = $"{Path.Combine(path, name)}-input.png"; File.WriteAllBytes(tempPngInputPath, outputTexture.EncodeToPNG()); quality = Math.Max(0, Math.Min(100, quality)); // this will build progressive jpegs by default; -baseline stops this. // progressive results in better compression UTinyBuildUtilities.RunInShell( $"moz-cjpeg -quality {quality} -outfile \"{colorFilePath}\" \"{tempPngInputPath}\"", new ShellProcessArgs() { ExtraPaths = ImageUtilsPath().AsEnumerable() }); File.Delete(tempPngInputPath); output.Add(new FileInfo(colorFilePath)); if (ReallyHasAlpha(texture)) { // @TODO Optimization by reusing the texture above var outputAlphaTexture = CopyTexture(texture, TextureFormat.RGBA32); var pixels = outputAlphaTexture.GetPixels32(); // broadcast alpha to color for (var i = 0; i < pixels.Length; i++) { pixels[i].r = pixels[i].a; pixels[i].g = pixels[i].a; pixels[i].b = pixels[i].a; pixels[i].a = 255; } outputAlphaTexture.SetPixels32(pixels); outputAlphaTexture.Apply(); //var pngCrushInputPath = Path.Combine(Application.temporaryCachePath, "alpha-input.png"); var pngCrushInputPath = $"{Path.Combine(path, name)}_a-input.png"; var alphaFilePath = $"{Path.Combine(path, name)}_a.png"; using (var stream = new FileStream(pngCrushInputPath, FileMode.Create)) { var bytes = outputAlphaTexture.EncodeToPNG(); stream.Write(bytes, 0, bytes.Length); } // convert to 8-bit grayscale png UTinyBuildUtilities.RunInShell( $"pngcrush -s -c 0 \"{pngCrushInputPath}\" \"{alphaFilePath}\"", new ShellProcessArgs() { ExtraPaths = ImageUtilsPath().AsEnumerable() }); output.Add(new FileInfo(alphaFilePath)); } }
/// <summary> /// Outputs the final `index.html` file /// </summary> private static void GenerateHTML(UTinyBuildOptions options, UTinyBuildResults results) { var project = options.Project; var settingsFile = new FileInfo(Path.Combine(results.BinaryFolder.FullName, KSettingsFileName)); var runtimeFile = new FileInfo(Path.Combine(results.BinaryFolder.FullName, KRuntimeFileName)); var bindingsFile = new FileInfo(Path.Combine(results.BinaryFolder.FullName, KBindingsFileName)); var assetsFile = new FileInfo(Path.Combine(results.BinaryFolder.FullName, KAssetsFileName)); var entityGroupsFile = new FileInfo(Path.Combine(results.BinaryFolder.FullName, KEntityGroupsFileName)); var systemsFile = new FileInfo(Path.Combine(results.BinaryFolder.FullName, KSystemsFileName)); var codeFile = new FileInfo(Path.Combine(results.BinaryFolder.FullName, KCodeFileName)); var mainFile = new FileInfo(Path.Combine(results.BinaryFolder.FullName, KMainFileName)); var webSocketClientFile = new FileInfo(Path.Combine(results.BinaryFolder.FullName, KWebSocketClientFileName)); var webpDecompressorFile = new FileInfo(Path.Combine(results.BinaryFolder.FullName, KWebPDecompressorFileName)); // nb: this writer is not HTML-friendly var writer = new UTinyCodeWriter() { CodeStyle = new CodeStyle() { BeginBrace = string.Empty, EndBrace = string.Empty, BraceLayout = BraceLayout.EndOfLine, Indent = " ", NewLine = Environment.NewLine } }; writer.Line("<!DOCTYPE html>"); using (writer.Scope("<html>")) { using (writer.Scope("<head>")) { writer.Line("<meta charset=\"UTF-8\">"); if (UsesAdSupport(project)) { writer.Line("<script src=\"mraid.js\"></script>"); } if (project.Settings.RunBabel) { // Babelize user code var title = $"{UTinyConstants.ApplicationName} Build"; const string messageFormat = "Transpiling {0} to ECMAScript 5"; EditorUtility.DisplayProgressBar(title, "Transpiling to ECMAScript 5", 0.0f); try { // We only need to transpile user authored code var userCode = new [] { systemsFile, codeFile }; var babelDir = new DirectoryInfo(UTinyBuildPipeline.GetToolDirectory("babel")); for (var i = 0; i < userCode.Length; i++) { var file = userCode[i]; EditorUtility.DisplayProgressBar(title, string.Format(messageFormat, file.Name), i / (float)userCode.Length); UTinyBuildUtilities.RunNode(babelDir, "index.js", $"\"{file.FullName}\" \"{file.FullName}\""); } } finally { EditorUtility.ClearProgressBar(); } } // Gather all game files (order is important) var files = new List <FileInfo> { settingsFile, runtimeFile, bindingsFile, assetsFile, entityGroupsFile, systemsFile, codeFile, mainFile, webSocketClientFile, webpDecompressorFile }.Where(file => file != null && file.Exists).ToList(); // Extra steps for Release config if (options.Configuration == UTinyBuildConfiguration.Release) { // Minify JavaScript var gameFile = new FileInfo(Path.Combine(results.BinaryFolder.FullName, "game.js")); EditorUtility.DisplayProgressBar($"{UTinyConstants.ApplicationName} Build", "Minifying JavaScript code...", 0.0f); try { var minifyDir = new DirectoryInfo(UTinyBuildPipeline.GetToolDirectory("minify")); UTinyBuildUtilities.RunNode(minifyDir, "index.js", $"\"{gameFile.FullName}\" {String.Join(" ", files.Select(file => '"' + file.FullName + '"'))}"); files.ForEach(file => file.Delete()); } finally { EditorUtility.ClearProgressBar(); } // Package as single html file if (project.Settings.SingleFileHtml) { writer.Line("<script type=\"text/javascript\">"); writer.WriteRaw(File.ReadAllText(gameFile.FullName)); writer.Line(); writer.Line("</script>"); gameFile.Delete(); } else { writer.LineFormat("<script src=\"{0}\"></script>", gameFile.Name); } } else { files.ForEach(file => writer.LineFormat("<script src=\"{0}\"></script>", file.Name)); } writer.LineFormat("<title>{0}</title>", project.Name); writer.CodeStyle.EndBrace = "</head>"; } using (writer.Scope("<body>")) { writer.CodeStyle.EndBrace = "</body>"; } writer.CodeStyle.EndBrace = "</html>"; } // Write final index.html file var htmlFile = new FileInfo(Path.Combine(results.BinaryFolder.FullName, KHtmlFileName)); File.WriteAllText(htmlFile.FullName, writer.ToString(), Encoding.UTF8); }
private bool ZippingBugReport() { return(UTinyBuildUtilities.ZipFolder(new DirectoryInfo(BugProjectPath), ZipFilePath)); }
public static UTinyBuildResults Build(UTinyBuildOptions options) { if (options?.Project == null || options.Destination == null) { throw new ArgumentException($"{UTinyConstants.ApplicationName}: invalid build options provided", nameof(options)); } var buildStart = DateTime.Now; var results = new UTinyBuildResults(); IUTinyBuilder builder = null; switch (options.Platform) { case UTinyPlatform.HTML5: builder = new UTinyHTML5Builder(); break; default: throw new ArgumentException($"{UTinyConstants.ApplicationName}: build platform not supported", nameof(options)); } try { EditorUtility.DisplayProgressBar(ProgressBarTitle, "Build started for " + options.Platform.ToString(), 0.0f); var destFolder = options.Destination; destFolder.Create(); // BUILD = <DEST>/PLATFORM/CONFIG var buildFolder = new DirectoryInfo(GetBuildDirectory(options.Project, options.Platform, options.Configuration)); results.OutputFolder = buildFolder; UTinyBuildUtilities.PurgeDirectory(buildFolder); buildFolder.Create(); options.Destination = results.BinaryFolder = buildFolder; var idlFile = new FileInfo(Path.Combine(buildFolder.FullName, "generated.cs")); UTinyIDLGenerator.GenerateIDL(options.Project, idlFile); var distFolder = GetRuntimeDistFolder(); var bindGem = new FileInfo(Path.Combine( distFolder.FullName, "bindgem/BindGem/bin/Release/BindGem.exe")); var exeName = "\"" + bindGem.FullName + "\""; // always call bindgem with mono for consistency exeName = "mono " + exeName; // reference the core runtime file var bindReferences = $"-r \"{RuntimeDefsAssemblyPath}\""; UTinyBuildUtilities.RunInShell( $"{exeName} -j {bindReferences} -o bind-generated {idlFile.Name}", new ShellProcessArgs() { WorkingDirectory = buildFolder, ExtraPaths = TinyPreferences.MonoDirectory.AsEnumerable() }); // @TODO Perform a full refresh before building builder.Build(options, results); results.BuildReport.Update(); Debug.Log($"{UTinyConstants.ApplicationName} project generated at: {results.BinaryFolder.FullName}"); TinyEditorAnalytics.SendBuildEvent(options.Project, results, DateTime.Now - buildStart); return(results); } catch (Exception ex) { TinyEditorAnalytics.SendException("BuildPipeline.Build", ex); throw; } finally { EditorUtility.ClearProgressBar(); UTinyEditorUtility.RepaintAllWindows(); } }