using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using UnityEngine; using UnityEditorInternal; using UnityEditor; using NiceIO.Sysroot; #if UNITY_STANDALONE_LINUX_API using UnityEditor.LinuxStandalone; #endif namespace UnityEditor.Il2Cpp { /// /// Describe a payload location and destination /// public struct PayloadDescriptor { /// /// Path of payload tarball /// internal NPath path; /// /// Path directory where payload is to be installed /// internal NPath dir; } /// /// Initialization status /// enum InitializationStatus { Uninitialized, Failed, Succeeded } /// /// Base class for sysroot and toolchain packages /// public class SysrootPackage #if UNITY_STANDALONE_LINUX_API || UNITY_WEBGL_API : Sysroot #endif { private static bool IsLinuxIL2CPPPresent() { string targetDir = $"{BuildPipeline.GetPlaybackEngineDirectory(BuildTargetGroup.Standalone, BuildTarget.StandaloneLinux64, BuildOptions.None)}/Variations/il2cpp"; if (Directory.Exists(targetDir)) return true; return false; } public static bool ShouldLogDebugMessage() { return !String.IsNullOrEmpty(Environment.GetEnvironmentVariable("UNITY_SYSROOT_DEBUG")); } [InitializeOnLoadMethod] private static void IssueWarningIfLinuxIL2CPPNotPresent() { if (ShouldLogDebugMessage() && !IsLinuxIL2CPPPresent()) { UnityEngine.Debug.LogWarning($"Linux Compiler Toolchain package(s) present, but required Linux-IL2CPP is missing"); } } /// /// Name of package /// public #if UNITY_STANDALONE_LINUX_API || UNITY_WEBGL_API override #else virtual #endif string Name => "com.unity.sysroot"; /// /// Name of host platform (linux, win, macos) /// public #if UNITY_STANDALONE_LINUX_API || UNITY_WEBGL_API override #else virtual #endif string HostPlatform => ""; /// /// Name of host architecture /// public #if UNITY_STANDALONE_LINUX_API || UNITY_WEBGL_API override #else virtual #endif string HostArch => ""; /// /// Name of target platform (linux, win, macos) /// public #if UNITY_STANDALONE_LINUX_API || UNITY_WEBGL_API override #else virtual #endif string TargetPlatform => ""; /// /// Name of target architecture /// public #if UNITY_STANDALONE_LINUX_API || UNITY_WEBGL_API override #else virtual #endif string TargetArch => ""; /// /// Supplies arguments to il2cpp.exe /// /// Next argument to il2cpp.exe public #if UNITY_STANDALONE_LINUX_API || UNITY_WEBGL_API override #else virtual #endif IEnumerable GetIl2CppArguments() { return null; } /// /// Name of payload tarball /// protected string Payload => "payload.tar.7z"; private List _payloads = new List(); private InitializationStatus _initStatus = InitializationStatus.Uninitialized; /// /// Initialize package /// /// Success or failure of initialization public #if UNITY_STANDALONE_LINUX_API || UNITY_WEBGL_API override #else virtual #endif bool Initialize() { if (_initStatus != InitializationStatus.Uninitialized) return _initStatus == InitializationStatus.Succeeded; foreach (PayloadDescriptor pd in _payloads) { if (!Directory.Exists(pd.dir.ToString(SlashMode.Native)) && !InstallPayload(pd)) { UnityEngine.Debug.LogError($"Failed to initialize package: {Name}"); _initStatus = InitializationStatus.Failed; return false; } } _initStatus = InitializationStatus.Succeeded; return true; } /// /// Compute path of payload tarball /// /// The name of the package /// Path of payload tarball internal NPath PayloadPath(string packageName) { return new NPath(Path.GetFullPath($"Packages/{packageName}")).Combine("data~/payload.tar.7z"); } /// /// Register payload tarball and destination (installed location) /// /// The name of the package /// The directory to install the payload in relative to sysroot cache public void RegisterPayload(string packageName, string payloadDir) { _payloads.Add(new PayloadDescriptor{path = PayloadPath(packageName).ToString(SlashMode.Native), dir = PayloadInstallDirectory(payloadDir).ToString(SlashMode.Native)}); } private bool RunShellCommand(string command, string workDir = null) { var p = new Process(); p.StartInfo.UseShellExecute = false; #if UNITY_EDITOR_WIN p.StartInfo.CreateNoWindow = true; p.StartInfo.FileName = "cmd"; p.StartInfo.Arguments = $"/c \"{command}\""; #else p.StartInfo.FileName = "/bin/sh"; p.StartInfo.Arguments = $"-c \'{command}\'"; #endif p.StartInfo.WorkingDirectory = string.IsNullOrEmpty(workDir) ? Environment.CurrentDirectory : workDir; p.Start(); p.WaitForExit(); bool result = p.ExitCode == 0; if (!result && ShouldLogDebugMessage()) UnityEngine.Debug.LogError($"Failed to execute command command=\"{p.StartInfo.FileName}\" arguments=\"{p.StartInfo.Arguments}\""); return result; } private bool DecompressSysroot(NPath payload, NPath workDir) { if (!RunShellCommand(CommandCreateDirectory(workDir))) { UnityEngine.Debug.LogError($"Failed to create directory {workDir}"); return false; } if (!RunShellCommand(CommandUncompressTarball(payload, workDir), workDir.ToString(SlashMode.Native))) { UnityEngine.Debug.LogError($"Failed to uncompress payload"); RunShellCommand(CommandRemoveDirectoryTree(workDir)); return false; } return PostDecompressActions(workDir); } private bool InstallPayload(PayloadDescriptor pd) { return DecompressSysroot(pd.path, pd.dir); } private string CommandCreateDirectory(NPath dir) { _initStatus = InitializationStatus.Failed; #if UNITY_EDITOR_WIN return $"mkdir {dir.InQuotes(SlashMode.Native)}"; #else return $"mkdir -p {dir.InQuotes()}"; #endif } private string Get7zPath() { #if UNITY_EDITOR_WIN string command = "7z"; #else string command = "7za"; #endif return new NPath($"{EditorApplication.applicationContentsPath}/Tools/{command}").InQuotes(SlashMode.Native); } private string CommandUncompressTarball(NPath tarball, NPath destDir) { #if UNITY_EDITOR_WIN return $"{Get7zPath()} x -y {tarball.InQuotes(SlashMode.Native)} -so | {Get7zPath()} x -y -aoa -ttar -si"; #else return $"{Get7zPath()} x -y {tarball.InQuotes()} -so | tar xf - --directory={destDir.InQuotes()}"; #endif } private string CommandRemoveDirectoryTree(NPath dir) { #if UNITY_EDITOR_WIN return $"rd /s /q {dir.InQuotes(SlashMode.Native)}"; #else return $"rm -rf {dir.InQuotes()}"; #endif } private bool PostDecompressActions(NPath workDir) { return true; } private string UserAppDataFolder() { return #if UNITY_EDITOR_OSX $"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}/Library/Unity"; #else $"{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}/unity3d"; #endif } /// /// Returns path of installed payload /// /// The directory to install the payload in relative to sysroot cache /// Fully-qualified path of install directory internal NPath PayloadInstallDirectory(string payloadDir) { string cacheDir = Environment.GetEnvironmentVariable("UNITY_SYSROOT_CACHE"); if (string.IsNullOrEmpty(cacheDir)) cacheDir = $"{UserAppDataFolder()}/cache/sysroots"; return new NPath($"{cacheDir}/{payloadDir}"); } } }